@rt-tools/ui-kit-v2 0.7.0 → 0.8.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.
@@ -1,15 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, ViewEncapsulation, ChangeDetectionStrategy, Component, signal, InjectionToken, inject, Injector, RendererFactory2, Injectable, computed, makeEnvironmentProviders, Pipe, numberAttribute, effect, ElementRef, Directive, booleanAttribute, output, HostBinding, DestroyRef, ViewContainerRef, viewChild, forwardRef, Renderer2, afterNextRender, untracked, HostListener, contentChild, TemplateRef, afterRenderEffect, linkedSignal, isSignal, LOCALE_ID, contentChildren, ViewChild, model } from '@angular/core';
2
+ import { input, ViewEncapsulation, ChangeDetectionStrategy, Component, signal, InjectionToken, inject, Injector, RendererFactory2, Injectable, computed, makeEnvironmentProviders, Pipe, numberAttribute, effect, ElementRef, Directive, booleanAttribute, output, HostBinding, DestroyRef, ViewContainerRef, viewChild, forwardRef, Renderer2, afterNextRender, untracked, TemplateRef, HostListener, contentChild, afterRenderEffect, linkedSignal, isSignal, LOCALE_ID, contentChildren, ViewChild, model } from '@angular/core';
3
3
  import { BlockDirective, ElemDirective, ModDirective, PlatformService, StorageService, IDBStorageService, WINDOW } from '@rt-tools/core';
4
- import * as i1$1 from '@angular/cdk/overlay';
4
+ import * as i1 from '@angular/cdk/overlay';
5
5
  import { Overlay, ScrollStrategyOptions, OverlayConfig, OverlayModule } from '@angular/cdk/overlay';
6
6
  import { ComponentPortal, TemplatePortal } from '@angular/cdk/portal';
7
7
  import { takeUntilDestroyed, outputToObservable, toSignal, toObservable } from '@angular/core/rxjs-interop';
8
- import { Subject, mergeMap, map, EMPTY, merge, takeUntil, tap, catchError, switchMap, debounce, timer, filter, of, exhaustMap, startWith, take, finalize, shareReplay, defer, fromEvent, debounceTime, distinctUntilChanged } from 'rxjs';
8
+ import { Subject, mergeMap, map, EMPTY, merge, takeUntil, tap, catchError, switchMap, debounce, timer, filter, of, finalize, shareReplay, take, startWith, exhaustMap, defer, fromEvent, debounceTime, distinctUntilChanged } from 'rxjs';
9
9
  import { DOCUMENT, NgTemplateOutlet, DatePipe, DecimalPipe, formatDate } from '@angular/common';
10
10
  import { HttpClient } from '@angular/common/http';
11
- import * as i1 from '@angular/forms';
12
- import { NgControl, Validators, FormGroup, FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, PristineChangeEvent, FormsModule } from '@angular/forms';
11
+ import * as i1$1 from '@angular/forms';
12
+ import { Validators, NgControl, FormGroup, FormControl, ReactiveFormsModule, NG_VALUE_ACCESSOR, PristineChangeEvent, FormsModule } from '@angular/forms';
13
13
  import { CdkTextareaAutosize } from '@angular/cdk/text-field';
14
14
  import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout';
15
15
  import { Router, ActivatedRoute, NavigationEnd, RouterLink, RouterLinkActive } from '@angular/router';
@@ -1926,20 +1926,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1926
1926
  }, template: "<ng-container rtBlock=\"rt-aside-section\">\n @if (heading()) {\n <h3 rtElem=\"heading\" qa-dataid=\"aside-section-heading\">{{ heading() }}</h3>\n }\n <div rtElem=\"body\" qa-dataid=\"aside-section-body\">\n <ng-content />\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-aside-section{--rt-aside-section-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;gap:var(--rt-space-md)}.rt-aside-section+.rt-aside-section{padding-top:var(--rt-aside-content-gap);border-top:var(--rt-aside-section-border-width) solid var(--rt-color-border-subtle)}.rt-aside-section__heading{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold);line-height:var(--rt-leading-snug)}.rt-aside-section__body{display:flex;flex-direction:column;gap:var(--rt-space-md)}}\n"] }]
1927
1927
  }], propDecorators: { heading: [{ type: i0.Input, args: [{ isSignal: true, alias: "heading", required: false }] }] } });
1928
1928
 
1929
- /**
1930
- * Базовый класс input-семейства common/ui. Владеет единым CVA-ядром:
1931
- * - self-injected `NgControl` + ручная привязка `valueAccessor` (без
1932
- * `NG_VALUE_ACCESSOR` provider — иначе NG0200 cycle при self-reference),
1933
- * что даёт доступ к `control.invalid/touched` для авто-подсветки ошибки;
1934
- * - сигнал значения `value`, состояния `isDisabled`/`isInvalid` (отключение
1935
- * складывается из входа `disabled` и слова формы — см. `isDisabled`);
1936
- * - общие инпуты `size`/`disabled`/`controlId`/`ariaLabel`/`clearable`;
1937
- * - оркестрацию очистки (`clear`) через абстрактные хуки конкретного поля.
1938
- *
1939
- * Конкретные поля реализуют `getEmptyValue`/`hasValue`/`focusAfterClear`. Поля
1940
- * с отдельным display-состоянием (autocomplete, input-number) дополнительно
1941
- * переопределяют `writeValue`/`clearValue`, синхронизируя свой display-сигнал.
1942
- */
1929
+ /** Состояние поля, которому форма ещё ничего не сказала. */
1930
+ const PRISTINE_STATE = { invalid: false, errors: null, required: false };
1931
+ /** Рассылает ли контрол своё состояние. Переходник сигнальной формы — не рассылает. */
1932
+ function emitsEvents(control) {
1933
+ return typeof control.events?.pipe === 'function';
1934
+ }
1935
+ /** Состояние формы, снятое с контрола любого рода. */
1936
+ function stateOf(control) {
1937
+ return {
1938
+ invalid: !!control.invalid && (control.touched || control.dirty),
1939
+ errors: control.errors,
1940
+ required: control.hasValidator(Validators.required),
1941
+ };
1942
+ }
1943
1943
  class RtFormControlBase {
1944
1944
  #ngControl;
1945
1945
  #destroyRef;
@@ -1949,10 +1949,17 @@ class RtFormControlBase {
1949
1949
  // авто-id для связки label[for], read-only режим, зеркало ошибок/required.
1950
1950
  #controlIdAssigned;
1951
1951
  #readonlyAssigned;
1952
- #errors;
1953
- #required;
1952
+ /** Состояние, разосланное прежней привязкой: её контрол сигналами не читается. */
1953
+ #pushedState;
1954
+ /**
1955
+ * Контрол сигнальной привязки. Его состояние не рассылается, а читается геттерами, и
1956
+ * внутри геттеров стоят вызовы сигналов формы — то есть из `computed` оно реактивно.
1957
+ */
1958
+ #signalControl;
1954
1959
  /** Отключение, назначенное формой через CVA (`setDisabledState`). */
1955
1960
  #disabledByForm;
1961
+ /** Состояние формы: у сигнальной привязки читается, у прежней берётся разосланное. */
1962
+ #state;
1956
1963
  constructor() {
1957
1964
  this.#ngControl = inject(NgControl, { self: true, optional: true });
1958
1965
  this.#destroyRef = inject(DestroyRef);
@@ -1964,10 +1971,15 @@ class RtFormControlBase {
1964
1971
  ...(ngDevMode ? [{ debugName: "#controlIdAssigned" }] : /* istanbul ignore next */ []));
1965
1972
  this.#readonlyAssigned = signal(false, /* @ts-ignore */
1966
1973
  ...(ngDevMode ? [{ debugName: "#readonlyAssigned" }] : /* istanbul ignore next */ []));
1967
- this.#errors = signal(null, /* @ts-ignore */
1968
- ...(ngDevMode ? [{ debugName: "#errors" }] : /* istanbul ignore next */ []));
1969
- this.#required = signal(false, /* @ts-ignore */
1970
- ...(ngDevMode ? [{ debugName: "#required" }] : /* istanbul ignore next */ []));
1974
+ /** Состояние, разосланное прежней привязкой: её контрол сигналами не читается. */
1975
+ this.#pushedState = signal(PRISTINE_STATE, /* @ts-ignore */
1976
+ ...(ngDevMode ? [{ debugName: "#pushedState" }] : /* istanbul ignore next */ []));
1977
+ /**
1978
+ * Контрол сигнальной привязки. Его состояние не рассылается, а читается геттерами, и
1979
+ * внутри геттеров стоят вызовы сигналов формы — то есть из `computed` оно реактивно.
1980
+ */
1981
+ this.#signalControl = signal(null, /* @ts-ignore */
1982
+ ...(ngDevMode ? [{ debugName: "#signalControl" }] : /* istanbul ignore next */ []));
1971
1983
  /** Отключение, назначенное формой через CVA (`setDisabledState`). */
1972
1984
  this.#disabledByForm = signal(false, /* @ts-ignore */
1973
1985
  ...(ngDevMode ? [{ debugName: "#disabledByForm" }] : /* istanbul ignore next */ []));
@@ -1980,7 +1992,13 @@ class RtFormControlBase {
1980
1992
  */
1981
1993
  this.isDisabled = computed(() => this.disabled() || this.#disabledByForm(), /* @ts-ignore */
1982
1994
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
1983
- this.isInvalid = signal(false, /* @ts-ignore */
1995
+ /** Состояние формы: у сигнальной привязки читается, у прежней берётся разосланное. */
1996
+ this.#state = computed(() => {
1997
+ const control = this.#signalControl();
1998
+ return control === null ? this.#pushedState() : stateOf(control);
1999
+ }, /* @ts-ignore */
2000
+ ...(ngDevMode ? [{ debugName: "#state" }] : /* istanbul ignore next */ []));
2001
+ this.isInvalid = computed(() => this.#state().invalid, /* @ts-ignore */
1984
2002
  ...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
1985
2003
  /** Видимость кнопки очистки: включена, есть значение, поле активно. */
1986
2004
  this.showClearButton = computed(() => this.clearable() && this.hasValue() && !this.isDisabled(), /* @ts-ignore */
@@ -2004,13 +2022,15 @@ class RtFormControlBase {
2004
2022
  ...(ngDevMode ? [{ debugName: "effectiveControlId" }] : /* istanbul ignore next */ []));
2005
2023
  /** Read-only режим (плоский текст вместо контрола); включается rt-field'ом. */
2006
2024
  this.isReadonly = this.#readonlyAssigned.asReadonly();
2007
- /** Публичный invalid для rt-field (touched/dirty уже учтён в #syncState). */
2025
+ /** Публичный invalid для rt-field (touched/dirty учтён в состоянии). */
2008
2026
  this.invalid = computed(() => this.isInvalid(), /* @ts-ignore */
2009
2027
  ...(ngDevMode ? [{ debugName: "invalid" }] : /* istanbul ignore next */ []));
2010
2028
  /** Текущие ошибки связанного контрола — источник текста ошибки в rt-field. */
2011
- this.errors = this.#errors.asReadonly();
2029
+ this.errors = computed(() => this.#state().errors, /* @ts-ignore */
2030
+ ...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
2012
2031
  /** Помечен ли контрол как обязательный — для авто-«*» в rt-field. */
2013
- this.required = this.#required.asReadonly();
2032
+ this.required = computed(() => this.#state().required, /* @ts-ignore */
2033
+ ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
2014
2034
  this.value = signal(this.getEmptyValue(), /* @ts-ignore */
2015
2035
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2016
2036
  // Self-inject NgControl + ручная привязка valueAccessor: доступ к
@@ -2025,11 +2045,17 @@ class RtFormControlBase {
2025
2045
  if (control === null) {
2026
2046
  return;
2027
2047
  }
2048
+ // Контрол, который состояния не рассылает, — переходник сигнальной формы: событий у
2049
+ // него нет вовсе, и подписка на них роняла поле из запуска вместе со всем поддеревом
2050
+ // разметки. Состояние у него читается геттерами, поэтому берётся вычислением.
2051
+ if (!emitsEvents(control)) {
2052
+ this.#signalControl.set(control);
2053
+ return;
2054
+ }
2028
2055
  // Авто-подсветка invalid + зеркало ошибок/required для rt-field.
2029
- this.#required.set(control.hasValidator(Validators.required));
2030
- this.#syncState(control);
2056
+ this.#pushedState.set(stateOf(control));
2031
2057
  control.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(() => {
2032
- this.#syncState(control);
2058
+ this.#pushedState.set(stateOf(control));
2033
2059
  });
2034
2060
  }
2035
2061
  writeValue(value) {
@@ -2075,10 +2101,6 @@ class RtFormControlBase {
2075
2101
  markTouched() {
2076
2102
  this.#onTouched();
2077
2103
  }
2078
- #syncState(control) {
2079
- this.isInvalid.set(!!control.invalid && (control.touched || control.dirty));
2080
- this.#errors.set(control.errors);
2081
- }
2082
2104
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtFormControlBase, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
2083
2105
  static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: RtFormControlBase, isStandalone: true, inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, bordered: { classPropertyName: "bordered", publicName: "bordered", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
2084
2106
  }
@@ -3059,6 +3081,82 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
3059
3081
  }, template: "<article\n rtBlock=\"rt-card\"\n qa-dataid=\"card\"\n [rtMod]=\"{ clickable: clickable() }\"\n [attr.role]=\"clickable() ? 'button' : null\"\n [attr.tabindex]=\"clickable() ? 0 : null\"\n [attr.aria-label]=\"ariaLabel()\"\n (click)=\"onClick($event)\"\n (keydown.enter)=\"onKeyActivate($event)\"\n (keydown.space)=\"onKeyActivate($event)\">\n <header rtElem=\"header\">\n @if (header(); as h) {\n <h3 rtElem=\"title\" qa-dataid=\"card-title\">{{ h }}</h3>\n }\n <ng-content select=\"[rtCardHeader]\" />\n </header>\n <div rtElem=\"content\">\n <ng-content />\n </div>\n <footer rtElem=\"footer\">\n <ng-content select=\"[rtCardFooter]\" />\n </footer>\n</article>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-card{display:block}rt-card .rt-card{--rt-card-radius: var(--rt-radius-xl);--rt-card-shadow: var(--rt-shadow-card);--rt-card-shadow-hover: var(--rt-shadow-lg);--rt-card-focus-shadow: var(--rt-shadow-focus-ring);--rt-card-transition-duration: var(--rt-duration-fast);display:block;padding:var(--rt-space-lg);border-radius:var(--rt-card-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-card-shadow);color:var(--rt-color-text-primary)}rt-card .rt-card--clickable{cursor:pointer;transition:box-shadow var(--rt-card-transition-duration) ease,transform var(--rt-card-transition-duration) ease}rt-card .rt-card--clickable:hover{box-shadow:var(--rt-card-shadow-hover)}rt-card .rt-card--clickable:focus-visible{box-shadow:var(--rt-card-focus-shadow);outline:2px solid transparent;outline-offset:2px}rt-card .rt-card__header{margin-block-end:var(--rt-space-md)}rt-card .rt-card__header:empty{display:none}rt-card .rt-card__title{margin:0;font-size:var(--rt-text-lg);line-height:var(--rt-leading-snug)}rt-card .rt-card__content{display:block}rt-card .rt-card__footer{margin-block-start:var(--rt-space-md)}rt-card .rt-card__footer:empty{display:none}}\n"] }]
3060
3082
  }], propDecorators: { header: [{ type: i0.Input, args: [{ isSignal: true, alias: "header", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clickable", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
3061
3083
 
3084
+ /**
3085
+ * Захватывает `<ng-template rtChatMessageActions let-message>` — содержимое «…»-меню
3086
+ * действий у реплики. `rt-chat` рисует этот шаблон рядом с точечными действиями реплики,
3087
+ * прокидывая её через `$implicit`.
3088
+ *
3089
+ * Точечные действия остаются на месте: удаление, повтор отправки и скачивание общи для
3090
+ * любой переписки. Шаблон нужен тем действиям, которых кит не знает и знать не должен, —
3091
+ * без него потребителю оставалось ждать правки кита либо ставить кнопку над диалогом, то
3092
+ * есть не там, где читают реплику.
3093
+ *
3094
+ * Дженерика у контекста нет: тип реплики у переписки один, и type-carrier, который у
3095
+ * строки таблицы несёт тип строки, здесь не нужен.
3096
+ *
3097
+ * @example
3098
+ * ```html
3099
+ * <rt-chat [messages]="messages()" [messageHasActions]="canAct">
3100
+ * <ng-template rtChatMessageActions let-message>
3101
+ * <rt-menu-item icon="link" label="Скопировать ссылку" (selected)="copy(message)" />
3102
+ * </ng-template>
3103
+ * </rt-chat>
3104
+ * ```
3105
+ */
3106
+ class RtChatMessageActionsDirective {
3107
+ constructor() {
3108
+ this.template = inject(TemplateRef);
3109
+ }
3110
+ /** Type-guard: сужает контекст шаблона до реплики при проверке шаблонов. */
3111
+ static ngTemplateContextGuard(_directive,
3112
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате; убрать его нечем, подпись задаёт каркас
3113
+ context) {
3114
+ return true;
3115
+ }
3116
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatMessageActionsDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3117
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.5", type: RtChatMessageActionsDirective, isStandalone: true, selector: "ng-template[rtChatMessageActions]", ngImport: i0 }); }
3118
+ }
3119
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatMessageActionsDirective, decorators: [{
3120
+ type: Directive,
3121
+ args: [{
3122
+ selector: 'ng-template[rtChatMessageActions]',
3123
+ }]
3124
+ }] });
3125
+
3126
+ /**
3127
+ * Есть ли у реплики хотя бы одно доступное действие потребителя.
3128
+ *
3129
+ * Признак приходит предикатом от экрана, а не подсчётом отрисованных пунктов меню:
3130
+ * содержимое спроецированного шаблона известно только после отрисовки, и кнопка
3131
+ * успевала бы мигнуть на первом кадре.
3132
+ *
3133
+ * Предикат не задан — у реплики есть действия: экран, который ничего не гейтит,
3134
+ * показывает кнопку меню. Так же это устроено у строки таблицы, и расхождение между
3135
+ * двумя точками проекции одного кита читалось бы дефектом.
3136
+ */
3137
+ function messageHasAvailableActions(message, hasActions) {
3138
+ return hasActions === null ? true : hasActions(message);
3139
+ }
3140
+
3141
+ /**
3142
+ * Есть ли у реплики доступные действия — гейт кнопки «…» у неё.
3143
+ *
3144
+ * Pure-pipe: пересчитывается только при смене реплики или предиката, а не на каждом
3145
+ * цикле проверки. Значение приходит из контекста шаблона (реплика треда), поэтому
3146
+ * `computed()` здесь неприменим.
3147
+ */
3148
+ class RtChatMessageHasActionsPipe {
3149
+ transform(message, hasActions) {
3150
+ return messageHasAvailableActions(message, hasActions);
3151
+ }
3152
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatMessageHasActionsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
3153
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.0.5", ngImport: i0, type: RtChatMessageHasActionsPipe, isStandalone: true, name: "rtChatMessageHasActions" }); }
3154
+ }
3155
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatMessageHasActionsPipe, decorators: [{
3156
+ type: Pipe,
3157
+ args: [{ name: 'rtChatMessageHasActions' }]
3158
+ }] });
3159
+
3062
3160
  /**
3063
3161
  * Модель общего чат-компонента `rt-chat`: один корневой неймспейс
3064
3162
  * с префиксом `I`. Доменные стора маппят свою модель сообщения в `Message`, а сам
@@ -3103,7 +3201,118 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
3103
3201
  args: [{ name: 'rtChatStatusLabel' }]
3104
3202
  }] });
3105
3203
 
3106
- const BEM_BLOCK$14 = 'rt-confirm-popover';
3204
+ const BEM_BLOCK$14 = 'rt-menu';
3205
+ const POSITION_BELOW_END = {
3206
+ originX: 'end',
3207
+ originY: 'bottom',
3208
+ overlayX: 'end',
3209
+ overlayY: 'top',
3210
+ offsetY: 4,
3211
+ };
3212
+ const POSITION_ABOVE_END = {
3213
+ originX: 'end',
3214
+ originY: 'top',
3215
+ overlayX: 'end',
3216
+ overlayY: 'bottom',
3217
+ offsetY: -4,
3218
+ };
3219
+ const POSITION_BELOW_START = {
3220
+ originX: 'start',
3221
+ originY: 'bottom',
3222
+ overlayX: 'start',
3223
+ overlayY: 'top',
3224
+ offsetY: 4,
3225
+ };
3226
+ const POSITION_ABOVE_START = {
3227
+ originX: 'start',
3228
+ originY: 'top',
3229
+ overlayX: 'start',
3230
+ overlayY: 'bottom',
3231
+ offsetY: -4,
3232
+ };
3233
+ /**
3234
+ * Dropdown-меню действий: icon-only триггер «…» + плавающая панель с пунктами
3235
+ * `rt-menu-item`, спроецированными через `<ng-content>`. Типовое применение —
3236
+ * per-row действия в конце строки `rt-table` (см. `[showRowActions]`).
3237
+ *
3238
+ * Движок — CDK `cdkConnectedOverlay` с прозрачным backdrop: авто-flip у края
3239
+ * viewport (4 fallback-позиции), backdrop-click и `(detach)` закрывают, Escape
3240
+ * закрывает через `(overlayKeydown)`. Панель не режется `overflow: hidden`
3241
+ * предками (таблицы, карточки), т.к. рендерится в overlay-контейнере.
3242
+ *
3243
+ * Выбор пункта закрывает меню: `rt-menu-item` всплывающе диспатчит DOM-событие
3244
+ * `rtMenuSelect`, которое панель ловит, — это переживает content-projection.
3245
+ *
3246
+ * @example
3247
+ * ```html
3248
+ * <rt-menu ariaLabel="Действия">
3249
+ * <rt-menu-item icon="ico-edit" label="Редактировать" (selected)="edit()" />
3250
+ * <rt-menu-item icon="ico-trash" label="Удалить" danger (selected)="remove()" />
3251
+ * </rt-menu>
3252
+ * ```
3253
+ */
3254
+ class RtMenuComponent {
3255
+ constructor() {
3256
+ this.#t_uiActions = rtKitLabel('uiActions');
3257
+ this.isOpen = signal(false, /* @ts-ignore */
3258
+ ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
3259
+ this.ariaText = computed(() => this.ariaLabel() || this.#t_uiActions(), /* @ts-ignore */
3260
+ ...(ngDevMode ? [{ debugName: "ariaText" }] : /* istanbul ignore next */ []));
3261
+ /** Fallback-цепочка позиций панели: первой идёт сторона по `align`. */
3262
+ this.overlayPositions = computed(() => this.align() === 'end'
3263
+ ? [POSITION_BELOW_END, POSITION_ABOVE_END, POSITION_BELOW_START, POSITION_ABOVE_START]
3264
+ : [POSITION_BELOW_START, POSITION_ABOVE_START, POSITION_BELOW_END, POSITION_ABOVE_END], /* @ts-ignore */
3265
+ ...(ngDevMode ? [{ debugName: "overlayPositions" }] : /* istanbul ignore next */ []));
3266
+ /** Иконка триггера. Default `ellipsis-h` («…» — три точки в конце строки). */
3267
+ this.icon = input('ellipsis-h', /* @ts-ignore */
3268
+ ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
3269
+ /** ARIA-метка триггера (icon-only кнопка) + текст tooltip'а. */
3270
+ /** Пусто — берётся переведённая подпись по умолчанию */
3271
+ this.ariaLabel = input('', /* @ts-ignore */
3272
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
3273
+ /** Сторона раскрытия панели относительно триггера. */
3274
+ this.align = input('end', /* @ts-ignore */
3275
+ ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
3276
+ /** Триггер отключён — меню не открыть. */
3277
+ this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3278
+ }
3279
+ #t_uiActions;
3280
+ toggle(event) {
3281
+ // Клик по триггеру не должен активировать строку таблицы под ним.
3282
+ event.stopPropagation();
3283
+ this.isOpen.update((open) => !open);
3284
+ }
3285
+ close() {
3286
+ this.isOpen.set(false);
3287
+ }
3288
+ onKeydown(event) {
3289
+ if (event.key === 'Escape') {
3290
+ this.close();
3291
+ }
3292
+ }
3293
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3294
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: RtMenuComponent, isStandalone: true, selector: "rt-menu", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.rt-menu--open": "isOpen()" }, classAttribute: "rt-menu" }, ngImport: i0, template: "<rt-icon-button\n cdkOverlayOrigin\n qa-dataid=\"menu-trigger\"\n #trigger=\"cdkOverlayOrigin\"\n variant=\"ghost\"\n [icon]=\"icon()\"\n [ariaLabel]=\"ariaText()\"\n [tooltip]=\"ariaText()\"\n [disabled]=\"disabled()\"\n (clicked)=\"toggle($event)\" />\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n (overlayKeydown)=\"onKeydown($event)\">\n <ng-container rtBlock=\"rt-menu\">\n <div rtElem=\"panel\" qa-dataid=\"menu-panel\" role=\"menu\" (rtMenuSelect)=\"close()\">\n <ng-content />\n </div>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu{display:inline-flex;align-items:center;justify-content:center}.rt-menu__panel{--rt-menu-panel-radius: var(--rt-radius-md);--rt-menu-panel-border-width: var(--rt-border-width-thin);--rt-menu-panel-shadow: var(--rt-shadow-lg);display:flex;min-width:180px;flex-direction:column;padding:var(--rt-space-xs);border:var(--rt-menu-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-menu-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-menu-panel-shadow)}}\n"], dependencies: [{ kind: "ngmodule", type:
3295
+ // @angular/cdk
3296
+ OverlayModule }, { kind: "directive", type: i1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type:
3297
+ // standalone components / directives
3298
+ RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
3299
+ }
3300
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtMenuComponent, decorators: [{
3301
+ type: Component,
3302
+ args: [{ selector: 'rt-menu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
3303
+ // @angular/cdk
3304
+ OverlayModule,
3305
+ // standalone components / directives
3306
+ RtIconButtonComponent,
3307
+ BlockDirective,
3308
+ ElemDirective,
3309
+ ], host: {
3310
+ class: BEM_BLOCK$14,
3311
+ '[class.rt-menu--open]': 'isOpen()',
3312
+ }, template: "<rt-icon-button\n cdkOverlayOrigin\n qa-dataid=\"menu-trigger\"\n #trigger=\"cdkOverlayOrigin\"\n variant=\"ghost\"\n [icon]=\"icon()\"\n [ariaLabel]=\"ariaText()\"\n [tooltip]=\"ariaText()\"\n [disabled]=\"disabled()\"\n (clicked)=\"toggle($event)\" />\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n (overlayKeydown)=\"onKeydown($event)\">\n <ng-container rtBlock=\"rt-menu\">\n <div rtElem=\"panel\" qa-dataid=\"menu-panel\" role=\"menu\" (rtMenuSelect)=\"close()\">\n <ng-content />\n </div>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu{display:inline-flex;align-items:center;justify-content:center}.rt-menu__panel{--rt-menu-panel-radius: var(--rt-radius-md);--rt-menu-panel-border-width: var(--rt-border-width-thin);--rt-menu-panel-shadow: var(--rt-shadow-lg);display:flex;min-width:180px;flex-direction:column;padding:var(--rt-space-xs);border:var(--rt-menu-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-menu-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-menu-panel-shadow)}}\n"] }]
3313
+ }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3314
+
3315
+ const BEM_BLOCK$13 = 'rt-confirm-popover';
3107
3316
  /**
3108
3317
  * Презентационная панель подтверждения. Рендерится директивой `[rtConfirm]`
3109
3318
  * через CDK `ComponentPortal` под host-кнопкой. Содержит опциональный заголовок,
@@ -3150,7 +3359,7 @@ class RtConfirmPopoverComponent {
3150
3359
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtConfirmPopoverComponent, decorators: [{
3151
3360
  type: Component,
3152
3361
  args: [{ selector: 'rt-confirm-popover', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [RtButtonDirective, BlockDirective, ElemDirective], host: {
3153
- class: BEM_BLOCK$14,
3362
+ class: BEM_BLOCK$13,
3154
3363
  }, template: "<ng-container rtBlock=\"rt-confirm-popover\">\n @if (title()) {\n <p rtElem=\"title\">{{ title() }}</p>\n }\n\n <p rtElem=\"message\">{{ message() }}</p>\n\n <div rtElem=\"actions\">\n <button\n rtButton\n qa-dataid=\"confirm-cancel\"\n type=\"button\"\n theme=\"secondary\"\n appearance=\"text\"\n [label]=\"cancelText()\"\n [attr.aria-label]=\"cancelText()\"\n (click)=\"onCancel()\"></button>\n <button\n rtButton\n qa-dataid=\"confirm-accept\"\n type=\"button\"\n [theme]=\"tone()\"\n [label]=\"confirmText()\"\n [attr.aria-label]=\"confirmText()\"\n (click)=\"onConfirm()\"></button>\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-confirm-popover{--rt-confirm-popover-radius: var(--rt-radius-md);--rt-confirm-popover-border-width: var(--rt-border-width-thin);--rt-confirm-popover-shadow: var(--rt-shadow-lg);display:flex;width:280px;max-width:280px;flex-direction:column;padding:var(--rt-space-md);border:var(--rt-confirm-popover-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-confirm-popover-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-confirm-popover-shadow);gap:var(--rt-space-sm)}.rt-confirm-popover__title{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-semibold)}.rt-confirm-popover__message{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);line-height:var(--rt-leading-normal)}.rt-confirm-popover__actions{display:flex;justify-content:space-between;gap:var(--rt-space-sm);margin-block-start:var(--rt-space-xs)}}\n"] }]
3155
3364
  }], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "message", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cancelLabel", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], accepted: [{ type: i0.Output, args: ["accepted"] }], cancelled: [{ type: i0.Output, args: ["cancelled"] }] } });
3156
3365
 
@@ -3336,7 +3545,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
3336
3545
  }]
3337
3546
  }], ctorParameters: () => [], propDecorators: { message: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirm", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirmTitle", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirmLabel", required: false }] }], cancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirmCancelLabel", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirmTone", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtConfirmDisabled", required: false }] }], confirmed: [{ type: i0.Output, args: ["confirmed"] }] } });
3338
3547
 
3339
- const BEM_BLOCK$13 = 'rt-delta-view';
3548
+ const BEM_BLOCK$12 = 'rt-delta-view';
3340
3549
  /** Inline-атрибут → оборачивающий тег. Порядок задаёт вложенность обёрток. */
3341
3550
  const INLINE_TAGS = [
3342
3551
  ['bold', 'strong'],
@@ -3463,7 +3672,7 @@ class RtDeltaViewComponent {
3463
3672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtDeltaViewComponent, decorators: [{
3464
3673
  type: Component,
3465
3674
  args: [{ selector: 'rt-delta-view', template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
3466
- class: BEM_BLOCK$13,
3675
+ class: BEM_BLOCK$12,
3467
3676
  }, styles: ["@layer rt-kit.components{.rt-delta-view{display:block}.rt-delta-view p,.rt-delta-view ul,.rt-delta-view ol,.rt-delta-view blockquote,.rt-delta-view pre,.rt-delta-view h1,.rt-delta-view h2,.rt-delta-view h3{margin:0}.rt-delta-view ul,.rt-delta-view ol{padding-inline-start:var(--rt-space-lg)}.rt-delta-view blockquote{border-inline-start:1px solid var(--rt-color-border-default);color:var(--rt-color-text-muted);padding-inline-start:var(--rt-space-sm)}.rt-delta-view pre{margin:0;font-family:monospace;white-space:pre-wrap}}\n"] }]
3468
3677
  }], ctorParameters: () => [], propDecorators: { delta: [{ type: i0.Input, args: [{ isSignal: true, alias: "delta", required: false }] }] } });
3469
3678
 
@@ -4108,7 +4317,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4108
4317
  args: [{ name: 'rtTruncateMiddle' }]
4109
4318
  }] });
4110
4319
 
4111
- const BEM_BLOCK$12 = 'rt-file-card';
4320
+ const BEM_BLOCK$11 = 'rt-file-card';
4112
4321
  /** Иконка типа файла крупнее прочего: по ней карточку находят взглядом. */
4113
4322
  const ICON_SIZES = {
4114
4323
  sm: 'sm',
@@ -4207,14 +4416,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4207
4416
  // pipes
4208
4417
  RtTruncateMiddlePipe,
4209
4418
  ], host: {
4210
- class: BEM_BLOCK$12,
4419
+ class: BEM_BLOCK$11,
4211
4420
  '[class.rt-file-card--sm]': "size() === 'sm'",
4212
4421
  '[class.rt-file-card--md]': "size() === 'md'",
4213
4422
  '[class.rt-file-card--lg]': "size() === 'lg'",
4214
4423
  }, template: "<ng-container rtBlock=\"rt-file-card\">\n <rt-icon rtElem=\"icon\" [size]=\"iconSize()\" [name]=\"iconName()\" />\n\n <span rtElem=\"text\">\n <span rtElem=\"title\" qa-dataid=\"file-card-title\" [rtTooltip]=\"title()\">{{ title() }}</span>\n <span rtElem=\"name\" qa-dataid=\"file-card-name\">{{ name() | rtTruncateMiddle }}</span>\n </span>\n\n <span rtElem=\"meta\">\n @if (hasSize()) {\n <span rtElem=\"size\" qa-dataid=\"file-card-size\">{{ sizeLabel() }}</span>\n }\n\n @if (showDownload()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-download\"\n icon=\"ico-download\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiDownload\"\n [tooltip]=\"t().uiDownload\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onDownload()\" />\n }\n\n @if (showRename()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-rename\"\n icon=\"ico-edit\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRename\"\n [tooltip]=\"t().uiRename\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRename()\" />\n }\n\n @if (showRemove()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"file-card-remove\"\n icon=\"ico-trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiRemove\"\n [tooltip]=\"t().uiRemove\"\n [size]=\"actionSize()\"\n [disabled]=\"disabled()\"\n (clicked)=\"onRemove()\" />\n }\n </span>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-file-card{--rt-file-card-radius: var(--rt-radius-lg);--rt-file-card-border-width: var(--rt-border-width-thin);--rt-file-card-badge-radius: var(--rt-radius-full);--rt-file-card-padding-block: var(--rt-space-sm);--rt-file-card-padding-inline: var(--rt-space-md);--rt-file-card-gap: var(--rt-space-sm);--rt-file-card-title-size: var(--rt-text-md);--rt-file-card-name-size: var(--rt-text-sm);--rt-file-card-badge-size: var(--rt-text-xs);display:flex;align-items:center;padding:var(--rt-file-card-padding-block) var(--rt-file-card-padding-inline);border:var(--rt-file-card-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-file-card-radius);background-color:var(--rt-color-bg-surface);gap:var(--rt-file-card-gap)}.rt-file-card--sm{--rt-file-card-padding-block: var(--rt-space-xs);--rt-file-card-padding-inline: var(--rt-space-sm);--rt-file-card-gap: var(--rt-space-xs);--rt-file-card-title-size: var(--rt-text-xs);--rt-file-card-name-size: var(--rt-text-2xs);--rt-file-card-badge-size: var(--rt-text-2xs)}.rt-file-card--md{--rt-file-card-padding-block: var(--rt-space-sm);--rt-file-card-padding-inline: var(--rt-space-md);--rt-file-card-gap: var(--rt-space-sm);--rt-file-card-title-size: var(--rt-text-md);--rt-file-card-name-size: var(--rt-text-sm);--rt-file-card-badge-size: var(--rt-text-xs)}.rt-file-card--lg{--rt-file-card-padding-block: var(--rt-space-md);--rt-file-card-padding-inline: var(--rt-space-lg);--rt-file-card-gap: var(--rt-space-md);--rt-file-card-title-size: var(--rt-text-lg);--rt-file-card-name-size: var(--rt-text-md);--rt-file-card-badge-size: var(--rt-text-sm)}.rt-file-card__icon{flex-shrink:0}.rt-file-card__text{display:flex;min-width:0;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-2xs)}.rt-file-card__title{overflow:hidden;color:var(--rt-color-text-primary);font-size:var(--rt-file-card-title-size);font-weight:var(--rt-font-weight-semibold);text-overflow:ellipsis;white-space:nowrap}.rt-file-card__name{color:var(--rt-color-text-muted);font-size:var(--rt-file-card-name-size)}.rt-file-card__meta{display:flex;flex-shrink:0;align-items:center;gap:var(--rt-space-xs)}.rt-file-card__size{padding:var(--rt-space-2xs) var(--rt-space-sm);border:var(--rt-file-card-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-file-card-badge-radius);color:var(--rt-color-text-muted);font-size:var(--rt-file-card-badge-size);white-space:nowrap}.rt-file-card__action{flex-shrink:0}}\n"] }]
4215
4424
  }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], sizeBytes: [{ type: i0.Input, args: [{ isSignal: true, alias: "sizeBytes", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], showDownload: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDownload", required: false }] }], showRemove: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRemove", required: false }] }], showRename: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRename", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], download: [{ type: i0.Output, args: ["download"] }], removed: [{ type: i0.Output, args: ["removed"] }], rename: [{ type: i0.Output, args: ["rename"] }] } });
4216
4425
 
4217
- const BEM_BLOCK$11 = 'rt-file-drop';
4426
+ const BEM_BLOCK$10 = 'rt-file-drop';
4218
4427
  /** Задержка скрытия оверлея: dragleave стреляет при переходе между дочерними
4219
4428
  элементами, без debounce рамка мерцает. */
4220
4429
  const DRAG_LEAVE_DEBOUNCE_MS = 80;
@@ -4399,7 +4608,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4399
4608
  ElemDirective,
4400
4609
  ModDirective,
4401
4610
  ], host: {
4402
- class: BEM_BLOCK$11,
4611
+ class: BEM_BLOCK$10,
4403
4612
  }, template: "<ng-container rtBlock=\"rt-file-drop\">\n <ng-content />\n\n @if (isDragging()) {\n <div rtElem=\"overlay\" qa-dataid=\"file-drop-overlay\" aria-hidden=\"true\" [rtMod]=\"{ zoned: hasZones() }\">\n @if (hasZones()) {\n @for (zone of zones(); track zone.id) {\n <div\n rtElem=\"zone\"\n qa-dataid=\"file-drop-zone\"\n [attr.data-zone]=\"zone.id\"\n [attr.data-active]=\"activeZoneId() === zone.id\"\n [rtMod]=\"{ active: activeZoneId() === zone.id }\">\n <span rtElem=\"label\" qa-dataid=\"file-drop-zone-label\">{{ zone.label }}</span>\n @if (zone.sublabel) {\n <span rtElem=\"sublabel\" qa-dataid=\"file-drop-zone-sublabel\">{{ zone.sublabel }}</span>\n }\n </div>\n }\n } @else {\n <div rtElem=\"frame\" qa-dataid=\"file-drop-frame\">\n <span rtElem=\"label\" qa-dataid=\"file-drop-label\">{{ overlayText() }}</span>\n </div>\n }\n </div>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-file-drop{--rt-file-drop-radius: var(--rt-radius-lg);--rt-file-drop-border-width: var(--rt-border-width-thin);--rt-file-drop-transition-duration: var(--rt-duration-fast);position:relative;display:block;height:100%}.rt-file-drop__overlay{position:absolute;z-index:50;display:flex;align-items:center;justify-content:center;padding:var(--rt-space-lg);border-radius:var(--rt-file-drop-radius);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#00000073;inset:var(--rt-space-sm);pointer-events:none}.rt-file-drop__overlay--zoned{flex-direction:column;align-items:stretch;padding:var(--rt-space-md);gap:var(--rt-space-sm)}.rt-file-drop__frame{display:flex;width:100%;height:100%;box-sizing:border-box;align-items:center;justify-content:center;padding:var(--rt-space-md);border:var(--rt-file-drop-border-width) dashed var(--rt-color-action-on-primary);border-radius:var(--rt-file-drop-radius);gap:var(--rt-space-sm);text-align:center}.rt-file-drop__zone{display:flex;min-height:0;box-sizing:border-box;flex:1 1 0;flex-direction:column;align-items:center;justify-content:center;padding:var(--rt-space-md);border:var(--rt-file-drop-border-width) dashed var(--rt-color-action-on-primary);border-radius:var(--rt-file-drop-radius);gap:var(--rt-space-2xs);text-align:center;transition:border-color var(--rt-file-drop-transition-duration) ease,background-color var(--rt-file-drop-transition-duration) ease}.rt-file-drop__zone--active{border-style:solid;border-color:var(--rt-color-action-primary);background:#ffffff1a}.rt-file-drop__label{color:var(--rt-color-action-on-primary);font-size:var(--rt-text-lg);font-weight:var(--rt-font-weight-semibold)}.rt-file-drop__sublabel{color:var(--rt-color-action-on-primary);font-size:var(--rt-text-sm);opacity:.8}}\n"] }]
4404
4613
  }], ctorParameters: () => [], propDecorators: { disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], overlayLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlayLabel", required: false }] }], zones: [{ type: i0.Input, args: [{ isSignal: true, alias: "zones", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], filesDropped: [{ type: i0.Output, args: ["filesDropped"] }], zoneFilesDropped: [{ type: i0.Output, args: ["zoneFilesDropped"] }], draggingChange: [{ type: i0.Output, args: ["draggingChange"] }], onDragOver: [{
4405
4614
  type: HostListener,
@@ -4415,7 +4624,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4415
4624
  args: ['drop', ['$event']]
4416
4625
  }] } });
4417
4626
 
4418
- const BEM_BLOCK$10 = 'rt-rich-editor';
4627
+ const BEM_BLOCK$$ = 'rt-rich-editor';
4419
4628
  /** Модули тулбара — строго whitelist (без link/image/color/script). */
4420
4629
  const TOOLBAR = {
4421
4630
  full: [
@@ -4572,7 +4781,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4572
4781
  // contentChild(RtFormControlBase) (query по абстрактному классу).
4573
4782
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtRichEditorComponent) },
4574
4783
  ], host: {
4575
- class: BEM_BLOCK$10,
4784
+ class: BEM_BLOCK$$,
4576
4785
  // Пока начинка редактора не смонтирована, хост несёт признак недостроенности: снаружи
4577
4786
  // недостроенный редактор ничем не отличается от достроенного пустого, и съёмка витрины
4578
4787
  // ловила именно этот промежуток — кадр выходил втрое ниже эталона.
@@ -4580,7 +4789,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4580
4789
  }, template: "<ng-container rtBlock=\"rt-rich-editor\">\n <div #editor rtElem=\"surface\" qa-dataid=\"rich-editor-surface\"></div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-rich-editor{--rt-rich-editor-radius: var(--rt-radius-md);display:flex;flex-direction:column;min-block-size:0}.rt-rich-editor .ql-toolbar.ql-snow,.rt-rich-editor .ql-container.ql-snow{border-color:var(--rt-color-border-default)}.rt-rich-editor .ql-toolbar.ql-snow{flex:0 0 auto;border-start-end-radius:var(--rt-rich-editor-radius);border-start-start-radius:var(--rt-rich-editor-radius)}.rt-rich-editor .ql-container.ql-snow{flex:1 1 auto;border-end-end-radius:var(--rt-rich-editor-radius);border-end-start-radius:var(--rt-rich-editor-radius);font-family:inherit;font-size:inherit;min-block-size:0;overflow-y:auto}.rt-rich-editor .ql-editor{background-color:var(--rt-color-bg-surface-subtle);color:var(--rt-color-text-primary);min-block-size:96px}.rt-rich-editor .ql-editor.ql-blank:before{color:var(--rt-color-text-muted);font-style:normal}.rt-rich-editor .ql-snow .ql-stroke{stroke:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-fill{fill:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-picker{color:var(--rt-color-text-muted)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-stroke,.rt-rich-editor .ql-snow button.ql-active .ql-stroke{stroke:var(--rt-color-text-link)}.rt-rich-editor .ql-snow .ql-toolbar button:hover .ql-fill,.rt-rich-editor .ql-snow button.ql-active .ql-fill{fill:var(--rt-color-text-link)}}\n"] }]
4581
4790
  }], propDecorators: { editorHost: [{ type: i0.ViewChild, args: ['editor', { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], enterPressed: [{ type: i0.Output, args: ["enterPressed"] }] } });
4582
4791
 
4583
- const BEM_BLOCK$$ = 'rt-message-composer';
4792
+ const BEM_BLOCK$_ = 'rt-message-composer';
4584
4793
  /**
4585
4794
  * Композер сообщения: авто-растущая textarea сверху, под разделительной линией —
4586
4795
  * ряд контролов (слева — прикрепить файл, справа — отправить). Презентационный,
@@ -4718,7 +4927,7 @@ class RtMessageComposerComponent {
4718
4927
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtMessageComposerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
4719
4928
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtMessageComposerComponent, isStandalone: true, selector: "rt-message-composer", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, formatting: { classPropertyName: "formatting", publicName: "formatting", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, minRows: { classPropertyName: "minRows", publicName: "minRows", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, droppedFiles: { classPropertyName: "droppedFiles", publicName: "droppedFiles", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted" }, host: { classAttribute: "rt-message-composer" }, viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n @if (formatting()) {\n <rt-rich-editor\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n } @else {\n <textarea\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n <div rtElem=\"divider\"></div>\n\n <div rtElem=\"controls\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"send\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n </div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-md);--rt-message-composer-border-width: var(--rt-border-width-thin);--rt-message-composer-divider-width: var(--rt-border-width-thin);display:flex;flex-direction:column;padding:var(--rt-space-4);border:var(--rt-message-composer-border-width) solid var(--rt-color-border-default);border-radius:var(--rt-message-composer-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-2)}.rt-message-composer:focus-within{border-color:var(--rt-color-border-strong)}.rt-message-composer__input{padding:0;border:0;margin:0;background:transparent;color:inherit;font:inherit;font-size:var(--rt-text-sm);inline-size:100%;line-height:var(--rt-leading-snug);outline:0;resize:none}.rt-message-composer__input::placeholder{color:var(--rt-color-text-muted)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__divider{background:var(--rt-color-border-default);block-size:var(--rt-message-composer-divider-width);margin-inline:calc(-1 * var(--rt-space-4))}.rt-message-composer__controls{display:flex;align-items:center;gap:var(--rt-space-2)}.rt-message-composer__send{margin-inline-start:auto}}\n"], dependencies: [{ kind: "ngmodule", type:
4720
4929
  // angular
4721
- ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
4930
+ ReactiveFormsModule }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
4722
4931
  // cdk
4723
4932
  CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "component", type:
4724
4933
  // standalone components / directives
@@ -4738,11 +4947,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4738
4947
  BlockDirective,
4739
4948
  ElemDirective,
4740
4949
  ], host: {
4741
- class: BEM_BLOCK$$,
4950
+ class: BEM_BLOCK$_,
4742
4951
  }, template: "<ng-container rtBlock=\"rt-message-composer\" [formGroup]=\"form\">\n @if (formatting()) {\n <rt-rich-editor\n qa-dataid=\"message-composer-rich\"\n formControlName=\"delta\"\n [placeholder]=\"placeholderText()\"\n [toolbar]=\"toolbar()\"\n (enterPressed)=\"submit()\" />\n } @else {\n <textarea\n rtElem=\"input\"\n qa-dataid=\"message-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholderText()\"\n [cdkAutosizeMinRows]=\"minRows()\"\n [cdkAutosizeMaxRows]=\"maxRows()\"\n (keydown)=\"onKeydown($event)\"></textarea>\n }\n\n @if (attachments() && files().length) {\n <div rtElem=\"files\" qa-dataid=\"message-composer-files\">\n @for (file of files(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"message-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"disabled() || sending()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n\n <div rtElem=\"divider\"></div>\n\n <div rtElem=\"controls\">\n @if (attachments()) {\n <input\n #fileEl\n qa-dataid=\"message-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"message-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"disabled() || sending()\"\n (clicked)=\"openPicker()\" />\n }\n\n <rt-icon-button\n rtElem=\"send\"\n qa-dataid=\"message-composer-send\"\n icon=\"send\"\n variant=\"primary\"\n [ariaLabel]=\"t().chatSendAria\"\n [tooltip]=\"t().chatSendLabel\"\n [disabled]=\"!canSend()\"\n (clicked)=\"submit()\" />\n </div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-message-composer{--rt-message-composer-radius: var(--rt-radius-md);--rt-message-composer-border-width: var(--rt-border-width-thin);--rt-message-composer-divider-width: var(--rt-border-width-thin);display:flex;flex-direction:column;padding:var(--rt-space-4);border:var(--rt-message-composer-border-width) solid var(--rt-color-border-default);border-radius:var(--rt-message-composer-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-2)}.rt-message-composer:focus-within{border-color:var(--rt-color-border-strong)}.rt-message-composer__input{padding:0;border:0;margin:0;background:transparent;color:inherit;font:inherit;font-size:var(--rt-text-sm);inline-size:100%;line-height:var(--rt-leading-snug);outline:0;resize:none}.rt-message-composer__input::placeholder{color:var(--rt-color-text-muted)}.rt-message-composer__input:disabled{cursor:not-allowed}.rt-message-composer__files{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-message-composer__divider{background:var(--rt-color-border-default);block-size:var(--rt-message-composer-divider-width);margin-inline:calc(-1 * var(--rt-space-4))}.rt-message-composer__controls{display:flex;align-items:center;gap:var(--rt-space-2)}.rt-message-composer__send{margin-inline-start:auto}}\n"] }]
4743
4952
  }], ctorParameters: () => [], propDecorators: { fileInput: [{ type: i0.ViewChild, args: ['fileEl', { isSignal: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], formatting: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatting", required: false }] }], toolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "toolbar", required: false }] }], minRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "minRows", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], droppedFiles: [{ type: i0.Input, args: [{ isSignal: true, alias: "droppedFiles", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }] } });
4744
4953
 
4745
- const BEM_BLOCK$_ = 'rt-note';
4954
+ const BEM_BLOCK$Z = 'rt-note';
4746
4955
  /**
4747
4956
  * Тихая информационная заметка-callout для асайдов и форм: приглушённый текст
4748
4957
  * на subtle-фоне, без иконки и severity-цвета. Назначение — пояснить контекст
@@ -4769,12 +4978,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4769
4978
  ], host: {
4770
4979
  // Block-класс на host (BlockDirective пропускает comment-ноду ng-container),
4771
4980
  // role — на том же элементе: rt-note самодостаточен, без внутреннего контейнера.
4772
- class: BEM_BLOCK$_,
4981
+ class: BEM_BLOCK$Z,
4773
4982
  role: 'note',
4774
4983
  }, template: "<ng-container rtBlock=\"rt-note\">\n <ng-content />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-note{--rt-note-radius: var(--rt-radius-md);display:block;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-note-radius);background:var(--rt-color-bg-subtle);color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);line-height:var(--rt-leading-normal)}}\n"] }]
4775
4984
  }] });
4776
4985
 
4777
- const BEM_BLOCK$Z = 'rt-spinner';
4986
+ const BEM_BLOCK$Y = 'rt-spinner';
4778
4987
  /**
4779
4988
  * Атомарный rotating-spinner для loading-индикаторов.
4780
4989
  *
@@ -4798,8 +5007,8 @@ class RtSpinnerComponent {
4798
5007
  }
4799
5008
  get hostClasses() {
4800
5009
  return {
4801
- [BEM_BLOCK$Z]: true,
4802
- [`${BEM_BLOCK$Z}--${this.color()}`]: true,
5010
+ [BEM_BLOCK$Y]: true,
5011
+ [`${BEM_BLOCK$Y}--${this.color()}`]: true,
4803
5012
  };
4804
5013
  }
4805
5014
  get cssDiameter() {
@@ -4811,7 +5020,7 @@ class RtSpinnerComponent {
4811
5020
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtSpinnerComponent, decorators: [{
4812
5021
  type: Component,
4813
5022
  args: [{ selector: 'rt-spinner', template: '', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, host: {
4814
- class: BEM_BLOCK$Z,
5023
+ class: BEM_BLOCK$Y,
4815
5024
  role: 'status',
4816
5025
  'aria-live': 'polite',
4817
5026
  }, styles: ["@layer rt-kit.components{.rt-spinner{--rt-spinner-diameter: 32px;--rt-spinner-border-width: 3px;--rt-spinner-track-color: var(--rt-color-border-subtle);--rt-spinner-progress-color: var(--rt-color-action-primary);--rt-spinner-rotation-duration: 1s;--rt-spinner-rotation-duration-reduced: 4s;--rt-spinner-radius: var(--rt-radius-full);display:inline-block;width:var(--rt-spinner-diameter);height:var(--rt-spinner-diameter);box-sizing:border-box;border:var(--rt-spinner-border-width) solid var(--rt-spinner-track-color);border-radius:var(--rt-spinner-radius);border-right-color:var(--rt-spinner-progress-color);animation:rt-spinner-rotate var(--rt-spinner-rotation-duration) linear infinite}.rt-spinner--primary{--rt-spinner-progress-color: var(--rt-color-action-primary)}.rt-spinner--neutral{--rt-spinner-progress-color: var(--rt-color-text-primary)}.rt-spinner--on-primary{--rt-spinner-track-color: var(--rt-overlay-white-25);--rt-spinner-progress-color: var(--rt-color-action-on-primary)}@keyframes rt-spinner-rotate{to{transform:rotate(1turn)}}@media(prefers-reduced-motion:reduce){.rt-spinner{animation-duration:var(--rt-spinner-rotation-duration-reduced)}}}\n"] }]
@@ -4912,7 +5121,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4912
5121
  * </rt-toolbar>
4913
5122
  * ```
4914
5123
  */
4915
- const BEM_BLOCK$Y = 'rt-toolbar';
5124
+ const BEM_BLOCK$X = 'rt-toolbar';
4916
5125
  class RtToolbarComponent {
4917
5126
  constructor() {
4918
5127
  this.leftTpl = contentChild(RtToolbarLeftDirective, { ...(ngDevMode ? { debugName: "leftTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
@@ -4942,23 +5151,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
4942
5151
  ElemDirective,
4943
5152
  ModDirective,
4944
5153
  ], host: {
4945
- class: BEM_BLOCK$Y,
5154
+ class: BEM_BLOCK$X,
4946
5155
  '[class.rt-toolbar--dense]': 'dense()',
4947
5156
  }, template: "@if (leftTpl() || centerTpl() || rightTpl()) {\n <ng-container rtBlock=\"rt-toolbar\">\n <div rtElem=\"bars\">\n @if (leftTpl()) {\n <div rtElem=\"bar\" qa-dataid=\"toolbar-bar\" data-slot=\"left\" [rtMod]=\"{ left: true }\">\n <ng-container [ngTemplateOutlet]=\"leftTpl() ?? null\" />\n </div>\n }\n\n @if (centerTpl()) {\n <div rtElem=\"bar\" qa-dataid=\"toolbar-bar\" data-slot=\"center\" [rtMod]=\"{ center: true }\">\n <ng-container [ngTemplateOutlet]=\"centerTpl() ?? null\" />\n </div>\n }\n\n @if (rightTpl()) {\n <div rtElem=\"bar\" qa-dataid=\"toolbar-bar\" data-slot=\"right\" [rtMod]=\"{ right: true }\">\n <ng-container [ngTemplateOutlet]=\"rightTpl() ?? null\" />\n </div>\n }\n </div>\n </ng-container>\n}\n", styles: ["@layer rt-kit.components{.rt-toolbar{display:block}.rt-toolbar__bars{display:flex;width:100%;flex-direction:row;align-items:center;justify-content:space-between;padding:var(--rt-space-2) 0;gap:var(--rt-space-2)}.rt-toolbar__bar{display:flex;align-items:center}.rt-toolbar__bar--left{flex:0 0 auto;gap:var(--rt-space-2)}.rt-toolbar__bar--center{flex:1 1 auto;justify-content:flex-end;gap:var(--rt-space-2)}.rt-toolbar__bar--center rt-input,.rt-toolbar__bar--center rt-select{width:100%}.rt-toolbar__bar--right{flex:0 0 auto;margin-left:auto;gap:var(--rt-space-sm)}@media(width<=768px){.rt-toolbar:not(.rt-toolbar--dense) .rt-toolbar__bars{flex-direction:column;align-items:stretch;gap:var(--rt-space-sm)}.rt-toolbar:not(.rt-toolbar--dense) .rt-toolbar__bar{flex-wrap:wrap}.rt-toolbar:not(.rt-toolbar--dense) .rt-toolbar__bar--center{justify-content:stretch}.rt-toolbar:not(.rt-toolbar--dense) .rt-toolbar__bar--right{justify-content:flex-end;margin-left:0}}}\n"] }]
4948
5157
  }], propDecorators: { leftTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtToolbarLeftDirective), { ...{ read: TemplateRef }, isSignal: true }] }], centerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtToolbarCenterDirective), { ...{ read: TemplateRef }, isSignal: true }] }], rightTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtToolbarRightDirective), { ...{ read: TemplateRef }, isSignal: true }] }], dense: [{ type: i0.Input, args: [{ isSignal: true, alias: "dense", required: false }] }] } });
4949
5158
 
4950
- const BEM_BLOCK$X = 'rt-chat';
4951
- /** Ключ заглушки имени вложения, когда домен не отдал `fileName` (только URL). */
4952
- const ATTACHMENT_FALLBACK_KEY = 'chatAttachmentFallback';
4953
- /** Зазор от низа треда, в пределах которого считаем пользователя «у нижнего края». */
4954
- const NEAR_BOTTOM_THRESHOLD_PX = 64;
5159
+ /** Выбранные файлы вместе с только что добавленными. Список всегда новый: его читает сигнал. */
5160
+ function withAppendedFiles(current, added) {
5161
+ return [...current, ...added];
5162
+ }
5163
+ /** Выбранные файлы без того, что стоит на этом месте. */
5164
+ function withoutFileAt(current, index) {
5165
+ return current.filter((_, position) => position !== index);
5166
+ }
5167
+ /** Файлы, названные полем выбора. Поле без выбора отдаёт пустой список, а не отсутствие. */
5168
+ function pickedFiles(files) {
5169
+ return Array.from(files ?? []);
5170
+ }
5171
+
5172
+ /** Зазор от низа треда, в пределах которого читатель считается стоящим у нижнего края. */
5173
+ const RT_CHAT_NEAR_BOTTOM_THRESHOLD_PX = 64;
5174
+ /**
5175
+ * Задержки повторных подтягиваний ленты к низу после первой прокрутки. Присланное
5176
+ * содержимое дорисовывается несколькими проходами уже после того, как лента встала по
5177
+ * промежуточной высоте, и набор тиков покрывает окно, за которое высота устаканивается.
5178
+ */
5179
+ const RT_CHAT_PIN_RETRY_DELAYS_MS = [50, 150, 300, 500, 800];
5180
+ /** Стоит ли читатель у нижнего края треда. */
5181
+ function isNearBottom(metrics) {
5182
+ return metrics.scrollHeight - metrics.clientHeight - metrics.scrollTop <= RT_CHAT_NEAR_BOTTOM_THRESHOLD_PX;
5183
+ }
4955
5184
  /**
4956
- * Задержки (мс) серии повторных пинов к низу после первичной прокрутки. Rich-контент
4957
- * (`rt-delta-view`, код-блоки) дорисовывается несколькими render-пассами уже после
4958
- * того, как afterRenderEffect выставил scrollTop по промежуточной высоте, — набор
4959
- * тиков покрывает окно, за которое высота устаканивается.
5185
+ * Прилипает ли лента к низу после этой прокрутки.
5186
+ *
5187
+ * Прилипание снимается только тогда, когда читатель уехал вверх сам. Прокрутка, сделанная
5188
+ * самим компонентом, и рост содержимого приходят с тем же или большим положением — на них
5189
+ * прилипание лишь подтверждается: иначе позднее событие от собственной прокрутки читалось бы
5190
+ * отрывом от низа, и лента переставала бы догонять последнее сообщение.
4960
5191
  */
4961
- const PIN_RETRY_DELAYS_MS = [50, 150, 300, 500, 800];
5192
+ function nextAtBottom(previous, lastScrollTop, metrics) {
5193
+ const nearBottom = isNearBottom(metrics);
5194
+ return metrics.scrollTop < lastScrollTop - 1 ? nearBottom : previous || nearBottom;
5195
+ }
5196
+
5197
+ const BEM_BLOCK$W = 'rt-chat';
5198
+ /** Ключ заглушки имени вложения, когда домен не отдал `fileName` (только URL). */
5199
+ const ATTACHMENT_FALLBACK_KEY = 'chatAttachmentFallback';
4962
5200
  /**
4963
5201
  * Общий презентационный чат: тред сообщений + композер. Никакого data-access/api —
4964
5202
  * история приходит готовым массивом `IRtChat.Message[]`, отправка/обновление/
@@ -5080,6 +5318,13 @@ class RtChatComponent {
5080
5318
  */
5081
5319
  this.droppedFiles = signal(null, /* @ts-ignore */
5082
5320
  ...(ngDevMode ? [{ debugName: "droppedFiles" }] : /* istanbul ignore next */ []));
5321
+ /**
5322
+ * Шаблон действий у реплики: `<ng-template rtChatMessageActions let-message>`.
5323
+ *
5324
+ * Не объявлен — разметка реплики прежняя, и лишней кнопки у неё не появляется.
5325
+ */
5326
+ this.messageActions = contentChild(RtChatMessageActionsDirective, /* @ts-ignore */
5327
+ ...(ngDevMode ? [{ debugName: "messageActions" }] : /* istanbul ignore next */ []));
5083
5328
  /** История сообщений (consumer уже смаппил из доменной модели). */
5084
5329
  this.messages = input([], /* @ts-ignore */
5085
5330
  ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
@@ -5141,7 +5386,8 @@ class RtChatComponent {
5141
5386
  this.send = output();
5142
5387
  /** Клик refresh — consumer перетягивает тред. */
5143
5388
  this.refresh = output();
5144
- /** Клик по вложению — consumer качает blob своим api. */
5389
+ /** Клик по вложению — consumer качает blob своим api. Шаблон зовёт выход прямо: релей-метод,
5390
+ * который только повторял `emit`, ничего не добавлял, а файл при этом рос. */
5145
5391
  this.downloadFile = output();
5146
5392
  /** Клик по карточке вложения из `attachments[]` — консюмер качает по `publicId`. */
5147
5393
  this.downloadAttachment = output();
@@ -5150,6 +5396,14 @@ class RtChatComponent {
5150
5396
  this.deleteMessage = output();
5151
5397
  /** Повторная отправка своего сообщения со статусом `failed`. */
5152
5398
  this.retryMessage = output();
5399
+ /**
5400
+ * Есть ли у реплики действия потребителя — гейт кнопки «…» у неё.
5401
+ *
5402
+ * Не задан — точка действий показывается у каждой реплики: экран, который ничего не
5403
+ * гейтит, показывает её так же, как это делает строка таблицы.
5404
+ */
5405
+ this.messageHasActions = input(null, /* @ts-ignore */
5406
+ ...(ngDevMode ? [{ debugName: "messageHasActions" }] : /* istanbul ignore next */ []));
5153
5407
  this.#formValue = toSignal(this.replyForm.valueChanges, {
5154
5408
  initialValue: this.replyForm.value,
5155
5409
  });
@@ -5230,57 +5484,39 @@ class RtChatComponent {
5230
5484
  this.droppedFiles.set([...files]);
5231
5485
  return;
5232
5486
  }
5233
- this.replyForm.controls.files.setValue([...this.replyForm.controls.files.value, ...files]);
5487
+ this.replyForm.controls.files.setValue(withAppendedFiles(this.replyForm.controls.files.value, files));
5234
5488
  }
5235
5489
  openFilePicker() {
5236
5490
  this.composerFileEl()?.nativeElement.click();
5237
5491
  }
5238
5492
  onFilesPicked(event) {
5239
5493
  const inputEl = event.target;
5240
- const picked = Array.from(inputEl.files ?? []);
5494
+ const picked = pickedFiles(inputEl.files);
5241
5495
  if (picked.length > 0) {
5242
- this.replyForm.controls.files.setValue([...this.replyForm.controls.files.value, ...picked]);
5496
+ this.replyForm.controls.files.setValue(withAppendedFiles(this.replyForm.controls.files.value, picked));
5243
5497
  }
5244
5498
  // Сброс значения: без него повторный выбор того же файла не даёт `change`.
5245
5499
  inputEl.value = '';
5246
5500
  }
5247
5501
  removeFile(index) {
5248
- const next = this.replyForm.controls.files.value.filter((_, position) => position !== index);
5249
- this.replyForm.controls.files.setValue(next);
5502
+ this.replyForm.controls.files.setValue(withoutFileAt(this.replyForm.controls.files.value, index));
5250
5503
  }
5251
- /**
5252
- * Обновляем «у нижнего ли края» по скроллу — основа решения об автоскролле.
5253
- * Прилипание к низу снимаем ТОЛЬКО когда пользователь прокрутил ВВЕРХ (scrollTop
5254
- * уменьшился). Событие от программной прокрутки и рост контента идут при том же
5255
- * или большем scrollTop — тогда прилипание лишь усиливаем, но не снимаем. Иначе
5256
- * async-событие от нашего же `scrollTop = scrollHeight`, долетающее уже после
5257
- * поздней дорисовки rich-контента (высота выросла, scrollTop прежний → мнимый
5258
- * «отрыв от низа»), сбрасывало бы `#atBottom` и ломало до-прокрутку.
5259
- */
5504
+ /** Признак «читатель у нижнего края» обновляется по прокрутке: на нём стоит автопрокрутка. */
5260
5505
  onThreadScroll() {
5261
5506
  const el = this.thread()?.nativeElement;
5262
5507
  if (el === undefined) {
5263
5508
  return;
5264
5509
  }
5265
- const scrollTop = el.scrollTop;
5266
- const nearBottom = el.scrollHeight - el.clientHeight - scrollTop <= NEAR_BOTTOM_THRESHOLD_PX;
5267
- if (scrollTop < this.#lastScrollTop - 1) {
5268
- this.#atBottom = nearBottom;
5269
- }
5270
- else {
5271
- this.#atBottom = this.#atBottom || nearBottom;
5272
- }
5273
- this.#lastScrollTop = scrollTop;
5510
+ this.#atBottom = nextAtBottom(this.#atBottom, this.#lastScrollTop, el);
5511
+ this.#lastScrollTop = el.scrollTop;
5274
5512
  }
5275
5513
  /**
5276
- * Серия отложенных пинов к низу: rich-контент дорисовывается несколькими
5277
- * render-пассами (иногда с пересозданием `#thread`), поэтому одного скролла в
5278
- * afterRenderEffect мало — финальная высота устанавливается позже. Каждый тик
5279
- * заново берёт актуальный элемент треда и его текущий scrollHeight и, пока
5280
- * пользователь у нижнего края, дотягивает ленту до конца.
5514
+ * Серия отложенных подтягиваний ленты к низу: присланное содержимое дорисовывается
5515
+ * несколькими проходами, и одной прокрутки мало. Каждый тик берёт тред заново — он
5516
+ * бывает пересоздан — и дотягивает ленту, пока читатель стоит у нижнего края.
5281
5517
  */
5282
5518
  #pinToBottomDeferred() {
5283
- for (const delay of PIN_RETRY_DELAYS_MS) {
5519
+ for (const delay of RT_CHAT_PIN_RETRY_DELAYS_MS) {
5284
5520
  setTimeout(() => {
5285
5521
  const el = this.thread()?.nativeElement;
5286
5522
  if (el !== undefined && this.#atBottom) {
@@ -5289,35 +5525,17 @@ class RtChatComponent {
5289
5525
  }, delay);
5290
5526
  }
5291
5527
  }
5292
- onRefresh() {
5293
- this.refresh.emit();
5294
- }
5295
- onDownload(message) {
5296
- this.downloadFile.emit(message);
5297
- }
5298
- /** Релей клика по вложению наружу — сам компонент файлов не качает. */
5299
- onDownloadAttachment(attachment) {
5300
- this.downloadAttachment.emit(attachment);
5301
- }
5302
- /** Релей запроса на удаление сообщения — удаляет домен, компонент только рисует. */
5303
- onDeleteMessage(message) {
5304
- this.deleteMessage.emit(message);
5305
- }
5306
- /** Релей повтора отправки упавшего сообщения — переотправляет домен. */
5307
- onRetryMessage(message) {
5308
- this.retryMessage.emit(message);
5309
- }
5310
5528
  toggleExpand() {
5311
5529
  const next = !this.expanded();
5312
5530
  this.expanded.set(next);
5313
5531
  this.expandedChange.emit(next);
5314
5532
  }
5315
5533
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
5316
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtChatComponent, isStandalone: true, selector: "rt-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fetching: { classPropertyName: "fetching", publicName: "fetching", isSignal: true, isRequired: false, transformFunction: null }, canReply: { classPropertyName: "canReply", publicName: "canReply", isSignal: true, isRequired: false, transformFunction: null }, replyBlockReason: { classPropertyName: "replyBlockReason", publicName: "replyBlockReason", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, emptyHint: { classPropertyName: "emptyHint", publicName: "emptyHint", isSignal: true, isRequired: false, transformFunction: null }, hasThread: { classPropertyName: "hasThread", publicName: "hasThread", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, richComposer: { classPropertyName: "richComposer", publicName: "richComposer", isSignal: true, isRequired: false, transformFunction: null }, formatting: { classPropertyName: "formatting", publicName: "formatting", isSignal: true, isRequired: false, transformFunction: null }, formattingToolbar: { classPropertyName: "formattingToolbar", publicName: "formattingToolbar", isSignal: true, isRequired: false, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showExpand: { classPropertyName: "showExpand", publicName: "showExpand", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", refresh: "refresh", downloadFile: "downloadFile", downloadAttachment: "downloadAttachment", expandedChange: "expandedChange", deleteMessage: "deleteMessage", retryMessage: "retryMessage" }, host: { properties: { "class.rt-chat--expanded": "expanded()", "class.rt-chat--fill": "fill()" }, classAttribute: "rt-chat" }, viewQueries: [{ propertyName: "thread", first: true, predicate: ["thread"], descendants: true, isSignal: true }, { propertyName: "composerFileEl", first: true, predicate: ["composerFileEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"onRefresh()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"onDeleteMessage(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"onDeleteMessage(m)\" />\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"onDownloadAttachment(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"onDownload(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"onRetryMessage(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\" />\n } @else {\n <form rtElem=\"composer\" qa-dataid=\"chat-composer\" [formGroup]=\"replyForm\" (ngSubmit)=\"onSubmit()\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"], dependencies: [{ kind: "directive", type:
5534
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtChatComponent, isStandalone: true, selector: "rt-chat", inputs: { messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fetching: { classPropertyName: "fetching", publicName: "fetching", isSignal: true, isRequired: false, transformFunction: null }, canReply: { classPropertyName: "canReply", publicName: "canReply", isSignal: true, isRequired: false, transformFunction: null }, replyBlockReason: { classPropertyName: "replyBlockReason", publicName: "replyBlockReason", isSignal: true, isRequired: false, transformFunction: null }, sending: { classPropertyName: "sending", publicName: "sending", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, emptyHint: { classPropertyName: "emptyHint", publicName: "emptyHint", isSignal: true, isRequired: false, transformFunction: null }, hasThread: { classPropertyName: "hasThread", publicName: "hasThread", isSignal: true, isRequired: false, transformFunction: null }, attachments: { classPropertyName: "attachments", publicName: "attachments", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, richComposer: { classPropertyName: "richComposer", publicName: "richComposer", isSignal: true, isRequired: false, transformFunction: null }, formatting: { classPropertyName: "formatting", publicName: "formatting", isSignal: true, isRequired: false, transformFunction: null }, formattingToolbar: { classPropertyName: "formattingToolbar", publicName: "formattingToolbar", isSignal: true, isRequired: false, transformFunction: null }, fill: { classPropertyName: "fill", publicName: "fill", isSignal: true, isRequired: false, transformFunction: null }, showRefresh: { classPropertyName: "showRefresh", publicName: "showRefresh", isSignal: true, isRequired: false, transformFunction: null }, showExpand: { classPropertyName: "showExpand", publicName: "showExpand", isSignal: true, isRequired: false, transformFunction: null }, messageHasActions: { classPropertyName: "messageHasActions", publicName: "messageHasActions", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { send: "send", refresh: "refresh", downloadFile: "downloadFile", downloadAttachment: "downloadAttachment", expandedChange: "expandedChange", deleteMessage: "deleteMessage", retryMessage: "retryMessage" }, host: { properties: { "class.rt-chat--expanded": "expanded()", "class.rt-chat--fill": "fill()" }, classAttribute: "rt-chat" }, queries: [{ propertyName: "messageActions", first: true, predicate: RtChatMessageActionsDirective, descendants: true, isSignal: true }], viewQueries: [{ propertyName: "thread", first: true, predicate: ["thread"], descendants: true, isSignal: true }, { propertyName: "composerFileEl", first: true, predicate: ["composerFileEl"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\" />\n } @else {\n <form rtElem=\"composer\" qa-dataid=\"chat-composer\" [formGroup]=\"replyForm\" (ngSubmit)=\"onSubmit()\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"], dependencies: [{ kind: "directive", type:
5317
5535
  // Angular
5318
- CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
5536
+ CdkTextareaAutosize, selector: "textarea[cdkTextareaAutosize]", inputs: ["cdkAutosizeMinRows", "cdkAutosizeMaxRows", "cdkTextareaAutosize", "placeholder"], exportAs: ["cdkTextareaAutosize"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type:
5319
5537
  // standalone components / directives
5320
- RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "rounded", "loading", "loadingIcon"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "size", "color", "rotate"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "attachments", "sending", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles"], outputs: ["submitted"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "borderRadius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
5538
+ RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "rounded", "loading", "loadingIcon"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDeltaViewComponent, selector: "rt-delta-view", inputs: ["delta"] }, { kind: "component", type: RtFileCardComponent, selector: "rt-file-card", inputs: ["name", "sizeBytes", "size", "showDownload", "showRemove", "showRename", "disabled"], outputs: ["download", "removed", "rename"] }, { kind: "component", type: RtFileDropComponent, selector: "rt-file-drop", inputs: ["disabled", "overlayLabel", "zones", "accept"], outputs: ["filesDropped", "zoneFilesDropped", "draggingChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "size", "color", "rotate"] }, { kind: "component", type: RtMenuComponent, selector: "rt-menu", inputs: ["icon", "ariaLabel", "align", "disabled"] }, { kind: "component", type: RtMessageComposerComponent, selector: "rt-message-composer", inputs: ["placeholder", "accept", "attachments", "sending", "disabled", "formatting", "toolbar", "minRows", "maxRows", "droppedFiles"], outputs: ["submitted"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSkeletonWrapperComponent, selector: "rt-skeleton-wrapper", inputs: ["width", "height", "shape", "size", "borderRadius", "animation", "isLoading"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color"] }, { kind: "component", type: RtToolbarComponent, selector: "rt-toolbar", inputs: ["dense"] }, { kind: "directive", type: RtToolbarLeftDirective, selector: "[rtToolbarLeft]" }, { kind: "directive", type: RtToolbarRightDirective, selector: "[rtToolbarRight]" }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: RtChatStatusLabelPipe, name: "rtChatStatusLabel" }, { kind: "pipe", type: RtChatMessageHasActionsPipe, name: "rtChatMessageHasActions" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
5321
5539
  }
5322
5540
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtChatComponent, decorators: [{
5323
5541
  type: Component,
@@ -5325,6 +5543,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5325
5543
  // Angular
5326
5544
  CdkTextareaAutosize,
5327
5545
  DatePipe,
5546
+ NgTemplateOutlet,
5328
5547
  ReactiveFormsModule,
5329
5548
  // standalone components / directives
5330
5549
  RtButtonDirective,
@@ -5335,6 +5554,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5335
5554
  RtFileDropComponent,
5336
5555
  RtIconButtonComponent,
5337
5556
  RtIconComponent,
5557
+ RtChatMessageHasActionsPipe,
5558
+ RtMenuComponent,
5338
5559
  RtMessageComposerComponent,
5339
5560
  RtNoteComponent,
5340
5561
  RtSkeletonWrapperComponent,
@@ -5346,11 +5567,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5346
5567
  ElemDirective,
5347
5568
  ModDirective,
5348
5569
  ], host: {
5349
- class: BEM_BLOCK$X,
5570
+ class: BEM_BLOCK$W,
5350
5571
  '[class.rt-chat--expanded]': 'expanded()',
5351
5572
  '[class.rt-chat--fill]': 'fill()',
5352
- }, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"onRefresh()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"onDeleteMessage(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"onDeleteMessage(m)\" />\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"onDownloadAttachment(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"onDownload(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"onRetryMessage(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\" />\n } @else {\n <form rtElem=\"composer\" qa-dataid=\"chat-composer\" [formGroup]=\"replyForm\" (ngSubmit)=\"onSubmit()\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"] }]
5353
- }], ctorParameters: () => [], propDecorators: { thread: [{ type: i0.ViewChild, args: ['thread', { isSignal: true }] }], composerFileEl: [{ type: i0.ViewChild, args: ['composerFileEl', { isSignal: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetching", required: false }] }], canReply: [{ type: i0.Input, args: [{ isSignal: true, alias: "canReply", required: false }] }], replyBlockReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "replyBlockReason", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], emptyHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyHint", required: false }] }], hasThread: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasThread", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], richComposer: [{ type: i0.Input, args: [{ isSignal: true, alias: "richComposer", required: false }] }], formatting: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatting", required: false }] }], formattingToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "formattingToolbar", required: false }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showExpand: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExpand", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], downloadFile: [{ type: i0.Output, args: ["downloadFile"] }], downloadAttachment: [{ type: i0.Output, args: ["downloadAttachment"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }], deleteMessage: [{ type: i0.Output, args: ["deleteMessage"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }] } });
5573
+ }, template: "<ng-container rtBlock=\"rt-chat\">\n <rt-file-drop\n [overlayLabel]=\"t().chatDropOverlay\"\n [accept]=\"accept()\"\n [disabled]=\"dropDisabled()\"\n (filesDropped)=\"onFilesDropped($event)\">\n @if (showHeader()) {\n <rt-toolbar rtElem=\"header\" dense>\n <ng-template rtToolbarLeft>\n <h3 rtElem=\"title\" qa-dataid=\"chat-title\">{{ title() }}</h3>\n </ng-template>\n <ng-template rtToolbarRight>\n @if (showRefresh()) {\n <rt-icon-button\n qa-dataid=\"chat-refresh\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRefreshAria\"\n [tooltip]=\"t().chatRefreshTooltip\"\n [loading]=\"fetching()\"\n [disabled]=\"!hasThread()\"\n (clicked)=\"refresh.emit()\" />\n }\n @if (showExpand()) {\n <rt-icon-button\n qa-dataid=\"chat-expand\"\n variant=\"ghost\"\n [icon]=\"expanded() ? 'window-minimize' : 'window-maximize'\"\n [ariaLabel]=\"expandLabel()\"\n [tooltip]=\"expandLabel()\"\n (clicked)=\"toggleExpand()\" />\n }\n <ng-content select=\"[rtChatHeaderActions]\" />\n </ng-template>\n </rt-toolbar>\n }\n\n @if (!hasThread()) {\n <rt-note qa-dataid=\"chat-empty-hint\">{{ emptyHint() }}</rt-note>\n } @else {\n <div rtElem=\"thread-viewport\" qa-dataid=\"chat-thread-viewport\">\n @if (loading()) {\n <!-- \u0421\u043A\u0435\u043B\u0435\u0442\u043E\u043D \u043F\u043E\u0432\u0442\u043E\u0440\u044F\u0435\u0442 \u0441\u0442\u0440\u0443\u043A\u0442\u0443\u0440\u0443 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u044F (\u0430\u0432\u0442\u043E\u0440 \u2192 \u0442\u0435\u043A\u0441\u0442 \u2192 \u0434\u0430\u0442\u0430),\n \u0447\u0435\u0440\u0435\u0434\u0443\u044F \u0432\u044B\u0440\u0430\u0432\u043D\u0438\u0432\u0430\u043D\u0438\u0435, \u0447\u0442\u043E\u0431\u044B \u0442\u0440\u0435\u0434 \u043D\u0435 \u00AB\u043F\u0440\u044B\u0433\u0430\u043B\u00BB \u043F\u0440\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u0435 \u043D\u0430 \u0440\u0435\u0430\u043B\u044C\u043D\u044B\u0435. -->\n <div rtElem=\"thread\" qa-dataid=\"chat-thread-skeleton\">\n @for (row of skeletonRows; track row; let even = $even) {\n <div rtElem=\"message-skeleton\" qa-dataid=\"chat-message-skeleton\" [rtMod]=\"{ own: even }\">\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"72px\" height=\"10px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"100%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"55%\" height=\"13px\" [isLoading]=\"true\" />\n <rt-skeleton-wrapper rtElem=\"skeleton-line\" width=\"56px\" height=\"10px\" [isLoading]=\"true\" />\n </div>\n }\n </div>\n } @else {\n <div\n #thread\n rtElem=\"thread\"\n qa-dataid=\"chat-thread\"\n [rtMod]=\"{ frozen: fetching() }\"\n [attr.aria-busy]=\"fetching() ? 'true' : null\"\n (scroll)=\"onThreadScroll()\">\n @for (m of messages(); track m.id) {\n <div\n rtElem=\"message-row\"\n qa-dataid=\"chat-message\"\n [attr.data-own]=\"m.own\"\n [attr.data-status]=\"m.status || null\"\n [attr.data-deleted]=\"m.deleted || null\"\n [rtMod]=\"{ own: m.own }\">\n @if (m.canDelete && !m.deleted) {\n @if (m.deleteConfirm) {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n [rtConfirm]=\"m.deleteConfirm\"\n (confirmed)=\"deleteMessage.emit(m)\" />\n } @else {\n <rt-icon-button\n rtElem=\"message-delete\"\n qa-dataid=\"chat-message-delete\"\n icon=\"trash\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatDeleteAria\"\n [tooltip]=\"t().chatDeleteTooltip\"\n (clicked)=\"deleteMessage.emit(m)\" />\n }\n }\n @if (messageActions()?.template; as actionsTpl) {\n @if (m | rtChatMessageHasActions: messageHasActions()) {\n <div rtElem=\"message-actions\" qa-dataid=\"chat-message-actions\">\n <rt-menu>\n <ng-container\n [ngTemplateOutlet]=\"actionsTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: m }\" />\n </rt-menu>\n </div>\n }\n }\n <div rtElem=\"message\" [rtMod]=\"{ own: m.own, deleted: m.deleted }\">\n <p rtElem=\"message-author\" qa-dataid=\"chat-message-author\">{{ m.author }}</p>\n @if (m.deleted) {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-deleted\" rtMod=\"deleted\">\n {{ t().chatMessageDeleted }}\n </p>\n } @else {\n @if (m.delta) {\n <rt-delta-view rtElem=\"message-text\" qa-dataid=\"chat-message-text\" [delta]=\"m.delta\" />\n } @else {\n <p rtElem=\"message-text\" qa-dataid=\"chat-message-text\">{{ m.text }}</p>\n }\n @if (m.attachments?.length) {\n @for (att of m.attachments; track att.id) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"att.name || attachmentFallbackName()\"\n (download)=\"downloadAttachment.emit(att)\" />\n </span>\n }\n } @else if (m.fileUrl) {\n <span rtElem=\"message-attachment\" qa-dataid=\"chat-message-attachment\">\n <rt-file-card\n showDownload\n size=\"sm\"\n [name]=\"m.fileName || attachmentFallbackName()\"\n (download)=\"downloadFile.emit(m)\" />\n </span>\n }\n @if (m.deepLink) {\n <span rtElem=\"message-deeplink\">\n <a\n rtButton\n qa-dataid=\"chat-message-deeplink\"\n appearance=\"outlined\"\n icon=\"send\"\n rel=\"noopener\"\n target=\"_blank\"\n [href]=\"m.deepLink\"\n [label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"\n [attr.aria-label]=\"m.deepLinkLabel || t().uiSubscribeNotifications\"></a>\n </span>\n }\n }\n <span rtElem=\"message-footer\">\n <span rtElem=\"message-date\" qa-dataid=\"chat-message-date\">\n {{ m.createdAt | date: 'short' : undefined : locale() }}\n </span>\n @if (m.status) {\n <span\n rtElem=\"message-status\"\n qa-dataid=\"chat-message-status\"\n [attr.data-status]=\"m.status\"\n [rtMod]=\"{ status: m.status }\"\n [attr.aria-label]=\"m.status | rtChatStatusLabel\">\n @if (m.status === MessageStatus.Failed) {\n <rt-icon rtElem=\"status-icon\" name=\"exclamation-circle\" size=\"sm\" />\n <rt-icon-button\n qa-dataid=\"chat-message-retry\"\n icon=\"sync\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatRetryAria\"\n [tooltip]=\"t().chatRetryTooltip\"\n (clicked)=\"retryMessage.emit(m)\" />\n } @else {\n <!-- \u0427\u0430\u0441\u044B, \u043F\u043E\u043A\u0430 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0432 \u043F\u0443\u0442\u0438; \u0434\u0430\u043B\u044C\u0448\u0435 \u0434\u0432\u043E\u0439\u043D\u043E\u0439 \u0447\u0435\u043A, \u0446\u0432\u0435\u0442 \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E\n (\u0441\u0435\u0440\u044B\u0439/\u0441\u0438\u043D\u0438\u0439) \u0440\u0430\u0437\u043B\u0438\u0447\u0430\u0435\u0442 \u0434\u043E\u0441\u0442\u0430\u0432\u043B\u0435\u043D\u043E \u0438 \u043F\u0440\u043E\u0447\u0438\u0442\u0430\u043D\u043E. -->\n <rt-icon\n rtElem=\"status-icon\"\n size=\"sm\"\n [name]=\"m.status === MessageStatus.Sending ? 'clock' : 'check-double'\" />\n }\n </span>\n }\n </span>\n </div>\n </div>\n } @empty {\n <rt-note qa-dataid=\"chat-empty-thread\">{{ t().chatEmptyThread }}</rt-note>\n }\n </div>\n }\n @if (!loading() && fetching()) {\n <div rtElem=\"thread-overlay\" qa-dataid=\"chat-thread-overlay\" role=\"status\" [attr.aria-label]=\"t().chatRefreshAria\">\n <rt-spinner diameter=\"32\" />\n </div>\n }\n </div>\n\n @if (canReply()) {\n @if (richComposer()) {\n <rt-message-composer\n rtElem=\"composer-rich\"\n qa-dataid=\"chat-composer-rich\"\n [placeholder]=\"placeholder()\"\n [accept]=\"accept()\"\n [attachments]=\"attachments()\"\n [formatting]=\"formatting()\"\n [toolbar]=\"formattingToolbar()\"\n [sending]=\"sending()\"\n [disabled]=\"loading() || fetching()\"\n [droppedFiles]=\"droppedFiles()\"\n (submitted)=\"onComposerSubmit($event)\" />\n } @else {\n <form rtElem=\"composer\" qa-dataid=\"chat-composer\" [formGroup]=\"replyForm\" (ngSubmit)=\"onSubmit()\">\n @if (attachments() && selectedFiles().length > 0) {\n <div rtElem=\"composer-files\" qa-dataid=\"chat-composer-files\">\n @for (file of selectedFiles(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"chat-composer-file\"\n size=\"sm\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"composerDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </div>\n }\n <div rtElem=\"composer-field\" [rtMod]=\"{ attach: attachments() }\">\n @if (attachments()) {\n <input\n #composerFileEl\n qa-dataid=\"chat-composer-file-input\"\n type=\"file\"\n hidden\n multiple\n [attr.accept]=\"accept() || null\"\n (change)=\"onFilesPicked($event)\" />\n <rt-icon-button\n qa-dataid=\"chat-composer-attach\"\n icon=\"paperclip\"\n variant=\"ghost\"\n [ariaLabel]=\"t().chatAttachAria\"\n [tooltip]=\"t().chatAttachTooltip\"\n [disabled]=\"composerDisabled()\"\n (clicked)=\"openFilePicker()\" />\n }\n <textarea\n rtElem=\"composer-input\"\n qa-dataid=\"chat-composer-input\"\n cdkTextareaAutosize\n formControlName=\"message\"\n cdkAutosizeMinRows=\"1\"\n cdkAutosizeMaxRows=\"4\"\n [attr.aria-label]=\"t().chatComposerAria\"\n [placeholder]=\"placeholder()\"\n (keydown)=\"onComposerKeydown($event)\"></textarea>\n </div>\n <button\n rtButton\n rtElem=\"composer-send\"\n qa-dataid=\"chat-composer-send\"\n type=\"submit\"\n theme=\"primary\"\n appearance=\"filled\"\n [label]=\"t().chatSendLabel\"\n [attr.aria-label]=\"t().chatSendAria\"\n [loading]=\"sending()\"\n [disabled]=\"!canSend()\"></button>\n </form>\n }\n } @else if (replyBlockReason()) {\n <rt-note rtElem=\"reply-block\" qa-dataid=\"chat-reply-block\">{{ replyBlockReason() }}</rt-note>\n }\n\n <!-- \u0421\u043B\u043E\u0442 \u0434\u043E\u043C\u0435\u043D\u043D\u044B\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u043F\u043E\u0434 \u043A\u043E\u043C\u043F\u043E\u0437\u0435\u0440\u043E\u043C: \u0432 fullscreen\n \u043F\u043E\u043F\u0430\u0434\u0430\u0435\u0442 \u0432\u043D\u0443\u0442\u0440\u044C overlay, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 \u043A\u043D\u043E\u043F\u043A\u0438 \u043E\u0441\u0442\u0430\u044E\u0442\u0441\u044F \u0432\u0438\u0434\u043D\u044B. -->\n <div rtElem=\"footer-slot\" qa-dataid=\"chat-footer\" [rtMod]=\"{ blocked: fetching() }\" [attr.inert]=\"fetching() ? '' : null\">\n <ng-content select=\"[rtChatFooter]\" />\n </div>\n }\n </rt-file-drop>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-chat{--rt-chat-bubble-radius: var(--rt-radius-lg);--rt-chat-message-border-width: var(--rt-border-width-thin);--rt-chat-transition-duration: var(--rt-duration-fast);display:flex;flex-direction:column}.rt-chat>.rt-file-drop{display:flex;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0}.rt-chat--expanded{position:fixed;z-index:var(--rt-z-modal);box-sizing:border-box;padding:var(--rt-space-md);background:var(--rt-color-bg-surface);inset:0}.rt-chat--expanded>.rt-file-drop{block-size:100%}@media(width>=1441px){.rt-chat--expanded>.rt-file-drop{inline-size:1000px;margin-inline:auto;max-inline-size:100%}}.rt-chat--expanded .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--expanded .rt-chat__thread{max-height:none}.rt-chat--fill{block-size:100%}.rt-chat--fill .rt-chat__thread-viewport{flex:1 1 auto;min-block-size:0}.rt-chat--fill .rt-chat__thread{max-height:none}.rt-chat__title{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__thread-viewport{position:relative;display:flex;flex-direction:column;contain:inline-size;min-block-size:0}.rt-chat__thread{display:flex;max-height:280px;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-4);min-block-size:0;overflow-y:auto}.rt-chat__thread--frozen{overflow-y:hidden}.rt-chat__thread-overlay{position:absolute;z-index:1;display:flex;align-items:center;justify-content:center;background:color-mix(in srgb,var(--rt-color-bg-surface) 60%,transparent);inset:0}.rt-chat__footer-slot{display:flex;flex-direction:column}.rt-chat__footer-slot:empty{display:none}.rt-chat__footer-slot--blocked{opacity:.6;pointer-events:none}.rt-chat__message-row{display:flex;max-width:min(80%,480px);align-items:center;align-self:flex-start;gap:var(--rt-space-2)}.rt-chat__message-row--own{align-self:flex-end}.rt-chat__message-row:hover .rt-chat__message-delete,.rt-chat__message-row:focus-within .rt-chat__message-delete,.rt-chat__message-row:hover .rt-chat__message-actions,.rt-chat__message-row:focus-within .rt-chat__message-actions{opacity:1;pointer-events:auto}.rt-chat__message{display:flex;min-width:0;flex:0 1 auto;flex-direction:column;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-2)}.rt-chat__message--own{border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__message--deleted{border:var(--rt-chat-message-border-width) dashed var(--rt-color-border-default);background:transparent}.rt-chat__message-author{margin:0;color:var(--rt-chat-bubble-in-author);font-size:var(--rt-text-2xs);font-weight:var(--rt-font-weight-bold);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-chat__message--own .rt-chat__message-author{color:var(--rt-chat-bubble-own-author)}.rt-chat__message-text{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);line-height:var(--rt-leading-snug);white-space:pre-wrap}.rt-chat__message-text--deleted{color:var(--rt-color-text-muted);font-style:italic}.rt-chat__message-delete,.rt-chat__message-actions{flex:0 0 auto;opacity:0;pointer-events:none;transition:opacity var(--rt-chat-transition-duration) ease}.rt-chat__message-attachment{display:block}.rt-chat__message-footer{display:flex;align-items:center;gap:var(--rt-space-1)}.rt-chat__message-date{color:var(--rt-chat-bubble-date);font-size:var(--rt-text-xs);line-height:var(--rt-leading-none)}.rt-chat__message-status{display:inline-flex;align-items:center;color:var(--rt-color-text-muted);gap:var(--rt-space-0-5)}.rt-chat__message-status--status--read{color:var(--rt-chat-status-read)}.rt-chat__message-status--status--failed{color:var(--rt-color-state-danger)}.rt-chat__status-icon{font-size:var(--rt-text-sm)}.rt-chat__message-skeleton{display:flex;max-width:min(80%,480px);flex-direction:column;align-self:flex-start;padding:var(--rt-space-2);border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0;background:var(--rt-chat-bubble-in-bg);gap:var(--rt-space-1)}.rt-chat__message-skeleton--own{align-self:flex-end;border-radius:var(--rt-chat-bubble-radius) var(--rt-chat-bubble-radius) 0 var(--rt-chat-bubble-radius);background:var(--rt-chat-bubble-own-bg)}.rt-chat__skeleton-line{display:block}.rt-chat__message-deeplink{display:flex;justify-content:flex-start;margin-top:var(--rt-space-1)}.rt-chat__composer,.rt-chat__composer-rich,.rt-chat__reply-block{margin-top:var(--rt-space-4)}.rt-chat__composer{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-chat__composer-files{display:flex;flex-wrap:wrap;gap:var(--rt-space-2)}.rt-chat__composer-field{display:flex;box-sizing:border-box;align-items:center;border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);gap:var(--rt-space-1);min-block-size:var(--rt-input-height);padding-inline:var(--rt-input-padding-x);transition:var(--rt-input-transition)}.rt-chat__composer-field:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow)}.rt-chat__composer-field--attach{padding-inline-start:var(--rt-space-1)}.rt-chat__composer-input{box-sizing:content-box;flex:1 1 auto;border:0;background:transparent;color:var(--rt-input-color-text);font-family:inherit;font-size:var(--rt-input-font-size);inline-size:100%;line-height:var(--rt-textarea-line-height);padding-block:var(--rt-space-2);resize:none}.rt-chat__composer-input::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-chat__composer-input:focus-visible{outline:none}.rt-chat__composer-input:disabled{cursor:not-allowed}.rt-chat__composer-send{inline-size:100%}}\n"] }]
5574
+ }], ctorParameters: () => [], propDecorators: { thread: [{ type: i0.ViewChild, args: ['thread', { isSignal: true }] }], composerFileEl: [{ type: i0.ViewChild, args: ['composerFileEl', { isSignal: true }] }], messageActions: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtChatMessageActionsDirective), { isSignal: true }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], fetching: [{ type: i0.Input, args: [{ isSignal: true, alias: "fetching", required: false }] }], canReply: [{ type: i0.Input, args: [{ isSignal: true, alias: "canReply", required: false }] }], replyBlockReason: [{ type: i0.Input, args: [{ isSignal: true, alias: "replyBlockReason", required: false }] }], sending: [{ type: i0.Input, args: [{ isSignal: true, alias: "sending", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], emptyHint: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyHint", required: false }] }], hasThread: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasThread", required: false }] }], attachments: [{ type: i0.Input, args: [{ isSignal: true, alias: "attachments", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], richComposer: [{ type: i0.Input, args: [{ isSignal: true, alias: "richComposer", required: false }] }], formatting: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatting", required: false }] }], formattingToolbar: [{ type: i0.Input, args: [{ isSignal: true, alias: "formattingToolbar", required: false }] }], fill: [{ type: i0.Input, args: [{ isSignal: true, alias: "fill", required: false }] }], showRefresh: [{ type: i0.Input, args: [{ isSignal: true, alias: "showRefresh", required: false }] }], showExpand: [{ type: i0.Input, args: [{ isSignal: true, alias: "showExpand", required: false }] }], send: [{ type: i0.Output, args: ["send"] }], refresh: [{ type: i0.Output, args: ["refresh"] }], downloadFile: [{ type: i0.Output, args: ["downloadFile"] }], downloadAttachment: [{ type: i0.Output, args: ["downloadAttachment"] }], expandedChange: [{ type: i0.Output, args: ["expandedChange"] }], deleteMessage: [{ type: i0.Output, args: ["deleteMessage"] }], retryMessage: [{ type: i0.Output, args: ["retryMessage"] }], messageHasActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "messageHasActions", required: false }] }] } });
5354
5575
 
5355
5576
  /**
5356
5577
  * Статус уже сохранённого своего сообщения: сервер знает только, дошло ли оно до
@@ -5413,7 +5634,7 @@ function toRtMessage(message, options) {
5413
5634
  };
5414
5635
  }
5415
5636
 
5416
- const BEM_BLOCK$W = 'rt-checkbox';
5637
+ const BEM_BLOCK$V = 'rt-checkbox';
5417
5638
  /**
5418
5639
  * Чекбокс с `role="checkbox"` + `aria-checked` (`true`/`false`/`mixed`) поверх
5419
5640
  * нативного `<button>` — клавиатурный toggle (Enter/Space) работает «из коробки»
@@ -5506,11 +5727,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5506
5727
  multi: true,
5507
5728
  },
5508
5729
  ], host: {
5509
- class: BEM_BLOCK$W,
5730
+ class: BEM_BLOCK$V,
5510
5731
  }, template: "<button\n rtBlock=\"rt-checkbox\"\n qa-dataid=\"checkbox-control\"\n type=\"button\"\n role=\"checkbox\"\n [rtMod]=\"{\n checked: isChecked() && !indeterminate(),\n indeterminate: indeterminate(),\n disabled: isDisabled(),\n }\"\n [attr.id]=\"inputId()\"\n [attr.aria-checked]=\"indeterminate() ? 'mixed' : isChecked()\"\n [attr.aria-label]=\"ariaLabel()\"\n [disabled]=\"isDisabled()\"\n (click)=\"toggle()\">\n <span rtElem=\"box\">\n @if (indeterminate()) {\n <rt-icon rtElem=\"icon\" name=\"minus\" size=\"xs\" color=\"inverse\" />\n } @else if (isChecked()) {\n <rt-icon rtElem=\"icon\" name=\"check\" size=\"xs\" color=\"inverse\" />\n }\n </span>\n\n <span rtElem=\"label\" qa-dataid=\"checkbox-label\">\n <ng-content />\n </span>\n</button>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-checkbox{display:inline-block}rt-checkbox .rt-checkbox{--rt-checkbox-focus-radius: var(--rt-radius-sm);--rt-checkbox-focus-shadow: var(--rt-shadow-focus-ring);--rt-checkbox-box-radius: var(--rt-radius-sm);--rt-checkbox-transition-duration: var(--rt-duration-fast);display:inline-flex;align-items:center;padding:0;border:none;background:none;cursor:pointer;gap:var(--rt-space-sm);text-align:start}rt-checkbox .rt-checkbox:focus-visible{border-radius:var(--rt-checkbox-focus-radius);box-shadow:var(--rt-checkbox-focus-shadow);outline:2px solid transparent;outline-offset:2px}rt-checkbox .rt-checkbox--disabled{cursor:not-allowed;opacity:.5}rt-checkbox .rt-checkbox__box{display:inline-flex;width:18px;height:18px;flex-shrink:0;align-items:center;justify-content:center;border:var(--rt-input-border-width) solid var(--rt-color-border-strong);border-radius:var(--rt-checkbox-box-radius);background:var(--rt-color-bg-surface);transition:background var(--rt-checkbox-transition-duration) ease,border-color var(--rt-checkbox-transition-duration) ease}rt-checkbox .rt-checkbox--checked .rt-checkbox__box,rt-checkbox .rt-checkbox--indeterminate .rt-checkbox__box{border-color:var(--rt-color-action-primary);background:var(--rt-color-action-primary)}rt-checkbox .rt-checkbox__label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);-webkit-user-select:none;user-select:none}rt-checkbox .rt-checkbox__label:empty{display:none}}\n"] }]
5511
5732
  }], propDecorators: { inputId: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputId", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }] } });
5512
5733
 
5513
- const BEM_BLOCK$V = 'rt-collapsible-text';
5734
+ const BEM_BLOCK$U = 'rt-collapsible-text';
5514
5735
  class RtCollapsibleTextComponent {
5515
5736
  #resizeObserver;
5516
5737
  constructor() {
@@ -5571,7 +5792,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5571
5792
  ElemDirective,
5572
5793
  ModDirective,
5573
5794
  ], host: {
5574
- class: BEM_BLOCK$V,
5795
+ class: BEM_BLOCK$U,
5575
5796
  }, template: "<ng-container rtBlock=\"rt-collapsible-text\">\n <div\n #text\n rtElem=\"text\"\n qa-dataid=\"collapsible-text-body\"\n [rtMod]=\"{ clamped: !expanded() }\"\n [style.-webkit-line-clamp]=\"expanded() ? null : clampLines()\">\n @for (paragraph of paragraphs(); track $index) {\n <p rtElem=\"paragraph\" qa-dataid=\"collapsible-text-paragraph\">{{ paragraph }}</p>\n }\n </div>\n @if (overflowing() || expanded()) {\n <button rtElem=\"toggle\" qa-dataid=\"collapsible-text-toggle\" type=\"button\" [attr.aria-expanded]=\"expanded()\" (click)=\"toggle()\">\n {{ expanded() ? t().uiCollapse : t().uiMore }}\n </button>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-collapsible-text{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-collapsible-text__text{display:block}.rt-collapsible-text__text--clamped{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical}.rt-collapsible-text__paragraph{margin:0;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-collapsible-text__paragraph+.rt-collapsible-text__paragraph{margin-top:var(--rt-space-sm)}.rt-collapsible-text__toggle{align-self:flex-start;padding:0;border:0;background:transparent;color:var(--rt-color-text-link);cursor:pointer;font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}}\n"] }]
5576
5797
  }], ctorParameters: () => [], propDecorators: { textRef: [{ type: i0.ViewChild, args: ['text', { isSignal: true }] }], paragraphs: [{ type: i0.Input, args: [{ isSignal: true, alias: "paragraphs", required: true }] }], clampLines: [{ type: i0.Input, args: [{ isSignal: true, alias: "clampLines", required: false }] }] } });
5577
5798
 
@@ -5748,7 +5969,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5748
5969
 
5749
5970
  const RT_TOASTER_GAP_PX = 14;
5750
5971
 
5751
- const BEM_BLOCK$U = 'rt-toast';
5972
+ const BEM_BLOCK$T = 'rt-toast';
5752
5973
  const UNMOUNT_DELAY_MS = 200;
5753
5974
  class RtToastComponent {
5754
5975
  #host;
@@ -5768,14 +5989,14 @@ class RtToastComponent {
5768
5989
  #closeTimerPausedAt;
5769
5990
  get hostModifierClasses() {
5770
5991
  return {
5771
- [`${BEM_BLOCK$U}--severity--${this.toast().severity}`]: true,
5772
- [`${BEM_BLOCK$U}--filled`]: this.toast().filled === true,
5773
- [`${BEM_BLOCK$U}--y--${this.#positionY()}`]: true,
5774
- [`${BEM_BLOCK$U}--mounted`]: this.#mounted(),
5775
- [`${BEM_BLOCK$U}--removed`]: this.#removed(),
5776
- [`${BEM_BLOCK$U}--front`]: this.index() === 0,
5777
- [`${BEM_BLOCK$U}--hidden`]: this.index() + 1 > this.visibleToasts(),
5778
- [`${BEM_BLOCK$U}--expanded`]: this.expanded() || (this.expandByDefault() && this.#mounted()),
5992
+ [`${BEM_BLOCK$T}--severity--${this.toast().severity}`]: true,
5993
+ [`${BEM_BLOCK$T}--filled`]: this.toast().filled === true,
5994
+ [`${BEM_BLOCK$T}--y--${this.#positionY()}`]: true,
5995
+ [`${BEM_BLOCK$T}--mounted`]: this.#mounted(),
5996
+ [`${BEM_BLOCK$T}--removed`]: this.#removed(),
5997
+ [`${BEM_BLOCK$T}--front`]: this.index() === 0,
5998
+ [`${BEM_BLOCK$T}--hidden`]: this.index() + 1 > this.visibleToasts(),
5999
+ [`${BEM_BLOCK$T}--expanded`]: this.expanded() || (this.expandByDefault() && this.#mounted()),
5779
6000
  };
5780
6001
  }
5781
6002
  get hostStackStyles() {
@@ -5901,7 +6122,7 @@ class RtToastComponent {
5901
6122
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtToastComponent, decorators: [{
5902
6123
  type: Component,
5903
6124
  args: [{ selector: 'rt-toast', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [RtButtonDirective, RtIconComponent, RtIconButtonComponent, BlockDirective, ElemDirective], host: {
5904
- class: BEM_BLOCK$U,
6125
+ class: BEM_BLOCK$T,
5905
6126
  role: 'status',
5906
6127
  'aria-atomic': 'true',
5907
6128
  }, template: "<ng-container rtBlock=\"rt-toast\">\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"icon()\" [color]=\"toast().severity\" />\n <div rtElem=\"content\" qa-dataid=\"toast-content\">\n @if (toast().meta; as meta) {\n <span rtElem=\"meta\" qa-dataid=\"toast-meta\">{{ meta }}</span>\n }\n <span rtElem=\"message\" qa-dataid=\"toast-message\">{{ toast().message }}</span>\n @if (toast().description; as description) {\n <span rtElem=\"description\" qa-dataid=\"toast-description\">{{ description }}</span>\n }\n @if (toast().action || toast().secondaryAction) {\n <div rtElem=\"actions\">\n @if (toast().action; as action) {\n <button rtButton rtElem=\"action\" qa-dataid=\"toast-action\" size=\"sm\" type=\"button\" (click)=\"onAction()\">\n {{ action.label }}\n </button>\n }\n @if (toast().secondaryAction; as secondaryAction) {\n <button\n rtButton\n rtElem=\"action\"\n qa-dataid=\"toast-secondary-action\"\n size=\"sm\"\n theme=\"secondary\"\n appearance=\"text\"\n type=\"button\"\n (click)=\"onSecondaryAction()\">\n {{ secondaryAction.label }}\n </button>\n }\n </div>\n }\n </div>\n <rt-icon-button\n rtElem=\"close\"\n qa-dataid=\"toast-close\"\n icon=\"ico-close\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().uiClose\"\n (clicked)=\"onClose()\" />\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-toast{--rt-toast-radius: var(--rt-radius-md);--rt-toast-border-width: var(--rt-border-width-thin);--rt-toast-shadow: var(--rt-shadow-card);--rt-toast-enter-duration: var(--rt-duration-slow);--rt-toast-shadow-duration: var(--rt-duration-base);--rt-toast-leave-duration: var(--rt-duration-slower);--rt-toast-fade-duration: var(--rt-duration-fast);--y: translateY(100%);--lift-amount: calc(var(--lift) * var(--gap));position:absolute;z-index:var(--z-index);display:flex;width:100%;box-sizing:border-box;align-items:center;padding:var(--rt-space-4);border:var(--rt-toast-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-toast-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-toast-shadow);color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium);gap:var(--rt-space-2);line-height:var(--rt-leading-normal);opacity:0;outline:none;overflow-wrap:anywhere;touch-action:none;transform:var(--y);transition:transform var(--rt-toast-enter-duration),opacity var(--rt-toast-enter-duration),height var(--rt-toast-enter-duration),box-shadow var(--rt-toast-shadow-duration)}.rt-toast:after{position:absolute;bottom:100%;left:0;width:100%;height:calc(var(--gap) + 1px);content:\"\"}.rt-toast>*{transition:opacity var(--rt-toast-enter-duration)}.rt-toast--y--top{--y: translateY(-100%);--lift: 1;top:0}.rt-toast--y--bottom{--y: translateY(100%);--lift: -1;bottom:0}.rt-toast--filled .rt-toast__description{color:inherit}.rt-toast--filled.rt-toast--severity--info{border-color:var(--rt-color-state-info-bg);background:var(--rt-color-state-info-bg);color:var(--rt-color-state-info)}.rt-toast--filled.rt-toast--severity--success{border-color:var(--rt-color-state-success-bg);background:var(--rt-color-state-success-bg);color:var(--rt-color-state-success)}.rt-toast--filled.rt-toast--severity--warning{border-color:var(--rt-color-state-warning-bg);background:var(--rt-color-state-warning-bg);color:var(--rt-color-state-warning)}.rt-toast--filled.rt-toast--severity--danger{border-color:var(--rt-color-state-danger-bg);background:var(--rt-color-state-danger-bg);color:var(--rt-color-state-danger)}.rt-toast--mounted{--y: translateY(0);opacity:1}.rt-toast:not(.rt-toast--expanded,.rt-toast--front){--scale: var(--toasts-before) * .05 + 1;--y: translateY(calc(var(--lift-amount) * var(--toasts-before))) scale(calc(-1 * var(--scale)));height:var(--front-toast-height)}.rt-toast:not(.rt-toast--expanded,.rt-toast--front)>*{opacity:0}.rt-toast--hidden{opacity:0;pointer-events:none}.rt-toast--mounted.rt-toast--expanded{--y: translateY(calc(var(--lift) * var(--offset)));height:var(--initial-height)}.rt-toast--removed.rt-toast--front{--y: translateY(calc(var(--lift) * -100%));opacity:0}.rt-toast--removed.rt-toast--expanded:not(.rt-toast--front){--y: translateY(calc(var(--lift) * var(--offset) + var(--lift) * -100%));opacity:0}.rt-toast--removed:not(.rt-toast--front,.rt-toast--expanded){--y: translateY(40%);opacity:0;transition:transform var(--rt-toast-leave-duration),opacity var(--rt-toast-shadow-duration)}.rt-toast--removed:not(.rt-toast--front):before{position:absolute;content:\"\";inset:0;transform:scaleY(2)}.rt-toast__icon{flex-shrink:0}.rt-toast__content{display:flex;min-width:0;flex:1 1 auto;flex-direction:column;gap:var(--rt-space-0-5)}.rt-toast__description,.rt-toast__meta{color:var(--rt-color-text-muted);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular)}.rt-toast__actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--rt-space-2);margin-block-start:var(--rt-space-1)}.rt-toast .rt-toast__action{align-self:flex-start}.rt-toast .rt-toast__close{flex-shrink:0;align-self:flex-start;margin-inline-start:auto;opacity:0;transition:opacity var(--rt-toast-fade-duration)}.rt-toast:hover .rt-toast__close,.rt-toast .rt-toast__close:focus-visible{opacity:1}@media(prefers-reduced-motion){.rt-toast{transition:none}.rt-toast>*{transition:none}}}\n"] }]
@@ -5913,7 +6134,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
5913
6134
  args: ['style']
5914
6135
  }] } });
5915
6136
 
5916
- const BEM_BLOCK$T = 'rt-toaster';
6137
+ const BEM_BLOCK$S = 'rt-toaster';
5917
6138
  const DEFAULT_DURATION_MS = 4000;
5918
6139
  const DEFAULT_VISIBLE_TOASTS = 3;
5919
6140
  class RtToasterComponent {
@@ -5921,8 +6142,8 @@ class RtToasterComponent {
5921
6142
  #toastCounter;
5922
6143
  get hostModifierClasses() {
5923
6144
  return {
5924
- [`${BEM_BLOCK$T}--y--${this.positionY()}`]: true,
5925
- [`${BEM_BLOCK$T}--x--${this.positionX()}`]: true,
6145
+ [`${BEM_BLOCK$S}--y--${this.positionY()}`]: true,
6146
+ [`${BEM_BLOCK$S}--x--${this.positionX()}`]: true,
5926
6147
  };
5927
6148
  }
5928
6149
  get hostStackStyles() {
@@ -6012,7 +6233,7 @@ class RtToasterComponent {
6012
6233
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtToasterComponent, decorators: [{
6013
6234
  type: Component,
6014
6235
  args: [{ selector: 'rt-toaster', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [RtToastComponent], host: {
6015
- class: BEM_BLOCK$T,
6236
+ class: BEM_BLOCK$S,
6016
6237
  role: 'region',
6017
6238
  '[attr.aria-label]': 'ariaLabel()',
6018
6239
  '(mouseenter)': 'onStackEnter()',
@@ -6220,7 +6441,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
6220
6441
 
6221
6442
  /** Запас поверх длительности transition'а для страховочного таймера. */
6222
6443
  const ASIDE_TRANSITION_BUFFER_MS = 80;
6223
- const BEM_BLOCK$S = 'rt-container';
6444
+ const BEM_BLOCK$R = 'rt-container';
6224
6445
  /**
6225
6446
  * Multi-slot container с CSS-grid layout. Slot consumers wrap content в
6226
6447
  * `*rtContainerXxx` structural directives, контейнер подхватывает шаблоны через
@@ -6474,7 +6695,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
6474
6695
  ElemDirective,
6475
6696
  ModDirective,
6476
6697
  ], providers: [RtContainerStateService], host: {
6477
- class: BEM_BLOCK$S,
6698
+ class: BEM_BLOCK$R,
6478
6699
  '[class.rt-container--mobile-left-bottom]': "mobileLeftNav() === 'bottom'",
6479
6700
  '[class.rt-container--viewport]': "height() === 'viewport'",
6480
6701
  }, template: "<ng-container rtBlock=\"rt-container\">\n @if (headerTpl(); as tpl) {\n <header rtElem=\"header\" qa-dataid=\"container-header\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </header>\n }\n\n @if (leftSidenavTpl(); as tpl) {\n <aside rtElem=\"left-sidenav\" qa-dataid=\"container-left-sidenav\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </aside>\n }\n\n @if (toolbarLeftTpl(); as tpl) {\n <div rtElem=\"toolbar\" qa-dataid=\"container-toolbar\" data-slot=\"left\" [rtMod]=\"{ slot: 'left' }\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </div>\n }\n\n @if (toolbarCenterTpl(); as tpl) {\n <div rtElem=\"toolbar\" qa-dataid=\"container-toolbar\" data-slot=\"center\" [rtMod]=\"{ slot: 'center' }\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </div>\n }\n\n @if (toolbarRightTpl(); as tpl) {\n <div rtElem=\"toolbar\" qa-dataid=\"container-toolbar\" data-slot=\"right\" [rtMod]=\"{ slot: 'right' }\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </div>\n }\n\n @if (contentTpl(); as tpl) {\n <main rtElem=\"content\" qa-dataid=\"container-content\">\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n </main>\n }\n\n <rt-toaster position=\"bottom-center\" />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{:root{--rt-aside-animation-duration: .2s;--rt-aside-backdrop-bg: var(--rt-color-bg-overlay);--rt-aside-backdrop-blur: 5px}.cdk-overlay-backdrop.rt-aside-backdrop{-webkit-backdrop-filter:blur(var(--rt-aside-backdrop-blur));backdrop-filter:blur(var(--rt-aside-backdrop-blur));background:var(--rt-aside-backdrop-bg);opacity:0;pointer-events:none;transition:opacity var(--rt-aside-animation-duration) ease}.cdk-overlay-backdrop.rt-aside-backdrop.rt-aside-backdrop--visible{opacity:1;pointer-events:auto}.rt-aside-overlay{height:100dvh;transition:transform var(--rt-aside-animation-duration) ease}.rt-aside-overlay:not(.rt-aside-overlay--open){pointer-events:none}.rt-aside-overlay.rt-aside-overlay--entering{transition:none}.rt-aside-overlay--position-right.rt-aside-overlay--entering,.rt-aside-overlay--position-right.rt-aside-overlay--leaving{transform:translate(100%)}.rt-aside-overlay--position-left.rt-aside-overlay--entering,.rt-aside-overlay--position-left.rt-aside-overlay--leaving{transform:translate(-100%)}.rt-aside-overlay--position-right.rt-aside-overlay--open,.rt-aside-overlay--position-left.rt-aside-overlay--open{transform:translate(0)}@media(width<=768px){.rt-aside-overlay{overflow:hidden;width:100dvw;max-width:100dvw}}@media(prefers-reduced-motion:reduce){.rt-aside-overlay{transition:none}}}@layer rt-kit.components{.rt-container{--rt-container-border-width: var(--rt-border-width-thin);display:grid;min-height:100%;flex:1 1 auto;background-color:var(--rt-color-bg-page);color:var(--rt-color-text-primary);grid-template:\"header header\" auto \"left-sidenav toolbar\" auto \"left-sidenav content\" 1fr/auto 1fr}.rt-container__header{position:sticky;z-index:1;top:0;display:block;grid-area:header}.rt-container__left-sidenav{display:block;padding:var(--rt-space-md);border-right:var(--rt-container-border-width) solid var(--rt-color-border-subtle);background-color:var(--rt-color-bg-surface);grid-area:left-sidenav}.rt-container__toolbar{display:flex;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);border-bottom:var(--rt-container-border-width) solid var(--rt-color-border-subtle);background-color:var(--rt-color-bg-surface);grid-area:toolbar}.rt-container__toolbar--slot--left{justify-content:flex-start}.rt-container__toolbar--slot--center{justify-content:center}.rt-container__toolbar--slot--right{justify-content:flex-end}.rt-container__content{display:flex;overflow:auto;min-height:0;flex-direction:column;grid-area:content}.rt-container--viewport{block-size:100dvh;min-block-size:0}.rt-container--viewport .rt-container__content{overflow:hidden}.rt-container--viewport .rt-container__left-sidenav{overflow:auto}@media(width<=768px){.rt-container--mobile-left-bottom{grid-template:\"header\" auto \"toolbar\" auto \"content\" 1fr \"left-sidenav\" auto/1fr}.rt-container--mobile-left-bottom .rt-container__left-sidenav{padding:0;border-top:var(--rt-container-border-width) solid var(--rt-color-border-subtle);border-right:none}}}\n"] }]
@@ -6686,7 +6907,7 @@ class RtDialogRef {
6686
6907
  }
6687
6908
  }
6688
6909
 
6689
- const BEM_BLOCK$R = 'rt-dialog';
6910
+ const BEM_BLOCK$Q = 'rt-dialog';
6690
6911
  /**
6691
6912
  * Презентационный styled-frame для модалки поверх CDK Overlay.
6692
6913
  *
@@ -6737,7 +6958,7 @@ class RtDialogComponent {
6737
6958
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtDialogComponent, decorators: [{
6738
6959
  type: Component,
6739
6960
  args: [{ selector: 'rt-dialog', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [BlockDirective, ModDirective], host: {
6740
- class: BEM_BLOCK$R,
6961
+ class: BEM_BLOCK$Q,
6741
6962
  '[style.--rt-dialog-width]': 'width()',
6742
6963
  }, template: "<div\n rtBlock=\"rt-dialog\"\n qa-dataid=\"dialog\"\n role=\"dialog\"\n aria-modal=\"true\"\n [rtMod]=\"{ size: size() }\"\n [style.--rt-dialog-width]=\"width()\"\n [attr.aria-label]=\"ariaLabel()\">\n <ng-content />\n</div>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-dialog{display:contents}:root{--rt-dialog-width-sm: 460px;--rt-dialog-width-md: 660px;--rt-dialog-width-lg: 860px;--rt-dialog-shadow: var(--rt-shadow-dialog);--rt-dialog-radius: var(--rt-radius-xl);--rt-dialog-backdrop-bg: var(--rt-overlay-navy-70);--rt-dialog-backdrop-blur: 5px}rt-dialog .rt-dialog{display:flex;overflow:hidden;width:var(--rt-dialog-width, var(--rt-dialog-width-md));max-width:90dvw;max-height:90dvh;flex-direction:column;padding:0;border:none;border-radius:var(--rt-dialog-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-dialog-shadow);color:var(--rt-color-text-primary)}rt-dialog .rt-dialog--size--sm{width:var(--rt-dialog-width, var(--rt-dialog-width-sm))}rt-dialog .rt-dialog--size--md{width:var(--rt-dialog-width, var(--rt-dialog-width-md))}rt-dialog .rt-dialog--size--lg{width:var(--rt-dialog-width, var(--rt-dialog-width-lg))}.rt-dialog-backdrop{-webkit-backdrop-filter:blur(var(--rt-dialog-backdrop-blur));backdrop-filter:blur(var(--rt-dialog-backdrop-blur));background:var(--rt-dialog-backdrop-bg)}.rt-dialog-overlay{display:flex;align-items:center;justify-content:center}}\n"] }]
6743
6964
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
@@ -6774,7 +6995,7 @@ function resolveCloseIntent(state) {
6774
6995
  return state.guarded && !state.pristine ? ERtAsideCloseIntent.Ask : ERtAsideCloseIntent.Close;
6775
6996
  }
6776
6997
 
6777
- const BEM_BLOCK$Q = 'rt-aside-unsaved-dialog';
6998
+ const BEM_BLOCK$P = 'rt-aside-unsaved-dialog';
6778
6999
  /**
6779
7000
  * Окно, которое видит пользователь, закрывая панель с несохранёнными правками. Три
6780
7001
  * исхода: закрыть без сохранения, закрыть с сохранением, остаться. Открывает его
@@ -6812,7 +7033,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
6812
7033
  BlockDirective,
6813
7034
  ElemDirective,
6814
7035
  ], host: {
6815
- class: BEM_BLOCK$Q,
7036
+ class: BEM_BLOCK$P,
6816
7037
  }, template: "<rt-dialog width=\"440px\" qa-dataid=\"aside-unsaved-dialog\" [ariaLabel]=\"t().uiUnsavedTitle\">\n <div rtBlock=\"rt-aside-unsaved-dialog\">\n <div rtElem=\"text\">\n <h2 rtElem=\"title\" qa-dataid=\"aside-unsaved-title\">{{ t().uiUnsavedTitle }}</h2>\n <p rtElem=\"message\" qa-dataid=\"aside-unsaved-message\">{{ t().uiUnsavedMessage }}</p>\n </div>\n\n <div rtElem=\"footer\">\n <button\n rtButton\n qa-dataid=\"aside-unsaved-discard\"\n type=\"button\"\n theme=\"danger\"\n appearance=\"outlined\"\n [label]=\"t().uiUnsavedDiscard\"\n [attr.aria-label]=\"t().uiUnsavedDiscard\"\n (click)=\"onDiscard()\"></button>\n <button\n rtButton\n qa-dataid=\"aside-unsaved-stay\"\n type=\"button\"\n theme=\"secondary\"\n appearance=\"text\"\n [label]=\"t().uiUnsavedStay\"\n [attr.aria-label]=\"t().uiUnsavedStay\"\n (click)=\"onStay()\"></button>\n <button\n rtButton\n qa-dataid=\"aside-unsaved-save\"\n type=\"button\"\n appearance=\"filled\"\n [label]=\"t().uiUnsavedSave\"\n [attr.aria-label]=\"t().uiUnsavedSave\"\n (click)=\"onSave()\"></button>\n </div>\n </div>\n</rt-dialog>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-aside-unsaved-dialog{display:flex;box-sizing:border-box;flex-direction:column;padding:var(--rt-space-xl);gap:var(--rt-space-lg)}.rt-aside-unsaved-dialog__text{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-aside-unsaved-dialog__title{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold)}.rt-aside-unsaved-dialog__message{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-aside-unsaved-dialog__footer{display:flex;flex-wrap:wrap;justify-content:flex-end;gap:var(--rt-space-sm)}}\n"] }]
6817
7038
  }] });
6818
7039
 
@@ -6965,6 +7186,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
6965
7186
  args: [{ providedIn: 'root' }]
6966
7187
  }], ctorParameters: () => [] });
6967
7188
 
7189
+ /**
7190
+ * Правки, запущенные панелью.
7191
+ *
7192
+ * Подписка объявлена одна на панель, а не заводится на каждый вызов: каждая правка живёт
7193
+ * независимо от соседней, и отказ одной не уносит поток остальных. Приёмы удачи и отказа
7194
+ * приезжают вместе с работой — они замыкают доводы своего вызова.
7195
+ */
7196
+ class RtRouteAsideMutations {
7197
+ #mutationSource = new Subject();
7198
+ constructor(destroyRef) {
7199
+ this.#mutationSource
7200
+ .pipe(mergeMap((mutation) => mutation.op$.pipe(map(() => mutation.handleSuccess), catchError((error) => of(() => mutation.handleError(error))))), takeUntilDestroyed(destroyRef))
7201
+ .subscribe((handleResult) => handleResult());
7202
+ }
7203
+ /** Пускает правку в объявленный поток. */
7204
+ run(mutation) {
7205
+ this.#mutationSource.next(mutation);
7206
+ }
7207
+ }
7208
+
6968
7209
  /**
6969
7210
  * Абсолютные команды приводятся к сегментам от корня: внутри объекта `outlets`
6970
7211
  * ведущий слэш отдельным сегментом не разбирается.
@@ -6985,22 +7226,137 @@ function rootSegmentsOf(commands) {
6985
7226
  }
6986
7227
 
6987
7228
  /**
6988
- * Учёт панелей, стоящих на экране: из него роутерный гард узнаёт, кого спрашивать
6989
- * о несохранённых правках.
6990
- *
6991
- * Спрашивать компонент, который роутер подставляет в гард, нельзя: когда
6992
- * ro-маршрут объявлен под уровнем без своего компонента, Angular отдаёт детям
6993
- * контекст родителя, и в гард приходит компонент первичного аутлета — экран
6994
- * раздела, а не панель.
7229
+ * Уходы панели по маршруту и разрешение на них.
6995
7230
  *
6996
- * Ключ записи — объявление маршрута, а не порядок постановки. Гард стоит на
6997
- * каждом ro-маршруте, и одна навигация зовёт его за каждый уходящий маршрут
6998
- * отдельно: учёт стеком дал бы ответ не той панели и два вопроса о правках
6999
- * подряд. Объявление маршрута — та же ссылка на объект и у панели, и у снимка,
7000
- * который роутер отдаёт гарду, поэтому два каркаса на экране не путаются даже
7001
- * при одинаково названных аутлетах: маршруты у них разные.
7002
- *
7003
- * Служба корневая: гард живёт в инжекторе маршрута и до службы, поставленной на
7231
+ * Разрешение выдаётся вплотную к вызову маршрутизатора и снимается итогом навигации:
7232
+ * между закрытием панели и уходом наложение доигрывает своё, и разрешение, выданное
7233
+ * раньше, досталось бы чужому уходу — тронутая форма уехала бы без вопроса. Читается оно
7234
+ * тоже один раз: иначе первое же закрытие после записи сняло бы вопрос о правках со всех
7235
+ * следующих уходов.
7236
+ */
7237
+ class RtRouteAsideNavigation {
7238
+ #router;
7239
+ #leaveAllowed = false;
7240
+ constructor(router) {
7241
+ this.#router = router;
7242
+ }
7243
+ /** Разрешение на этот уход. Читается один раз и снимается чтением. */
7244
+ consumeLeaveAllowance() {
7245
+ const allowed = this.#leaveAllowed;
7246
+ this.#leaveAllowed = false;
7247
+ return allowed;
7248
+ }
7249
+ /** Уход, начатый самой панелью: разрешение живёт ровно эту навигацию. */
7250
+ allowed(navigate) {
7251
+ this.#leaveAllowed = true;
7252
+ void navigate()
7253
+ .catch(() => false)
7254
+ .then(() => {
7255
+ this.#leaveAllowed = false;
7256
+ });
7257
+ }
7258
+ /**
7259
+ * Адрес, на который уходит панель. Свой аутлет она снимает тем же деревом: абсолютные
7260
+ * команды меняют только первичную ветку, и панель, открытая в корне приложения, переход
7261
+ * пережила бы — маршрута под неё в корне нет, а такую навигацию маршрутизатор отклоняет
7262
+ * молча, оставляя приложение на панели, которой на экране уже нет.
7263
+ */
7264
+ relatedTree(outlets, commands, queryParams = null) {
7265
+ return this.#router.createUrlTree([{ outlets: { ...outlets, primary: rootSegmentsOf(commands) } }], { queryParams });
7266
+ }
7267
+ }
7268
+
7269
+ /**
7270
+ * Вопрос о несохранённых правках, заданный уходу по маршруту.
7271
+ *
7272
+ * Окно открывается одно на все уходы, пришедшие пока оно висит: второе поставило бы поверх
7273
+ * первого наложение, убрать которое было бы некому. Ответ раздаётся всем спросившим, а после
7274
+ * закрытия окна следующий уход спрашивает заново.
7275
+ */
7276
+ class RtRouteAsideUnsavedPrompt {
7277
+ #dialog;
7278
+ #injector;
7279
+ #deps;
7280
+ /** Вопрос, который сейчас висит. `null` — окна нет, и следующий уход откроет своё. */
7281
+ #pendingAsk$ = null;
7282
+ constructor(dialog, injector, deps) {
7283
+ this.#dialog = dialog;
7284
+ this.#injector = injector;
7285
+ this.#deps = deps;
7286
+ }
7287
+ /** Отпускать ли уход. Пока окно висит, все спросившие получают один и тот же ответ. */
7288
+ ask() {
7289
+ const pending = this.#pendingAsk$;
7290
+ if (pending !== null) {
7291
+ return pending;
7292
+ }
7293
+ const ask$ = this.#dialog
7294
+ .open(RtAsideUnsavedDialogComponent)
7295
+ .afterClosed()
7296
+ .pipe(switchMap((outcome) => this.#deactivateAfter(outcome)), finalize(() => {
7297
+ this.#pendingAsk$ = null;
7298
+ }), shareReplay({ bufferSize: 1, refCount: false }));
7299
+ this.#pendingAsk$ = ask$;
7300
+ return ask$;
7301
+ }
7302
+ /**
7303
+ * Что делать с уходом после ответа человека. «Закрыть с сохранением» ждёт конца записи:
7304
+ * панель уступает место только удачной — иначе правки пропали бы вместе с отказом, о
7305
+ * котором человек ещё не знает.
7306
+ */
7307
+ #deactivateAfter(outcome) {
7308
+ if (outcome === ERtAsideUnsavedOutcome.Discard) {
7309
+ return of(true);
7310
+ }
7311
+ if (outcome !== ERtAsideUnsavedOutcome.Save) {
7312
+ return of(false);
7313
+ }
7314
+ this.#deps.save();
7315
+ // Запись могла и не начаться — например, черновик не годится
7316
+ if (!this.#deps.submitting()) {
7317
+ return of(false);
7318
+ }
7319
+ return toObservable(this.#deps.submitting, { injector: this.#injector }).pipe(filter((submitting) => !submitting), take(1), map(() => this.#deps.submitError() === null));
7320
+ }
7321
+ }
7322
+
7323
+ /**
7324
+ * Признак нетронутой формы.
7325
+ *
7326
+ * Контрол принимается и сигналом: форма панели собрана директивой формы и до отрисовки
7327
+ * шаблона её ещё нет, поэтому запрос вида отдаёт её только сигналом. Нынешнее состояние
7328
+ * подставляется первым значением — события прошлого подписка не получит, и без него кнопка
7329
+ * записи осталась бы недоступной у формы, испачканной до подписки.
7330
+ *
7331
+ * Зовётся в контексте внедрения: `toObservable` и `toSignal` требуют его сами.
7332
+ */
7333
+ function pristineOf(control) {
7334
+ const control$ = isSignal(control) ? toObservable(control) : of(control);
7335
+ return toSignal(control$.pipe(switchMap((current) => {
7336
+ if (current === undefined) {
7337
+ return of(true);
7338
+ }
7339
+ return current.events.pipe(filter((event) => event instanceof PristineChangeEvent), map((event) => event.pristine), startWith(current.pristine));
7340
+ })), { initialValue: true });
7341
+ }
7342
+
7343
+ /**
7344
+ * Учёт панелей, стоящих на экране: из него роутерный гард узнаёт, кого спрашивать
7345
+ * о несохранённых правках.
7346
+ *
7347
+ * Спрашивать компонент, который роутер подставляет в гард, нельзя: когда
7348
+ * ro-маршрут объявлен под уровнем без своего компонента, Angular отдаёт детям
7349
+ * контекст родителя, и в гард приходит компонент первичного аутлета — экран
7350
+ * раздела, а не панель.
7351
+ *
7352
+ * Ключ записи — объявление маршрута, а не порядок постановки. Гард стоит на
7353
+ * каждом ro-маршруте, и одна навигация зовёт его за каждый уходящий маршрут
7354
+ * отдельно: учёт стеком дал бы ответ не той панели и два вопроса о правках
7355
+ * подряд. Объявление маршрута — та же ссылка на объект и у панели, и у снимка,
7356
+ * который роутер отдаёт гарду, поэтому два каркаса на экране не путаются даже
7357
+ * при одинаково названных аутлетах: маршруты у них разные.
7358
+ *
7359
+ * Служба корневая: гард живёт в инжекторе маршрута и до службы, поставленной на
7004
7360
  * уровне компонента, не дотянулся бы вовсе. Одного учёта на приложение хватает —
7005
7361
  * записи различает маршрут. Переменной уровня модуля учёт быть не может: та
7006
7362
  * пережила бы запрос при отдаче страницы сервером и оказалась бы общей на два
@@ -7055,7 +7411,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7055
7411
  args: [{ providedIn: 'root' }]
7056
7412
  }] });
7057
7413
 
7058
- /* eslint-disable max-lines -- файл делится задачей RT-849 */
7059
7414
  /**
7060
7415
  * Открытие ждёт `panel.ready()`, а не `ngAfterViewInit`: на F5 rt-container
7061
7416
  * создаёт overlay асинхронно, иначе `open()` гонится с ещё не готовым overlay.
@@ -7074,7 +7429,7 @@ class RtRouteAsideComponent {
7074
7429
  #isCreateMode;
7075
7430
  #resolving;
7076
7431
  #submitting;
7077
- #mutationSource;
7432
+ #mutations;
7078
7433
  #refreshSource;
7079
7434
  #closeIntentSource;
7080
7435
  #unsavedGuard;
@@ -7086,19 +7441,10 @@ class RtRouteAsideComponent {
7086
7441
  #dialog;
7087
7442
  #injector;
7088
7443
  #registry;
7444
+ /** Вопрос о правках, заданный уходу по маршруту: у кнопок панели свой путь. */
7445
+ #unsavedPrompt;
7089
7446
  #opened;
7090
- /**
7091
- * Уход санкционирован самой панелью, и о правках спрашивать не надо: они либо
7092
- * записаны, либо пользователь уже решил их судьбу. Признак живёт до первого
7093
- * же вопроса о правках и снимается вместе с ответом на него.
7094
- */
7095
- #leaveAllowed;
7096
- /**
7097
- * Вопрос о правках, заданный роутерному уходу и ещё не отвеченный. Второй
7098
- * уход, пришедший пока окно открыто, ждёт того же ответа: своё окно он
7099
- * поставил бы поверх первого, и убрать его было бы некому.
7100
- */
7101
- #pendingAsk$;
7447
+ #navigation;
7102
7448
  constructor() {
7103
7449
  this.#entity = signal(null, /* @ts-ignore */
7104
7450
  ...(ngDevMode ? [{ debugName: "#entity" }] : /* istanbul ignore next */ []));
@@ -7110,7 +7456,7 @@ class RtRouteAsideComponent {
7110
7456
  ...(ngDevMode ? [{ debugName: "#resolving" }] : /* istanbul ignore next */ []));
7111
7457
  this.#submitting = signal(false, /* @ts-ignore */
7112
7458
  ...(ngDevMode ? [{ debugName: "#submitting" }] : /* istanbul ignore next */ []));
7113
- this.#mutationSource = new Subject();
7459
+ this.#mutations = new RtRouteAsideMutations(inject(DestroyRef));
7114
7460
  this.#refreshSource = new Subject();
7115
7461
  this.#closeIntentSource = new Subject();
7116
7462
  this.#unsavedGuard = signal(null, /* @ts-ignore */
@@ -7123,20 +7469,15 @@ class RtRouteAsideComponent {
7123
7469
  this.#dialog = inject(RtDialogService);
7124
7470
  this.#injector = inject(Injector);
7125
7471
  this.#registry = inject(RtRouteAsideRegistry);
7472
+ /** Вопрос о правках, заданный уходу по маршруту: у кнопок панели свой путь. */
7473
+ this.#unsavedPrompt = new RtRouteAsideUnsavedPrompt(this.#dialog, this.#injector, {
7474
+ save: () => this.#unsavedGuard()?.save(),
7475
+ submitting: this.#submitting,
7476
+ submitError: computed(() => this.submitError()),
7477
+ });
7126
7478
  this.#opened = false;
7127
- /**
7128
- * Уход санкционирован самой панелью, и о правках спрашивать не надо: они либо
7129
- * записаны, либо пользователь уже решил их судьбу. Признак живёт до первого
7130
- * же вопроса о правках и снимается вместе с ответом на него.
7131
- */
7132
- this.#leaveAllowed = false;
7133
- /**
7134
- * Вопрос о правках, заданный роутерному уходу и ещё не отвеченный. Второй
7135
- * уход, пришедший пока окно открыто, ждёт того же ответа: своё окно он
7136
- * поставил бы поверх первого, и убрать его было бы некому.
7137
- */
7138
- this.#pendingAsk$ = null;
7139
7479
  this.router = inject(Router);
7480
+ this.#navigation = new RtRouteAsideNavigation(this.router);
7140
7481
  this.route = inject(ActivatedRoute);
7141
7482
  this.destroyRef = inject(DestroyRef);
7142
7483
  this.panel = viewChild(RtContainerRightSidenavPanelDirective, /* @ts-ignore */
@@ -7161,6 +7502,15 @@ class RtRouteAsideComponent {
7161
7502
  ...(ngDevMode ? [{ debugName: "pristine" }] : /* istanbul ignore next */ []));
7162
7503
  this.submitError = signal(null, /* @ts-ignore */
7163
7504
  ...(ngDevMode ? [{ debugName: "submitError" }] : /* istanbul ignore next */ []));
7505
+ /**
7506
+ * Удача мутации — тем же способом, что и отказ. Человек, работающий в панели,
7507
+ * смотрит в панель, а не на экран за её краем: тост об успехе всплывает там,
7508
+ * куда он в эту минуту не смотрит. Оба сигнала гасятся в начале каждой
7509
+ * попытки и гасят друг друга — это ответы на один вопрос, и двух сразу не
7510
+ * бывает.
7511
+ */
7512
+ this.submitSuccess = signal(null, /* @ts-ignore */
7513
+ ...(ngDevMode ? [{ debugName: "submitSuccess" }] : /* istanbul ignore next */ []));
7164
7514
  this.idParamName = 'id';
7165
7515
  /** id-only режим: нужен только id (например, смена пароля по userId), сущность не грузится. */
7166
7516
  this.idOnly = false;
@@ -7179,13 +7529,6 @@ class RtRouteAsideComponent {
7179
7529
  // не состояться — роутер отклоняет навигацию молча, — и панель, снятая с
7180
7530
  // учёта раньше времени, осталась бы на экране, не отвечая о своих правках.
7181
7531
  this.destroyRef.onDestroy(() => this.#registry.unregister(this.route.snapshot, this));
7182
- // Мутации из runMutation(): mergeMap подписывает каждый op$ независимо
7183
- // (как раньше — по подписке на вызов, без отмены предыдущей in-flight
7184
- // мутации); handler'ы замыкают opts вызова, catchError гасит ошибку
7185
- // конкретной мутации, не убивая общий стрим.
7186
- this.#mutationSource
7187
- .pipe(mergeMap((mutation) => mutation.op$.pipe(map(() => mutation.handleSuccess), catchError((error) => of(() => mutation.handleError(error))))), takeUntilDestroyed(this.destroyRef))
7188
- .subscribe((handleResult) => handleResult());
7189
7532
  // Рефетч сущности из #refreshEntity(): mergeMap — каждый запуск resolve
7190
7533
  // живёт независимо (как раньше — отдельная подписка на вызов).
7191
7534
  this.#refreshSource
@@ -7255,34 +7598,20 @@ class RtRouteAsideComponent {
7255
7598
  }
7256
7599
  // Уход начала сама панель: правки либо записаны секунду назад, либо их
7257
7600
  // судьбу пользователь уже решил. Вопрос был бы задан про то, чего нет.
7258
- if (this.#consumeLeaveAllowance()) {
7601
+ if (this.#navigation.consumeLeaveAllowance()) {
7259
7602
  return of(true);
7260
7603
  }
7261
7604
  if (intent === ERtAsideCloseIntent.Close) {
7262
7605
  return of(true);
7263
7606
  }
7264
- return this.#askUnsaved();
7607
+ return this.#unsavedPrompt.ask();
7265
7608
  }
7266
7609
  setEntity(entity) {
7267
7610
  this.#entity.set(entity);
7268
7611
  }
7269
- /**
7270
- * Признак нетронутой формы. Контрол принимается и сигналом: форма панели
7271
- * собрана директивой `ngForm` и до отрисовки шаблона её ещё нет, поэтому
7272
- * `viewChild(NgForm)` отдаёт её только сигналом.
7273
- */
7612
+ /** Признак нетронутой формы — тот же, что гейтит кнопку записи. */
7274
7613
  pristineSignal(control) {
7275
- const control$ = isSignal(control) ? toObservable(control) : of(control);
7276
- return toSignal(control$.pipe(switchMap((current) => {
7277
- if (current === undefined) {
7278
- return of(true);
7279
- }
7280
- return current.events.pipe(filter((event) => event instanceof PristineChangeEvent), map((event) => event.pristine),
7281
- // Форма могла испачкаться до того, как сигнал отдал её: события
7282
- // прошлого подписка не получит, и без нынешнего состояния
7283
- // кнопка записи осталась бы недоступной
7284
- startWith(current.pristine));
7285
- })), { initialValue: true });
7614
+ return pristineOf(control);
7286
7615
  }
7287
7616
  /**
7288
7617
  * Ставит гард закрытия: пока форма тронута, любой из четырёх путей закрытия
@@ -7338,19 +7667,28 @@ class RtRouteAsideComponent {
7338
7667
  * `errorText` принимается и функцией: причина отказа известна только после
7339
7668
  * него — стор кладёт её в свой сигнал, а панель без стора выводит из ответа
7340
7669
  * сервера, — и готовой строкой на момент запуска мутации её не передать.
7670
+ *
7671
+ * `successMessage` — текст удачи, который панель показывает внутри себя,
7672
+ * рядом с формой. Довод отделён от `successText` намеренно: переезд текста
7673
+ * из тоста в панель сломал бы всякого потребителя, который тост и хотел, а
7674
+ * сказать «мне нужен тост» ему после этого было бы нечем. Назван он по месту
7675
+ * показа, а не по исходу: два довода с разницей в одном слоге читались бы
7676
+ * как опечатка.
7341
7677
  */
7342
7678
  runMutation(op$, opts) {
7343
7679
  this.#submitting.set(true);
7344
7680
  this.submitError.set(null);
7345
- // Подписка объявлена один раз в конструкторе (см. #mutationSource-стрим) —
7346
- // здесь только эмит операции с handler'ами, замыкающими opts.
7347
- this.#mutationSource.next({
7681
+ this.submitSuccess.set(null);
7682
+ this.#mutations.run({
7348
7683
  op$,
7349
7684
  handleSuccess: () => {
7350
7685
  this.#submitting.set(false);
7351
7686
  if (opts?.successText) {
7352
7687
  this.#notificationBus.success(opts.successText);
7353
7688
  }
7689
+ if (opts?.successMessage) {
7690
+ this.submitSuccess.set(opts.successMessage);
7691
+ }
7354
7692
  // Пользователь шёл на связанную запись и по дороге согласился
7355
7693
  // сохранить: панель уходит туда, куда он нажал.
7356
7694
  const related = this.#relatedCommands;
@@ -7362,12 +7700,12 @@ class RtRouteAsideComponent {
7362
7700
  opts.onSuccess();
7363
7701
  return;
7364
7702
  }
7365
- // Закрытие после успеха идёт мимо обоих гардов — и кнопочного, и
7366
- // роутерного. Форма к этому моменту ещё тронута, и вопрос о
7367
- // несохранённых правках был бы задан ровно про то, что только что
7368
- // сохранилось; роутерный гард закрывает разрешение на уход,
7369
- // которое панель выдаёт себе перед вызовом роутера.
7370
- if (opts?.closeOnSuccess === true || this.#isCreateMode()) {
7703
+ // Закрытие после удачи идёт мимо обоих вопросов о правках: форма ещё
7704
+ // тронута, а спрашивать про только что записанное не о чем — уходу по
7705
+ // маршруту это закрывает разрешение на уход. Названный довод решает в обе
7706
+ // стороны, а не названный считается по режиму: создавать в закрытой панели
7707
+ // нечего.
7708
+ if (opts?.closeOnSuccess ?? this.#isCreateMode()) {
7371
7709
  this.#closePanel();
7372
7710
  return;
7373
7711
  }
@@ -7377,7 +7715,10 @@ class RtRouteAsideComponent {
7377
7715
  },
7378
7716
  handleError: (error) => {
7379
7717
  this.#submitting.set(false);
7380
- // Отказ оставляет пользователю� в панели с его правками, поэтому
7718
+ // Отказ снимает прежнюю удачу: иначе «сохранено» осталось бы на
7719
+ // панели рядом со свежим отказом.
7720
+ this.submitSuccess.set(null);
7721
+ // Отказ оставляет человека в панели с его правками, поэтому
7381
7722
  // намерение уйти на связанную запись снимается.
7382
7723
  this.#relatedCommands = null;
7383
7724
  const text = typeof opts?.errorText === 'function' ? opts.errorText(error) : opts?.errorText;
@@ -7428,72 +7769,6 @@ class RtRouteAsideComponent {
7428
7769
  pristine: this.pristine(),
7429
7770
  });
7430
7771
  }
7431
- /**
7432
- * Что делать с уходом панели после ответа пользователю�. «Закрыть с сохранением»
7433
- * ждёт конца записи: панель уступает место только удачной — иначе правки
7434
- * пропали бы вместе с отказом, о котором пользователь ещё не знает.
7435
- */
7436
- #deactivateAfter(outcome) {
7437
- if (outcome === ERtAsideUnsavedOutcome.Discard) {
7438
- return of(true);
7439
- }
7440
- if (outcome !== ERtAsideUnsavedOutcome.Save) {
7441
- return of(false);
7442
- }
7443
- this.#unsavedGuard()?.save();
7444
- // Запись могла и не начаться — например, черновик не годится
7445
- if (!this.#submitting()) {
7446
- return of(false);
7447
- }
7448
- return toObservable(this.#submitting, { injector: this.#injector }).pipe(filter((submitting) => !submitting), take(1), map(() => this.submitError() === null));
7449
- }
7450
- /**
7451
- * Вопрос о правках роутерному уходу. Окно открывается одно на все уходы,
7452
- * пришедшие пока оно висит: второе поставило бы поверх первого оверлей,
7453
- * убрать который было бы некому — от того же и `exhaustMap` на пути кнопок.
7454
- */
7455
- #askUnsaved() {
7456
- const pending = this.#pendingAsk$;
7457
- if (pending !== null) {
7458
- return pending;
7459
- }
7460
- const ask$ = this.#dialog
7461
- .open(RtAsideUnsavedDialogComponent)
7462
- .afterClosed()
7463
- .pipe(switchMap((outcome) => this.#deactivateAfter(outcome)), finalize(() => {
7464
- this.#pendingAsk$ = null;
7465
- }), shareReplay({ bufferSize: 1, refCount: false }));
7466
- this.#pendingAsk$ = ask$;
7467
- return ask$;
7468
- }
7469
- /**
7470
- * Разрешение на уход читается один раз: иначе первое же закрытие после записи
7471
- * сняло бы вопрос о правках со всех следующих уходов, и тронутая форма
7472
- * уезжала бы молча.
7473
- */
7474
- #consumeLeaveAllowance() {
7475
- const allowed = this.#leaveAllowed;
7476
- this.#leaveAllowed = false;
7477
- return allowed;
7478
- }
7479
- /**
7480
- * Уход, начатый самой панелью. Разрешение выдаётся здесь, вплотную к вызову
7481
- * роутера, а не при закрытии панели: между закрытием и навигацией оверлей
7482
- * доигрывает уход, и разрешение, выданное раньше, достаётся чужому уходу,
7483
- * пришедшему в это окно, — тронутая форма уехала бы без вопроса.
7484
- *
7485
- * Невостребованное разрешение снимается итогом навигации: роутер отклоняет
7486
- * навигацию молча, её может отменить другой гард, и оставшееся разрешение
7487
- * сняло бы вопрос о правках со следующего ухода, которого панель не начинала.
7488
- */
7489
- #navigateAllowed(navigate) {
7490
- this.#leaveAllowed = true;
7491
- void navigate()
7492
- .catch(() => false)
7493
- .then(() => {
7494
- this.#leaveAllowed = false;
7495
- });
7496
- }
7497
7772
  #closePanel() {
7498
7773
  this.panel()?.close();
7499
7774
  }
@@ -7501,18 +7776,10 @@ class RtRouteAsideComponent {
7501
7776
  const params = queryParams ?? this.#relatedQueryParams;
7502
7777
  this.#relatedCommands = null;
7503
7778
  this.#relatedQueryParams = null;
7504
- this.#navigateAllowed(() => this.router.navigateByUrl(this.#relatedTree(commands, params)));
7779
+ this.#navigation.allowed(() => this.router.navigateByUrl(this.#relatedTree(commands, params)));
7505
7780
  }
7506
- /**
7507
- * Адрес, на который уходит панель. Свой аутлет она снимает здесь же, тем же
7508
- * деревом: абсолютные команды меняют только первичную ветку, поэтому панель,
7509
- * открытая в корне приложения, переход переживает. Маршрута под неё в корне
7510
- * нет, и роутер отклоняет такую навигацию молча — адрес остаётся прежним, а
7511
- * приложение застревает на панели, которой на экране уже нет.
7512
- */
7513
7781
  #relatedTree(commands, queryParams = null) {
7514
- const outlets = { ...this.closeOutlets(), primary: rootSegmentsOf(commands) };
7515
- return this.router.createUrlTree([{ outlets }], { queryParams });
7782
+ return this.#navigation.relatedTree(this.closeOutlets(), commands, queryParams);
7516
7783
  }
7517
7784
  #applyUnsavedOutcome(outcome) {
7518
7785
  const related = this.#relatedCommands;
@@ -7525,7 +7792,7 @@ class RtRouteAsideComponent {
7525
7792
  return;
7526
7793
  }
7527
7794
  // Запись закрывает панель сама на успехе — иначе панель ушла бы с экрана
7528
- // раньше ответа, и отказ пользователю� было бы негде показать. Уход на
7795
+ // раньше ответа, и отказ негде было бы показать. Уход на
7529
7796
  // связанную запись ждёт того же успеха.
7530
7797
  if (outcome === ERtAsideUnsavedOutcome.Save) {
7531
7798
  this.#unsavedGuard()?.save();
@@ -7549,7 +7816,7 @@ class RtRouteAsideComponent {
7549
7816
  * `openRelated` явно, и на них перенос не действует.
7550
7817
  */
7551
7818
  #navigateAway() {
7552
- this.#navigateAllowed(() => this.router.navigate([{ outlets: this.closeOutlets() }], {
7819
+ this.#navigation.allowed(() => this.router.navigate([{ outlets: this.closeOutlets() }], {
7553
7820
  relativeTo: this.route.parent,
7554
7821
  queryParamsHandling: 'preserve',
7555
7822
  }));
@@ -7625,7 +7892,7 @@ const rtAsideUnsavedGuard = (component, currentRoute) => {
7625
7892
  const RT_COUNTER_DECREASE_KEY = 'counterDecrease';
7626
7893
  const RT_COUNTER_INCREASE_KEY = 'counterIncrease';
7627
7894
 
7628
- const BEM_BLOCK$P = 'rt-counter';
7895
+ const BEM_BLOCK$O = 'rt-counter';
7629
7896
  /** Значение всегда внутри границ: и при записи из формы, и при шаге кнопкой. */
7630
7897
  function clamp$1(value, min, max) {
7631
7898
  return Math.min(Math.max(value, min), max);
@@ -7751,13 +8018,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7751
8018
  multi: true,
7752
8019
  },
7753
8020
  ], host: {
7754
- class: BEM_BLOCK$P,
8021
+ class: BEM_BLOCK$O,
7755
8022
  role: 'group',
7756
8023
  '[attr.aria-label]': 'ariaLabel()',
7757
8024
  }, template: "<ng-container rtBlock=\"rt-counter\">\n <rt-icon-button\n qa-dataid=\"counter-decrease\"\n icon=\"minus\"\n variant=\"secondary\"\n size=\"sm\"\n [ariaLabel]=\"decreaseAriaLabel()\"\n [disabled]=\"!canDecrease()\"\n (clicked)=\"decrease()\" />\n <span rtElem=\"value\" qa-dataid=\"counter-value\" aria-live=\"polite\">{{ value() }}</span>\n <rt-icon-button\n qa-dataid=\"counter-increase\"\n icon=\"ico-plus\"\n variant=\"secondary\"\n size=\"sm\"\n [ariaLabel]=\"increaseAriaLabel()\"\n [disabled]=\"!canIncrease()\"\n (clicked)=\"increase()\" />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-counter{display:inline-flex;align-items:center;gap:var(--rt-counter-gap)}.rt-counter__value{min-width:var(--rt-counter-value-min-width);color:var(--rt-counter-value-color);font-size:var(--rt-counter-value-font-size);font-variant-numeric:tabular-nums;text-align:center}}\n"] }]
7758
8025
  }], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], decreaseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "decreaseLabel", required: false }] }], increaseLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "increaseLabel", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
7759
8026
 
7760
- const BEM_BLOCK$O = 'rt-counter-row';
8027
+ const BEM_BLOCK$N = 'rt-counter-row';
7761
8028
  /**
7762
8029
  * Строка списка со счётчиком: подпись с пояснением слева, контрол справа,
7763
8030
  * волосяная линия между соседними строками.
@@ -7788,11 +8055,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7788
8055
  BlockDirective,
7789
8056
  ElemDirective,
7790
8057
  ], host: {
7791
- class: BEM_BLOCK$O,
8058
+ class: BEM_BLOCK$N,
7792
8059
  }, template: "<ng-container rtBlock=\"rt-counter-row\">\n <span rtElem=\"text\">\n <span rtElem=\"label\" qa-dataid=\"counter-row-label\">{{ label() }}</span>\n @if (hint(); as hintText) {\n <span rtElem=\"hint\" qa-dataid=\"counter-row-hint\">{{ hintText }}</span>\n }\n </span>\n <ng-content />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-counter-row{--rt-counter-row-border-width: var(--rt-border-width-thin);display:flex;align-items:center;justify-content:space-between;padding:var(--rt-counter-row-padding-y) 0;gap:var(--rt-counter-row-gap)}.rt-counter-row:not(:first-of-type){border-top:var(--rt-counter-row-border-width) solid var(--rt-counter-row-color-border)}.rt-counter-row__text{display:flex;flex-direction:column;gap:var(--rt-space-2xs);text-align:start}.rt-counter-row__label{color:var(--rt-counter-row-label-color);font-size:var(--rt-counter-row-label-font-size);font-weight:var(--rt-font-weight-medium)}.rt-counter-row__hint{color:var(--rt-counter-row-hint-color);font-size:var(--rt-counter-row-hint-font-size)}}\n"] }]
7793
8060
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }] } });
7794
8061
 
7795
- const BEM_BLOCK$N = 'rt-date-picker';
8062
+ const BEM_BLOCK$M = 'rt-date-picker';
7796
8063
  /**
7797
8064
  * Date/time picker на нативном `<input type="date"|"datetime-local"|"time">`,
7798
8065
  * визуально и по токенам идентичный rt-input (host — box `--rt-input-*`, input —
@@ -7880,7 +8147,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7880
8147
  // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.
7881
8148
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtDatePickerComponent) },
7882
8149
  ], host: {
7883
- class: BEM_BLOCK$N,
8150
+ class: BEM_BLOCK$M,
7884
8151
  '[class.rt-date-picker--disabled]': 'isDisabled()',
7885
8152
  '[class.rt-date-picker--invalid]': 'isInvalid()',
7886
8153
  '[class.rt-date-picker--readonly]': 'isReadonly()',
@@ -7890,7 +8157,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7890
8157
  }, template: "<ng-container rtBlock=\"rt-date-picker\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"date-picker-readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '\u2014' }}</span>\n } @else {\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"date-picker-control\"\n [type]=\"type()\"\n [value]=\"value()\"\n [disabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.min]=\"min()\"\n [attr.max]=\"max()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"date-picker-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-date-picker{position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-date-picker:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-date-picker--borderless{border-color:transparent}.rt-date-picker--invalid{border-color:var(--rt-input-color-border-error)}.rt-date-picker--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-date-picker--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-date-picker--size--sm{min-height:var(--rt-input-height-sm)}.rt-date-picker--size--lg{min-height:var(--rt-input-height-lg)}.rt-date-picker--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-date-picker__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-date-picker__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-date-picker__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;outline:none}.rt-date-picker__field:disabled{cursor:not-allowed}.rt-date-picker__clear{flex-shrink:0}}\n"] }]
7891
8158
  }], propDecorators: { fieldEl: [{ type: i0.ViewChild, args: ['fieldEl', { isSignal: true }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }] } });
7892
8159
 
7893
- const BEM_BLOCK$M = 'rt-detail-list';
8160
+ const BEM_BLOCK$L = 'rt-detail-list';
7894
8161
  /**
7895
8162
  * Контейнер для стопки `rt-detail-row` — задаёт единый вертикальный gap между
7896
8163
  * строками «лейбл + значение». Используется в read-only details-панелях асайдов
@@ -7910,11 +8177,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7910
8177
  // standalone components / directives
7911
8178
  BlockDirective,
7912
8179
  ], host: {
7913
- class: BEM_BLOCK$M,
8180
+ class: BEM_BLOCK$L,
7914
8181
  }, template: "<ng-container rtBlock=\"rt-detail-list\">\n <ng-content />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-detail-list{display:flex;flex-direction:column;gap:var(--rt-space-md)}}\n"] }]
7915
8182
  }] });
7916
8183
 
7917
- const BEM_BLOCK$L = 'rt-detail-row';
8184
+ const BEM_BLOCK$K = 'rt-detail-row';
7918
8185
  /**
7919
8186
  * Одна read-only строка «лейбл + значение» в stacked-раскладке: приглушённый
7920
8187
  * лейбл сверху, значение снизу. Единый паттерн info-строки для всех асайдов
@@ -7943,11 +8210,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7943
8210
  BlockDirective,
7944
8211
  ElemDirective,
7945
8212
  ], host: {
7946
- class: BEM_BLOCK$L,
8213
+ class: BEM_BLOCK$K,
7947
8214
  }, template: "<ng-container rtBlock=\"rt-detail-row\">\n <span rtElem=\"label\" qa-dataid=\"detail-row-label\">{{ label() }}</span>\n <div rtElem=\"value\" qa-dataid=\"detail-row-value\">\n <rt-skeleton-wrapper width=\"180px\" height=\"16px\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-detail-row{display:flex;flex-direction:column;gap:var(--rt-field-gap)}.rt-detail-row__label{color:var(--rt-field-label-color);font-size:var(--rt-field-label-font-size);font-weight:var(--rt-field-label-font-weight)}.rt-detail-row__value{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}}\n"] }]
7948
8215
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
7949
8216
 
7950
- const BEM_BLOCK$K = 'rt-dialog-header';
8217
+ const BEM_BLOCK$J = 'rt-dialog-header';
7951
8218
  /**
7952
8219
  * Заголовок модалки для композиции внутри `<rt-dialog>`.
7953
8220
  *
@@ -7998,11 +8265,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
7998
8265
  BlockDirective,
7999
8266
  ElemDirective,
8000
8267
  ], host: {
8001
- class: BEM_BLOCK$K,
8268
+ class: BEM_BLOCK$J,
8002
8269
  }, template: "<header rtBlock=\"rt-dialog-header\" qa-dataid=\"dialog-header\">\n <h2 rtElem=\"title\" qa-dataid=\"dialog-title\">{{ title() }}</h2>\n @if (closable()) {\n <rt-icon-button\n rtElem=\"close\"\n qa-dataid=\"dialog-close\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n [ariaLabel]=\"t().uiClose\"\n (clicked)=\"onClose()\" />\n }\n</header>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-dialog-header{display:contents}rt-dialog-header .rt-dialog-header{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-lg);border-block-end:1px solid var(--rt-color-border-subtle);gap:var(--rt-space-sm)}rt-dialog-header .rt-dialog-header__title{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xl)}rt-dialog-header .rt-dialog-header__close{margin-inline-start:auto}}\n"] }]
8003
8270
  }], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], closable: [{ type: i0.Input, args: [{ isSignal: true, alias: "closable", required: false }] }] } });
8004
8271
 
8005
- const BEM_BLOCK$J = 'rt-dialog-footer';
8272
+ const BEM_BLOCK$I = 'rt-dialog-footer';
8006
8273
  /**
8007
8274
  * Footer-слот для композиции внутри `<rt-dialog>`. Принимает любой проектированный
8008
8275
  * контент (action-кнопки, статусные сообщения и т.п.), оборачивает в footer с
@@ -8025,11 +8292,11 @@ class RtDialogFooterComponent {
8025
8292
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtDialogFooterComponent, decorators: [{
8026
8293
  type: Component,
8027
8294
  args: [{ selector: 'rt-dialog-footer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [BlockDirective], host: {
8028
- class: BEM_BLOCK$J,
8295
+ class: BEM_BLOCK$I,
8029
8296
  }, template: "<footer rtBlock=\"rt-dialog-footer\" qa-dataid=\"dialog-footer\">\n <ng-content />\n</footer>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-dialog-footer{display:contents}rt-dialog-footer .rt-dialog-footer{display:flex;justify-content:flex-end;padding:var(--rt-space-lg);border-block-start:1px solid var(--rt-color-border-subtle);gap:var(--rt-space-sm)}rt-dialog-footer .rt-dialog-footer:empty{display:none}}\n"] }]
8030
8297
  }] });
8031
8298
 
8032
- const BEM_BLOCK$I = 'rt-download-link';
8299
+ const BEM_BLOCK$H = 'rt-download-link';
8033
8300
  class RtDownloadLinkComponent {
8034
8301
  constructor() {
8035
8302
  /**
@@ -8057,11 +8324,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8057
8324
  BlockDirective,
8058
8325
  ElemDirective,
8059
8326
  ], host: {
8060
- class: BEM_BLOCK$I,
8327
+ class: BEM_BLOCK$H,
8061
8328
  }, template: "<ng-container rtBlock=\"rt-download-link\">\n <button rtElem=\"button\" qa-dataid=\"download-link-button\" type=\"button\" [attr.aria-label]=\"downloadAriaLabel()\" (click)=\"onClick()\">\n <rt-icon rtElem=\"icon\" name=\"ico-download\" size=\"md\" />\n <span rtElem=\"label\" qa-dataid=\"download-link-label\">{{ label() }}</span>\n </button>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-download-link{--rt-download-link-focus-radius: var(--rt-radius-sm);--rt-download-link-focus-shadow: var(--rt-shadow-focus-ring);display:inline-flex}.rt-download-link__button{display:inline-flex;align-items:center;padding:0;border:0;background:transparent;color:var(--rt-color-text-link);cursor:pointer;font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-medium);gap:var(--rt-space-sm);line-height:var(--rt-leading-snug)}.rt-download-link__button:hover{text-decoration:underline}.rt-download-link__button:focus-visible{border-radius:var(--rt-download-link-focus-radius);box-shadow:var(--rt-download-link-focus-shadow);outline:none}.rt-download-link__icon{color:var(--rt-color-text-link)}}\n"] }]
8062
8329
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], downloadClick: [{ type: i0.Output, args: ["downloadClick"] }] } });
8063
8330
 
8064
- const BEM_BLOCK$H = 'rt-empty-state';
8331
+ const BEM_BLOCK$G = 'rt-empty-state';
8065
8332
  /**
8066
8333
  * Заглушка пустого состояния: центрированная иконка в круглой подложке, заголовок,
8067
8334
  * опциональное описание и проецируемый слот для действия (кнопка «Прикрепить»,
@@ -8094,7 +8361,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8094
8361
  BlockDirective,
8095
8362
  ElemDirective,
8096
8363
  ], host: {
8097
- class: BEM_BLOCK$H,
8364
+ class: BEM_BLOCK$G,
8098
8365
  }, template: "<ng-container rtBlock=\"rt-empty-state\">\n <div rtElem=\"illustration\">\n <ng-content select=\"[rtEmptyIllustration]\" />\n </div>\n\n @if (icon(); as iconName) {\n <span rtElem=\"icon-wrap\" qa-dataid=\"empty-state-icon\">\n <rt-icon size=\"xl\" color=\"muted\" [name]=\"iconName\" />\n </span>\n }\n\n @if (title()) {\n <p rtElem=\"title\" qa-dataid=\"empty-state-title\">{{ title() }}</p>\n }\n\n @if (description(); as desc) {\n <p rtElem=\"description\" qa-dataid=\"empty-state-description\">{{ desc }}</p>\n }\n\n <div rtElem=\"action\" qa-dataid=\"empty-state-action\">\n <ng-content />\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-empty-state{--rt-empty-state-icon-radius: var(--rt-radius-full);display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--rt-space-xl) var(--rt-space-lg);gap:var(--rt-space-sm);text-align:center}.rt-empty-state__illustration:empty{display:none}.rt-empty-state__illustration{margin-bottom:var(--rt-space-xs)}.rt-empty-state__icon-wrap{display:flex;width:64px;height:64px;align-items:center;justify-content:center;border-radius:var(--rt-empty-state-icon-radius);margin-bottom:var(--rt-space-xs);background:var(--rt-color-bg-subtle);color:var(--rt-color-text-muted)}.rt-empty-state__title{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-medium)}.rt-empty-state__description{max-width:320px;margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);line-height:var(--rt-leading-normal)}.rt-empty-state__action:empty{display:none}.rt-empty-state__action{margin-top:var(--rt-space-sm)}}\n"] }]
8099
8366
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }] } });
8100
8367
 
@@ -8135,7 +8402,7 @@ const RT_FIELD_DEFAULT_ERROR_KEYS = {
8135
8402
  /** Текст-плейсхолдер read-only поля без значения. */
8136
8403
  const RT_FIELD_EMPTY_VALUE = '—';
8137
8404
 
8138
- const BEM_BLOCK$G = 'rt-field';
8405
+ const BEM_BLOCK$F = 'rt-field';
8139
8406
  /** Счётчик для авто-id (связка label[for] ↔ controlId спроецированного контрола). */
8140
8407
  let uidSeed = 0;
8141
8408
  function nextAutoId() {
@@ -8253,10 +8520,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8253
8520
  BlockDirective,
8254
8521
  ElemDirective,
8255
8522
  ModDirective,
8256
- ], host: { class: BEM_BLOCK$G }, template: "<ng-container rtBlock=\"rt-field\">\n @if (label()) {\n <span rtElem=\"label-row\">\n <label rtElem=\"label\" qa-dataid=\"field-label\" [attr.for]=\"controlId()\">\n {{ label() }}\n @if (isRequired() && !hideRequiredMark()) {\n <span rtElem=\"required\" qa-dataid=\"field-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n @if (help()) {\n <button\n rtElem=\"help\"\n qa-dataid=\"field-help\"\n type=\"button\"\n rtPopoverTrigger=\"hover\"\n [attr.aria-label]=\"t().uiHint\"\n [rtPopover]=\"helpTpl\">\n <rt-icon name=\"ico-help\" size=\"sm\" color=\"muted\" />\n </button>\n <ng-template #helpTpl>\n <span rtElem=\"help-popover\" qa-dataid=\"field-help-popover\">{{ help() }}</span>\n </ng-template>\n }\n </span>\n }\n\n <rt-skeleton-wrapper rtElem=\"control\" height=\"var(--rt-input-height)\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n\n <!-- \u0421 \u0440\u0435\u0437\u0435\u0440\u0432\u043E\u043C hint \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043A\u0435 \u0434\u0430\u0436\u0435 \u043F\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 (\u0441\u043A\u0440\u044B\u0442\u044B\u0439), \u0430 \u0441\u0430\u043C\u0430\n \u043E\u0448\u0438\u0431\u043A\u0430 \u043A\u043B\u0430\u0434\u0451\u0442\u0441\u044F \u0432 \u0442\u0443 \u0436\u0435 \u044F\u0447\u0435\u0439\u043A\u0443 \u0433\u0440\u0438\u0434\u0430: \u0432\u044B\u0441\u043E\u0442\u0430 \u0437\u043E\u043D\u044B \u0440\u0430\u0432\u043D\u0430 \u0431\u043E\u043B\u044C\u0448\u0435\u043C\u0443 \u0438\u0437\n \u0434\u0432\u0443\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0438 \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0438\u0445 \u043F\u043E\u0434\u043C\u0435\u043D\u0435. \u0411\u0435\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u0430 \u2014 \u043A\u0430\u043A \u0431\u044B\u043B\u043E,\n \u043E\u0434\u043D\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0437\u0430 \u0440\u0430\u0437, \u0447\u0442\u043E\u0431\u044B \u043F\u043B\u043E\u0442\u043D\u044B\u0435 \u0444\u043E\u0440\u043C\u044B \u043D\u0435 \u043D\u0430\u0431\u0438\u0440\u0430\u043B\u0438 \u043B\u0438\u0448\u043D\u0438\u0439 \u0432\u043E\u0437\u0434\u0443\u0445. -->\n <span rtElem=\"messages\" [rtMod]=\"{ reserved: reserveHintSpace() }\">\n @if (projectedHint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n <ng-content select=\"[rtFieldHint]\" />\n </small>\n } @else if (hint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n {{ hint() }}\n </small>\n }\n\n @if (showError()) {\n <small rtElem=\"error\" qa-dataid=\"field-error\">{{ errorText() }}</small>\n }\n </span>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-field{display:flex;flex-direction:column;gap:var(--rt-field-gap)}.rt-field__label-row{display:inline-flex;align-items:center;gap:var(--rt-field-label-help-gap)}.rt-field__label{color:var(--rt-field-label-color);font-size:var(--rt-field-label-font-size);font-weight:var(--rt-field-label-font-weight)}.rt-field__required{color:var(--rt-field-required-color)}.rt-field__control{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-field__help{display:inline-flex;padding:0;border:none;margin:0;background:none;cursor:help}.rt-field__help-popover{display:block;max-width:240px;font-size:var(--rt-field-hint-font-size)}.rt-field__messages{display:grid}.rt-field__messages--reserved{font-size:var(--rt-field-error-font-size);min-block-size:1lh}.rt-field__messages--reserved>*{grid-area:1/1}.rt-field__hint{color:var(--rt-field-hint-color);font-size:var(--rt-field-hint-font-size)}.rt-field__hint--hidden{visibility:hidden}.rt-field__error{color:var(--rt-field-error-color);font-size:var(--rt-field-error-font-size)}}\n"] }]
8523
+ ], host: { class: BEM_BLOCK$F }, template: "<ng-container rtBlock=\"rt-field\">\n @if (label()) {\n <span rtElem=\"label-row\">\n <label rtElem=\"label\" qa-dataid=\"field-label\" [attr.for]=\"controlId()\">\n {{ label() }}\n @if (isRequired() && !hideRequiredMark()) {\n <span rtElem=\"required\" qa-dataid=\"field-required\" aria-hidden=\"true\">*</span>\n }\n </label>\n @if (help()) {\n <button\n rtElem=\"help\"\n qa-dataid=\"field-help\"\n type=\"button\"\n rtPopoverTrigger=\"hover\"\n [attr.aria-label]=\"t().uiHint\"\n [rtPopover]=\"helpTpl\">\n <rt-icon name=\"ico-help\" size=\"sm\" color=\"muted\" />\n </button>\n <ng-template #helpTpl>\n <span rtElem=\"help-popover\" qa-dataid=\"field-help-popover\">{{ help() }}</span>\n </ng-template>\n }\n </span>\n }\n\n <rt-skeleton-wrapper rtElem=\"control\" height=\"var(--rt-input-height)\" [isLoading]=\"loading()\">\n <ng-content />\n </rt-skeleton-wrapper>\n\n <!-- \u0421 \u0440\u0435\u0437\u0435\u0440\u0432\u043E\u043C hint \u043E\u0441\u0442\u0430\u0451\u0442\u0441\u044F \u0432 \u043F\u043E\u0442\u043E\u043A\u0435 \u0434\u0430\u0436\u0435 \u043F\u043E\u0434 \u043E\u0448\u0438\u0431\u043A\u043E\u0439 (\u0441\u043A\u0440\u044B\u0442\u044B\u0439), \u0430 \u0441\u0430\u043C\u0430\n \u043E\u0448\u0438\u0431\u043A\u0430 \u043A\u043B\u0430\u0434\u0451\u0442\u0441\u044F \u0432 \u0442\u0443 \u0436\u0435 \u044F\u0447\u0435\u0439\u043A\u0443 \u0433\u0440\u0438\u0434\u0430: \u0432\u044B\u0441\u043E\u0442\u0430 \u0437\u043E\u043D\u044B \u0440\u0430\u0432\u043D\u0430 \u0431\u043E\u043B\u044C\u0448\u0435\u043C\u0443 \u0438\u0437\n \u0434\u0432\u0443\u0445 \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0439 \u0438 \u043D\u0435 \u043C\u0435\u043D\u044F\u0435\u0442\u0441\u044F \u043F\u0440\u0438 \u0438\u0445 \u043F\u043E\u0434\u043C\u0435\u043D\u0435. \u0411\u0435\u0437 \u0440\u0435\u0437\u0435\u0440\u0432\u0430 \u2014 \u043A\u0430\u043A \u0431\u044B\u043B\u043E,\n \u043E\u0434\u043D\u043E \u0441\u043E\u043E\u0431\u0449\u0435\u043D\u0438\u0435 \u0437\u0430 \u0440\u0430\u0437, \u0447\u0442\u043E\u0431\u044B \u043F\u043B\u043E\u0442\u043D\u044B\u0435 \u0444\u043E\u0440\u043C\u044B \u043D\u0435 \u043D\u0430\u0431\u0438\u0440\u0430\u043B\u0438 \u043B\u0438\u0448\u043D\u0438\u0439 \u0432\u043E\u0437\u0434\u0443\u0445. -->\n <span rtElem=\"messages\" [rtMod]=\"{ reserved: reserveHintSpace() }\">\n @if (projectedHint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n <ng-content select=\"[rtFieldHint]\" />\n </small>\n } @else if (hint() && (reserveHintSpace() || !showError())) {\n <small rtElem=\"hint\" qa-dataid=\"field-hint\" [rtMod]=\"{ hidden: showError() }\" [attr.aria-hidden]=\"showError() ? 'true' : null\">\n {{ hint() }}\n </small>\n }\n\n @if (showError()) {\n <small rtElem=\"error\" qa-dataid=\"field-error\">{{ errorText() }}</small>\n }\n </span>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-field{display:flex;flex-direction:column;gap:var(--rt-field-gap)}.rt-field__label-row{display:inline-flex;align-items:center;gap:var(--rt-field-label-help-gap)}.rt-field__label{color:var(--rt-field-label-color);font-size:var(--rt-field-label-font-size);font-weight:var(--rt-field-label-font-weight)}.rt-field__required{color:var(--rt-field-required-color)}.rt-field__control{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-field__help{display:inline-flex;padding:0;border:none;margin:0;background:none;cursor:help}.rt-field__help-popover{display:block;max-width:240px;font-size:var(--rt-field-hint-font-size)}.rt-field__messages{display:grid}.rt-field__messages--reserved{font-size:var(--rt-field-error-font-size);min-block-size:1lh}.rt-field__messages--reserved>*{grid-area:1/1}.rt-field__hint{color:var(--rt-field-hint-color);font-size:var(--rt-field-hint-font-size)}.rt-field__hint--hidden{visibility:hidden}.rt-field__error{color:var(--rt-field-error-color);font-size:var(--rt-field-error-font-size)}}\n"] }]
8257
8524
  }], ctorParameters: () => [], propDecorators: { projectedControl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtFormControlBase), { isSignal: true }] }], projectedHint: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtFieldHintDirective), { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], help: [{ type: i0.Input, args: [{ isSignal: true, alias: "help", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], hideRequiredMark: [{ type: i0.Input, args: [{ isSignal: true, alias: "hideRequiredMark", required: false }] }], reserveHintSpace: [{ type: i0.Input, args: [{ isSignal: true, alias: "reserveHintSpace", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }] } });
8258
8525
 
8259
- const BEM_BLOCK$F = 'rt-file-list';
8526
+ const BEM_BLOCK$E = 'rt-file-list';
8260
8527
  /**
8261
8528
  * Вертикальный контейнер для карточек файлов (`rt-file-card`): задаёт отступы между
8262
8529
  * карточками и растягивает их по ширине. Чисто презентационный — содержимое
@@ -8274,11 +8541,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8274
8541
  // standalone components / directives
8275
8542
  BlockDirective,
8276
8543
  ], host: {
8277
- class: BEM_BLOCK$F,
8544
+ class: BEM_BLOCK$E,
8278
8545
  }, template: "<ng-container rtBlock=\"rt-file-list\">\n <ng-content />\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-file-list{display:flex;width:100%;flex-direction:column;gap:var(--rt-space-sm)}}\n"] }]
8279
8546
  }] });
8280
8547
 
8281
- const BEM_BLOCK$E = 'rt-file-input';
8548
+ const BEM_BLOCK$D = 'rt-file-input';
8282
8549
  /**
8283
8550
  * Поле выбора файлов: кнопка «выбрать» + список выбранных файлов карточками
8284
8551
  * `rt-file-card` (иконка типа, имя, размер, удаление). Скрывает нативный
@@ -8373,14 +8640,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8373
8640
  // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.
8374
8641
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtFileInputComponent) },
8375
8642
  ], host: {
8376
- class: BEM_BLOCK$E,
8643
+ class: BEM_BLOCK$D,
8377
8644
  '[class.rt-file-input--disabled]': 'isDisabled()',
8378
8645
  '[class.rt-file-input--invalid]': 'isInvalid()',
8379
8646
  '[class.rt-file-input--readonly]': 'isReadonly()',
8380
8647
  }, template: "<ng-container rtBlock=\"rt-file-input\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"file-input-readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '\u2014' }}</span>\n } @else {\n <input\n #fileEl\n qa-dataid=\"file-input-native\"\n type=\"file\"\n hidden\n [multiple]=\"multiple()\"\n [disabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.accept]=\"accept()\"\n [attr.webkitdirectory]=\"directory() ? '' : null\"\n (change)=\"onFiles($event)\" />\n\n <button\n rtElem=\"trigger\"\n qa-dataid=\"file-input-trigger\"\n type=\"button\"\n rtButton\n appearance=\"outlined\"\n icon=\"ico-upload\"\n [label]=\"buttonText()\"\n [attr.aria-label]=\"buttonText()\"\n [disabled]=\"isDisabled()\"\n (click)=\"openPicker()\"></button>\n\n @if (value().length > 0) {\n <rt-file-list rtElem=\"list\" qa-dataid=\"file-input-list\">\n @for (file of value(); track $index) {\n <rt-file-card\n showRemove\n qa-dataid=\"file-input-file\"\n [name]=\"file.name\"\n [sizeBytes]=\"file.size\"\n [disabled]=\"isDisabled()\"\n (removed)=\"removeFile($index)\" />\n }\n </rt-file-list>\n }\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-file-input{display:flex;flex-direction:column;align-items:flex-start;gap:var(--rt-space-sm)}.rt-file-input--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-file-input__list{width:100%}.rt-file-input__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-file-input__readonly--empty{color:var(--rt-field-value-empty-color)}}\n"] }]
8381
8648
  }], propDecorators: { fileEl: [{ type: i0.ViewChild, args: ['fileEl', { isSignal: true }] }], multiple: [{ type: i0.Input, args: [{ isSignal: true, alias: "multiple", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], directory: [{ type: i0.Input, args: [{ isSignal: true, alias: "directory", required: false }] }], buttonLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "buttonLabel", required: false }] }] } });
8382
8649
 
8383
- const BEM_BLOCK$D = 'rt-input';
8650
+ const BEM_BLOCK$C = 'rt-input';
8384
8651
  /**
8385
8652
  * Шеренный input common/ui. Поддерживает:
8386
8653
  * - интеграцию с Reactive Forms через общий `RtFormControlBase` (CVA-ядро,
@@ -8490,7 +8757,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8490
8757
  // contentChild(RtFormControlBase) (query по абстрактному классу).
8491
8758
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputComponent) },
8492
8759
  ], host: {
8493
- class: BEM_BLOCK$D,
8760
+ class: BEM_BLOCK$C,
8494
8761
  '[class.rt-input--disabled]': 'isDisabled()',
8495
8762
  '[class.rt-input--invalid]': 'isInvalid()',
8496
8763
  '[class.rt-input--readonly]': 'isReadonly()',
@@ -8503,7 +8770,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8503
8770
  }, template: "<ng-container rtBlock=\"rt-input\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '\u2014' }}</span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-control\"\n [type]=\"effectiveType()\"\n [placeholder]=\"placeholder()\"\n [value]=\"value()\"\n [disabled]=\"isDisabled()\"\n [attr.autocomplete]=\"autocomplete()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton() && !showPasswordToggle()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n @if (showPasswordToggle()) {\n <button\n rtElem=\"toggle\"\n qa-dataid=\"input-password-toggle\"\n type=\"button\"\n [attr.aria-label]=\"isPasswordVisible() ? t().uiHidePassword : t().uiShowPassword\"\n [tabIndex]=\"-1\"\n (click)=\"togglePassword()\">\n <rt-icon size=\"sm\" color=\"muted\" [name]=\"passwordToggleIcon()\" />\n </button>\n } @else if (iconRight(); as right) {\n <rt-icon rtElem=\"icon-right\" size=\"sm\" color=\"muted\" [name]=\"right\" />\n }\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input{--rt-input-toggle-radius: var(--rt-radius-sm);position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input--borderless{border-color:transparent}.rt-input--invalid{border-color:var(--rt-input-color-border-error)}.rt-input--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-input--size--sm{min-height:var(--rt-input-height-sm)}.rt-input--size--lg{min-height:var(--rt-input-height-lg)}.rt-input--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;outline:none}.rt-input__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input__field:disabled{cursor:not-allowed}.rt-input__icon-left,.rt-input__icon-right,.rt-input__clear{flex-shrink:0}.rt-input__toggle{display:inline-flex;flex-shrink:0;align-items:center;justify-content:center;padding:var(--rt-space-1);border:none;border-radius:var(--rt-input-toggle-radius);margin:0;background:transparent;color:inherit;cursor:pointer}.rt-input__toggle:hover{background-color:var(--rt-input-color-bg-hover)}.rt-input__toggle:focus-visible{outline:var(--rt-input-border-width) solid var(--rt-input-color-border-focus);outline-offset:var(--rt-input-focus-outline-offset)}}\n"] }]
8504
8771
  }], propDecorators: { fieldEl: [{ type: i0.ViewChild, args: ['fieldEl', { isSignal: true }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], iconLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconLeft", required: false }] }], iconRight: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconRight", required: false }] }], passwordToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "passwordToggle", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }] } });
8505
8772
 
8506
- const BEM_BLOCK$C = 'rt-select';
8773
+ const BEM_BLOCK$B = 'rt-select';
8507
8774
  /** Клавиши, открывающие закрытый dropdown с фокуса на trigger'е. */
8508
8775
  const OPEN_KEYS$1 = new Set(['ArrowDown', 'ArrowUp', 'Enter', ' ']);
8509
8776
  let panelIdSeed$1 = 0;
@@ -8678,7 +8945,7 @@ class RtSelectComponent extends RtFormControlBase {
8678
8945
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtSelectComponent) },
8679
8946
  ], viewQueries: [{ propertyName: "popover", first: true, predicate: RtPopoverDirective, descendants: true, isSignal: true }, { propertyName: "triggerEl", first: true, predicate: ["triggerEl"], descendants: true, isSignal: true }], usesInheritance: true, ngImport: i0, template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '\u2014' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n rtPopoverWidth=\"trigger\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"isOpen() && activeIndex() >= 0 ? panelId + '-opt-' + activeIndex() : null\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverClosed)=\"onClosed()\">\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input [placeholder]=\"filterPlaceholderText()\" [ngModel]=\"filterTerm()\" (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n \u041E\u043F\u0446\u0438\u0438 listbox'\u0430 \u0432\u044B\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043C\u044B\u0448\u044C\u044E \u0447\u0435\u0440\u0435\u0437 (click); keyboard-nav \u2014\n \u043D\u0430 trigger'\u0435 (activedescendant: ArrowUp/Down/Enter), \u043E\u043F\u0446\u0438\u0438 \u043D\u0435\n focusable \u0438\u043D\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043B\u044C\u043D\u043E, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 a11y-\u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u044B.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (opt of filteredOptions(); track opt.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"opt.value\"\n [rtMod]=\"{\n selected: opt.value === value(),\n active: i === activeIndex(),\n disabled: !!opt.disabled,\n }\"\n [attr.aria-selected]=\"opt.value === value()\"\n [attr.aria-disabled]=\"!!opt.disabled\"\n (click)=\"select(opt)\">\n {{ opt.label }}\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-caret-transition-duration: var(--rt-duration-fast);position:relative;display:block;width:100%}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}.rt-select__panel{--rt-select-panel-shadow: var(--rt-shadow-md);width:100%;max-height:var(--rt-input-panel-max-height);box-sizing:border-box;border:var(--rt-input-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-input-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-input-font-size)}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-color-action-primary-subtle);font-weight:var(--rt-font-weight-semibold)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}}\n"], dependencies: [{ kind: "ngmodule", type:
8680
8947
  // Angular
8681
- FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
8948
+ FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
8682
8949
  // standalone components / directives
8683
8950
  RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "size", "color", "rotate"] }, { kind: "component", type: RtInputComponent, selector: "rt-input", inputs: ["type", "placeholder", "iconLeft", "iconRight", "passwordToggle", "autocomplete"] }, { kind: "directive", type: RtPopoverDirective, selector: "[rtPopover]", inputs: ["rtPopover", "rtPopoverTrigger", "rtPopoverWidth", "rtPopoverAlign", "rtPopoverFitViewport", "rtPopoverContext", "rtPopoverPanelClass", "rtPopoverDisabled", "rtPopoverOffsetY", "rtPopoverOffsetX"], outputs: ["rtPopoverOpened", "rtPopoverClosed"], exportAs: ["rtPopover"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8684
8951
  }
@@ -8699,7 +8966,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8699
8966
  // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.
8700
8967
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtSelectComponent) },
8701
8968
  ], host: {
8702
- class: BEM_BLOCK$C,
8969
+ class: BEM_BLOCK$B,
8703
8970
  '[class.rt-select--open]': 'isOpen()',
8704
8971
  '[class.rt-select--disabled]': 'isDisabled()',
8705
8972
  '[class.rt-select--readonly]': 'isReadonly()',
@@ -8711,7 +8978,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8711
8978
  }, template: "<ng-container rtBlock=\"rt-select\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" [rtMod]=\"{ empty: !displayText() }\">{{ displayText() || '\u2014' }}</span>\n }\n <button\n #triggerEl\n rtElem=\"trigger\"\n qa-dataid=\"select-trigger\"\n type=\"button\"\n rtPopoverWidth=\"trigger\"\n [rtPopover]=\"panelTpl\"\n [rtPopoverDisabled]=\"isDisabled()\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.aria-controls]=\"panelId\"\n [attr.aria-activedescendant]=\"isOpen() && activeIndex() >= 0 ? panelId + '-opt-' + activeIndex() : null\"\n [disabled]=\"isDisabled()\"\n (keydown)=\"onTriggerKeydown($event)\"\n (rtPopoverClosed)=\"onClosed()\">\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n <span rtElem=\"label\" [rtMod]=\"{ placeholder: !selectedLabel() }\">\n {{ selectedLabel() || placeholder() }}\n </span>\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n <rt-icon rtElem=\"chevron\" name=\"chevron-down\" size=\"sm\" color=\"muted\" />\n </button>\n\n <ng-template #panelTpl>\n <div rtElem=\"panel\" role=\"listbox\" [attr.aria-label]=\"ariaLabel()\">\n @if (filter()) {\n <div rtElem=\"filter\">\n <rt-input [placeholder]=\"filterPlaceholderText()\" [ngModel]=\"filterTerm()\" (ngModelChange)=\"setFilter($event)\" />\n </div>\n }\n <!--\n \u041E\u043F\u0446\u0438\u0438 listbox'\u0430 \u0432\u044B\u0431\u0438\u0440\u0430\u044E\u0442\u0441\u044F \u043C\u044B\u0448\u044C\u044E \u0447\u0435\u0440\u0435\u0437 (click); keyboard-nav \u2014\n \u043D\u0430 trigger'\u0435 (activedescendant: ArrowUp/Down/Enter), \u043E\u043F\u0446\u0438\u0438 \u043D\u0435\n focusable \u0438\u043D\u0434\u0438\u0432\u0438\u0434\u0443\u0430\u043B\u044C\u043D\u043E, \u043F\u043E\u044D\u0442\u043E\u043C\u0443 a11y-\u043F\u0440\u0430\u0432\u0438\u043B\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435 \u043F\u0440\u0438\u043C\u0435\u043D\u0438\u043C\u044B.\n -->\n <!-- eslint-disable @angular-eslint/template/click-events-have-key-events, @angular-eslint/template/interactive-supports-focus -->\n <ul rtElem=\"list\">\n @for (opt of filteredOptions(); track opt.value; let i = $index) {\n <li\n rtElem=\"option\"\n qa-dataid=\"select-option\"\n role=\"option\"\n [id]=\"panelId + '-opt-' + i\"\n [attr.data-value]=\"opt.value\"\n [rtMod]=\"{\n selected: opt.value === value(),\n active: i === activeIndex(),\n disabled: !!opt.disabled,\n }\"\n [attr.aria-selected]=\"opt.value === value()\"\n [attr.aria-disabled]=\"!!opt.disabled\"\n (click)=\"select(opt)\">\n {{ opt.label }}\n </li>\n } @empty {\n <li rtElem=\"empty\">{{ t().uiNoOptions }}</li>\n }\n </ul>\n </div>\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-select{--rt-select-caret-transition-duration: var(--rt-duration-fast);position:relative;display:block;width:100%}.rt-select--borderless .rt-select__trigger{border-color:transparent}.rt-select__trigger{display:flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:pointer;font-family:inherit;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);text-align:start;transition:var(--rt-input-transition)}.rt-select__trigger:focus{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-select__trigger:disabled{cursor:not-allowed}.rt-select--open .rt-select__trigger{border-color:var(--rt-input-color-border-focus)}.rt-select--invalid .rt-select__trigger{border-color:var(--rt-input-color-border-error)}.rt-select--invalid .rt-select__trigger:focus{box-shadow:var(--rt-input-focus-shadow-error)}.rt-select--disabled{opacity:var(--rt-input-disabled-opacity)}.rt-select--readonly .rt-select__trigger{display:none}.rt-select__readonly{display:inline-flex;min-height:var(--rt-input-height);align-items:center;color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-select__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-select--size--sm .rt-select__trigger{min-height:var(--rt-input-height-sm)}.rt-select--size--lg .rt-select__trigger{min-height:var(--rt-input-height-lg)}.rt-select__label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-select__label--placeholder{color:var(--rt-input-color-text-placeholder)}.rt-select__icon-left,.rt-select__clear{flex-shrink:0}.rt-select__chevron{flex-shrink:0;transition:transform var(--rt-select-caret-transition-duration) ease}.rt-select--open .rt-select__chevron{transform:rotate(180deg)}.rt-select__panel{--rt-select-panel-shadow: var(--rt-shadow-md);width:100%;max-height:var(--rt-input-panel-max-height);box-sizing:border-box;border:var(--rt-input-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-input-radius);background-color:var(--rt-color-bg-surface);box-shadow:var(--rt-select-panel-shadow);overflow-y:auto}.rt-select__filter{padding:var(--rt-space-sm);border-block-end:var(--rt-input-border-width) solid var(--rt-color-border-subtle)}.rt-select__list{padding:var(--rt-space-2xs) 0;margin:0;list-style:none}.rt-select__option{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-input-font-size)}.rt-select__option:hover,.rt-select__option--active{background-color:var(--rt-color-bg-hover)}.rt-select__option--selected{background-color:var(--rt-color-action-primary-subtle);font-weight:var(--rt-font-weight-semibold)}.rt-select__option--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-select__option--disabled:hover{background-color:transparent}.rt-select__empty{padding:var(--rt-space-sm) var(--rt-space-md);color:var(--rt-input-color-text-placeholder);font-style:italic}}\n"] }]
8712
8979
  }], propDecorators: { popover: [{ type: i0.ViewChild, args: [i0.forwardRef(() => RtPopoverDirective), { isSignal: true }] }], triggerEl: [{ type: i0.ViewChild, args: ['triggerEl', { isSignal: true }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], iconLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconLeft", required: false }] }], filter: [{ type: i0.Input, args: [{ isSignal: true, alias: "filter", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], selectionChange: [{ type: i0.Output, args: ["selectionChange"] }] } });
8713
8980
 
8714
- const BEM_BLOCK$B = 'rt-toggle-button-group';
8981
+ const BEM_BLOCK$A = 'rt-toggle-button-group';
8715
8982
  /**
8716
8983
  * Маппинг размера группы на размер вложенной `<rt-icon>`. rt-icon не имеет
8717
8984
  * промежуточных значений (12 / 16 / 20 / 24 / 32), поэтому `lg` тоже едет
@@ -8778,12 +9045,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8778
9045
  ElemDirective,
8779
9046
  ModDirective,
8780
9047
  ], host: {
8781
- class: BEM_BLOCK$B,
9048
+ class: BEM_BLOCK$A,
8782
9049
  '[class.rt-toggle-button-group--full-width]': 'fullWidth()',
8783
9050
  }, template: "<div\n rtBlock=\"rt-toggle-button-group\"\n role=\"group\"\n [rtMod]=\"{\n sm: size() === 'sm',\n md: size() === 'md',\n lg: size() === 'lg',\n disabled: disabled(),\n }\"\n [attr.aria-label]=\"ariaLabel()\">\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n rtElem=\"button\"\n qa-dataid=\"toggle-button-group-option\"\n [attr.data-value]=\"option.value\"\n [rtMod]=\"{\n active: value() === option.value,\n first: $first,\n last: $last,\n }\"\n [disabled]=\"disabled()\"\n [attr.aria-pressed]=\"value() === option.value ? 'true' : 'false'\"\n [rtTooltip]=\"option.title ?? ''\"\n (click)=\"onOptionClick(option)\">\n @if (option.icon) {\n <rt-icon rtElem=\"icon\" color=\"current\" [name]=\"option.icon\" [size]=\"iconSize()\" />\n }\n <span rtElem=\"label\">{{ option.label }}</span>\n </button>\n }\n</div>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-toggle-button-group{display:inline-flex;--rt-toggle-button-group-track-bg: var(--rt-color-bg-surface-subtle);--rt-toggle-button-group-active-bg: var(--rt-color-bg-surface)}rt-toggle-button-group .rt-toggle-button-group{--rt-toggle-button-group-height: 30px;--rt-toggle-button-group-transition-duration: var(--rt-duration-fast);--rt-toggle-button-group-focus-shadow: var(--rt-shadow-focus-ring);--rt-toggle-button-group-track-padding: 4px;--rt-toggle-button-group-track-radius: 8px;--rt-toggle-button-group-btn-padding: 0 8px;--rt-toggle-button-group-btn-radius: 8px;--rt-toggle-button-group-btn-gap: 4px;--rt-toggle-button-group-btn-font-size: 12px;--rt-toggle-button-group-btn-line-height: 14px;display:inline-flex;height:var(--rt-toggle-button-group-height);box-sizing:border-box;align-items:stretch;padding:var(--rt-toggle-button-group-track-padding);border-radius:var(--rt-toggle-button-group-track-radius);background-color:var(--rt-toggle-button-group-track-bg);gap:0}rt-toggle-button-group .rt-toggle-button-group__button{display:inline-flex;align-items:center;justify-content:center;padding:var(--rt-toggle-button-group-btn-padding);border:0;border-radius:var(--rt-toggle-button-group-btn-radius);background-color:transparent;color:var(--rt-color-text-muted);cursor:pointer;font-family:inherit;font-size:var(--rt-toggle-button-group-btn-font-size);font-weight:var(--rt-font-weight-semibold);gap:var(--rt-toggle-button-group-btn-gap);line-height:var(--rt-toggle-button-group-btn-line-height);transition:background-color var(--rt-toggle-button-group-transition-duration) ease,color var(--rt-toggle-button-group-transition-duration) ease}rt-toggle-button-group .rt-toggle-button-group__button:hover:not(:disabled,rt-toggle-button-group .rt-toggle-button-group__button--active){color:var(--rt-color-text-primary)}rt-toggle-button-group .rt-toggle-button-group__button--active{background-color:var(--rt-toggle-button-group-active-bg);color:var(--rt-color-text-primary)}rt-toggle-button-group .rt-toggle-button-group__button:focus-visible{position:relative;z-index:1;box-shadow:var(--rt-toggle-button-group-focus-shadow);outline:2px solid transparent;outline-offset:1px}rt-toggle-button-group .rt-toggle-button-group__button:disabled{cursor:not-allowed}rt-toggle-button-group .rt-toggle-button-group__label{display:inline-block;white-space:nowrap}rt-toggle-button-group .rt-toggle-button-group__icon{flex-shrink:0}rt-toggle-button-group .rt-toggle-button-group--md{--rt-toggle-button-group-height: 40px;--rt-toggle-button-group-track-radius: 10px;--rt-toggle-button-group-btn-padding: 0 14px;--rt-toggle-button-group-btn-radius: 10px;--rt-toggle-button-group-btn-gap: 6px;--rt-toggle-button-group-btn-font-size: 14px;--rt-toggle-button-group-btn-line-height: 16px}rt-toggle-button-group .rt-toggle-button-group--lg{--rt-toggle-button-group-height: 50px;--rt-toggle-button-group-track-radius: 12px;--rt-toggle-button-group-btn-padding: 0 20px;--rt-toggle-button-group-btn-radius: 12px;--rt-toggle-button-group-btn-gap: 8px;--rt-toggle-button-group-btn-font-size: 16px;--rt-toggle-button-group-btn-line-height: 18px}rt-toggle-button-group .rt-toggle-button-group--disabled{opacity:.5}rt-toggle-button-group.rt-toggle-button-group--full-width{display:flex;width:100%}rt-toggle-button-group.rt-toggle-button-group--full-width .rt-toggle-button-group{display:flex;width:100%;flex:1 1 auto}rt-toggle-button-group.rt-toggle-button-group--full-width .rt-toggle-button-group .rt-toggle-button-group__button{flex:1 1 0}:root[data-theme=dark] rt-toggle-button-group,html.rt-theme-dark rt-toggle-button-group{--rt-toggle-button-group-track-bg: var(--rt-neutral-900)}}\n"] }]
8784
9051
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
8785
9052
 
8786
- const BEM_BLOCK$A = 'rt-filter-control';
9053
+ const BEM_BLOCK$z = 'rt-filter-control';
8787
9054
  /**
8788
9055
  * Адаптивный фильтр-контрол для тулбаров list-страниц.
8789
9056
  *
@@ -8843,7 +9110,7 @@ class RtFilterControlComponent {
8843
9110
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtFilterControlComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8844
9111
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtFilterControlComponent, isStandalone: true, selector: "rt-filter-control", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, fullWidth: { classPropertyName: "fullWidth", publicName: "fullWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { classAttribute: "rt-filter-control" }, ngImport: i0, template: "@if (isNarrow()) {\n <rt-select\n qa-dataid=\"filter-control-select\"\n size=\"md\"\n [options]=\"selectOptions()\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"ariaLabel()\"\n [clearable]=\"false\"\n [disabled]=\"disabled()\"\n [ngModel]=\"value()\"\n (selectionChange)=\"onSelectionChange($event)\" />\n} @else {\n <rt-toggle-button-group\n qa-dataid=\"filter-control-group\"\n [options]=\"options()\"\n [value]=\"value()\"\n [ariaLabel]=\"ariaLabel()\"\n [size]=\"size()\"\n [disabled]=\"disabled()\"\n [fullWidth]=\"fullWidth()\"\n (valueChange)=\"onValueChange($event)\" />\n}\n", styles: ["@layer rt-kit.components{.rt-filter-control{display:inline-flex}@media(width<=1080px){.rt-filter-control{display:block}.rt-filter-control .rt-select{inline-size:100%;max-inline-size:var(--rt-input-max-width)}}@media(width<=768px){.rt-filter-control{inline-size:100%}}}\n"], dependencies: [{ kind: "ngmodule", type:
8845
9112
  // Angular
8846
- FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
9113
+ FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
8847
9114
  // standalone components / directives
8848
9115
  RtSelectComponent, selector: "rt-select", inputs: ["options", "placeholder", "iconLeft", "filter", "filterPlaceholder"], outputs: ["selectionChange"] }, { kind: "component", type: RtToggleButtonGroupComponent, selector: "rt-toggle-button-group", inputs: ["options", "value", "ariaLabel", "size", "disabled", "fullWidth"], outputs: ["valueChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
8849
9116
  }
@@ -8856,7 +9123,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8856
9123
  RtSelectComponent,
8857
9124
  RtToggleButtonGroupComponent,
8858
9125
  ], host: {
8859
- class: BEM_BLOCK$A,
9126
+ class: BEM_BLOCK$z,
8860
9127
  }, template: "@if (isNarrow()) {\n <rt-select\n qa-dataid=\"filter-control-select\"\n size=\"md\"\n [options]=\"selectOptions()\"\n [placeholder]=\"placeholder()\"\n [ariaLabel]=\"ariaLabel()\"\n [clearable]=\"false\"\n [disabled]=\"disabled()\"\n [ngModel]=\"value()\"\n (selectionChange)=\"onSelectionChange($event)\" />\n} @else {\n <rt-toggle-button-group\n qa-dataid=\"filter-control-group\"\n [options]=\"options()\"\n [value]=\"value()\"\n [ariaLabel]=\"ariaLabel()\"\n [size]=\"size()\"\n [disabled]=\"disabled()\"\n [fullWidth]=\"fullWidth()\"\n (valueChange)=\"onValueChange($event)\" />\n}\n", styles: ["@layer rt-kit.components{.rt-filter-control{display:inline-flex}@media(width<=1080px){.rt-filter-control{display:block}.rt-filter-control .rt-select{inline-size:100%;max-inline-size:var(--rt-input-max-width)}}@media(width<=768px){.rt-filter-control{inline-size:100%}}}\n"] }]
8861
9128
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], fullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullWidth", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
8862
9129
 
@@ -8873,7 +9140,7 @@ const DEFAULT_ASPECT = Object.freeze({
8873
9140
  wordmark: 5.4,
8874
9141
  lockup: 3.6,
8875
9142
  });
8876
- const BEM_BLOCK$z = 'rt-logo';
9143
+ const BEM_BLOCK$y = 'rt-logo';
8877
9144
  /**
8878
9145
  * Логотип приложения: начертание названия и, в варианте `lockup`, слоган под ним.
8879
9146
  *
@@ -8939,7 +9206,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8939
9206
  BlockDirective,
8940
9207
  ElemDirective,
8941
9208
  ], host: {
8942
- class: BEM_BLOCK$z,
9209
+ class: BEM_BLOCK$y,
8943
9210
  '[style.height.px]': 'effectiveHeight()',
8944
9211
  '[style.width.px]': 'effectiveWidth()',
8945
9212
  '[attr.role]': "'img'",
@@ -8947,7 +9214,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
8947
9214
  }, template: "<ng-container rtBlock=\"rt-logo\">\n <span rtElem=\"wordmark\" qa-dataid=\"logo-wordmark\"></span>\n\n @if (showTagline()) {\n <span rtElem=\"tagline\" qa-dataid=\"logo-tagline\"></span>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-logo{display:inline-block;line-height:0}.rt-logo__wordmark,.rt-logo__tagline{display:block;width:100%;background-position:left center;background-repeat:no-repeat;background-size:contain}.rt-logo__wordmark{height:100%;background-image:var(--rt-logo-wordmark, none)}.rt-logo__tagline{background-image:var(--rt-logo-tagline, none)}[data-theme=dark] .rt-logo__wordmark{background-image:var(--rt-logo-wordmark-dark, var(--rt-logo-wordmark, none))}[data-theme=dark] .rt-logo__tagline{background-image:var(--rt-logo-tagline-dark, var(--rt-logo-tagline, none))}.rt-logo:has(.rt-logo__tagline){display:flex;flex-direction:column;align-items:stretch;justify-content:space-between}.rt-logo:has(.rt-logo__tagline) .rt-logo__wordmark{height:66.7%}.rt-logo:has(.rt-logo__tagline) .rt-logo__tagline{height:22.1%}}\n"] }]
8948
9215
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }], aspect: [{ type: i0.Input, args: [{ isSignal: true, alias: "aspect", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
8949
9216
 
8950
- const BEM_BLOCK$y = 'rt-header';
9217
+ const BEM_BLOCK$x = 'rt-header';
8951
9218
  /**
8952
9219
  * Общий хедер приложения.
8953
9220
  *
@@ -9002,11 +9269,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9002
9269
  ElemDirective,
9003
9270
  ModDirective,
9004
9271
  ], host: {
9005
- class: BEM_BLOCK$y,
9272
+ class: BEM_BLOCK$x,
9006
9273
  }, template: "<ng-container rtBlock=\"rt-header\">\n <div rtElem=\"content\">\n <rt-icon-button\n rtElem=\"back\"\n qa-dataid=\"header-back\"\n icon=\"arrow-left\"\n variant=\"ghost\"\n size=\"md\"\n shape=\"circle\"\n [ariaLabel]=\"t().uiBack\"\n [tooltip]=\"t().uiBack\"\n [rtMod]=\"{ visible: canGoBack() }\"\n (clicked)=\"onBackClick()\" />\n\n <div rtElem=\"logo\" qa-dataid=\"header-logo\">\n <rt-logo variant=\"wordmark\" height=\"15\" />\n </div>\n\n @if (showInvite()) {\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"header-invite\"\n icon=\"user-plus\"\n variant=\"ghost\"\n size=\"md\"\n shape=\"circle\"\n style=\"--rt-icon-button-size: 35px\"\n [ariaLabel]=\"t().uiInviteUser\"\n [tooltip]=\"t().uiInviteUser\"\n (clicked)=\"onInviteClick()\" />\n }\n\n <ng-content select=\"[rtHeaderBell]\" />\n\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"header-profile\"\n icon=\"user\"\n variant=\"ghost\"\n size=\"md\"\n shape=\"circle\"\n style=\"--rt-icon-button-size: 35px\"\n [ariaLabel]=\"t().uiProfile\"\n [tooltip]=\"t().uiProfile\"\n (clicked)=\"onProfileClick()\" />\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-header{--rt-header-radius: var(--rt-radius-lg);--rt-header-shadow: var(--rt-shadow-card-soft);--rt-header-back-transition-duration: var(--rt-duration-base);display:block;height:45px;padding:0 var(--rt-space-5);background:var(--rt-color-bg-surface);border-bottom-left-radius:var(--rt-header-radius);border-bottom-right-radius:var(--rt-header-radius);box-shadow:var(--rt-header-shadow);color:var(--rt-color-text-primary)}.rt-header__content{display:flex;width:100%;max-width:var(--rt-header-content-max-width, 100%);height:100%;align-items:center;margin:0 auto;gap:0}.rt-header__back{display:inline-flex;overflow:hidden;width:0;height:45px;align-items:center;justify-content:flex-start;opacity:0;pointer-events:none;transform:translate(-8px);transition:width var(--rt-header-back-transition-duration) cubic-bezier(.34,1.56,.64,1),opacity var(--rt-header-back-transition-duration) ease-out,transform var(--rt-header-back-transition-duration) cubic-bezier(.34,1.56,.64,1)}.rt-header__back--visible{width:35px;opacity:1;pointer-events:auto;transform:translate(0)}.rt-header__logo{display:flex;min-width:0;flex:1 0 0;align-items:center}[data-theme=dark] .rt-header{background:var(--rt-color-bg-nav)}@media(prefers-reduced-motion:reduce){.rt-header__back{transition:none}}}\n"] }]
9007
9274
  }], propDecorators: { canGoBack: [{ type: i0.Input, args: [{ isSignal: true, alias: "canGoBack", required: false }] }], showInvite: [{ type: i0.Input, args: [{ isSignal: true, alias: "showInvite", required: false }] }], backClick: [{ type: i0.Output, args: ["backClick"] }], inviteClick: [{ type: i0.Output, args: ["inviteClick"] }], profileClick: [{ type: i0.Output, args: ["profileClick"] }] } });
9008
9275
 
9009
- const BEM_BLOCK$x = 'rt-info-item';
9276
+ const BEM_BLOCK$w = 'rt-info-item';
9010
9277
  class RtInfoItemComponent {
9011
9278
  constructor() {
9012
9279
  this.label = input.required(/* @ts-ignore */
@@ -9027,12 +9294,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9027
9294
  BlockDirective,
9028
9295
  ElemDirective,
9029
9296
  ], host: {
9030
- class: BEM_BLOCK$x,
9297
+ class: BEM_BLOCK$w,
9031
9298
  '[class.rt-info-item--grow]': 'grow()',
9032
9299
  }, template: "<ng-container rtBlock=\"rt-info-item\">\n <span rtElem=\"label\" qa-dataid=\"info-item-label\">{{ label() }}</span>\n @if (loading()) {\n <rt-skeleton-wrapper rtElem=\"value-skeleton\" qa-dataid=\"info-item-skeleton\" width=\"80px\" [isLoading]=\"true\" />\n } @else {\n <span rtElem=\"value\" qa-dataid=\"info-item-value\"><ng-content /></span>\n }\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-info-item{padding:var(--rt-space-md);border-radius:var(--rt-radius-xl);background:var(--rt-color-bg-surface);box-shadow:var(--rt-shadow-card-soft);display:flex;min-width:0;flex-direction:column;margin:0;gap:var(--rt-space-0-5)}.rt-info-item--grow{flex:1 0 0}.rt-info-item__label{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-info-item__value{display:flex;flex-wrap:wrap;align-items:baseline;margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-bold);gap:var(--rt-space-0-5);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug);overflow-wrap:anywhere}.rt-info-item__value-skeleton{display:block}[data-theme=dark] .rt-info-item{background:var(--rt-color-bg-nav);box-shadow:var(--rt-shadow-card-soft)}}\n"] }]
9033
9300
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], grow: [{ type: i0.Input, args: [{ isSignal: true, alias: "grow", required: false }] }] } });
9034
9301
 
9035
- const BEM_BLOCK$w = 'rt-input-number';
9302
+ const BEM_BLOCK$v = 'rt-input-number';
9036
9303
  /**
9037
9304
  * Парсит строку в `number | null` по разделителям локали: `groupSep` и любые
9038
9305
  * пробелы удаляются, оставшийся разделитель дробной части приводится к точке.
@@ -9352,7 +9619,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9352
9619
  // Алиас базового токена — для contentChild(RtFormControlBase) в rt-field.
9353
9620
  { provide: RtFormControlBase, useExisting: forwardRef(() => RtInputNumberComponent) },
9354
9621
  ], host: {
9355
- class: BEM_BLOCK$w,
9622
+ class: BEM_BLOCK$v,
9356
9623
  '[class.rt-input-number--disabled]': 'isDisabled()',
9357
9624
  '[class.rt-input-number--invalid]': 'isInvalid()',
9358
9625
  '[class.rt-input-number--readonly]': 'isReadonly()',
@@ -9363,7 +9630,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9363
9630
  }, template: "<ng-container rtBlock=\"rt-input-number\">\n @if (isReadonly()) {\n <span rtElem=\"readonly\" qa-dataid=\"input-number-readonly\" [rtMod]=\"{ empty: !displayText() }\">\n @if (prefix() && displayText()) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\">{{ prefix() }}</span>\n }\n {{ displayText() || '\u2014' }}\n </span>\n } @else {\n @if (iconLeft(); as left) {\n <rt-icon rtElem=\"icon-left\" size=\"sm\" color=\"muted\" [name]=\"left\" />\n }\n @if (prefix(); as pfx) {\n <span rtElem=\"prefix\" qa-dataid=\"input-number-prefix\" aria-hidden=\"true\">{{ pfx }}</span>\n }\n <input\n #fieldEl\n rtElem=\"field\"\n qa-dataid=\"input-number-control\"\n type=\"text\"\n inputmode=\"decimal\"\n autocomplete=\"off\"\n [attr.id]=\"effectiveControlId()\"\n [attr.aria-label]=\"ariaLabel()\"\n [attr.aria-invalid]=\"isInvalid() || null\"\n [attr.placeholder]=\"placeholder()\"\n [disabled]=\"isDisabled()\"\n [value]=\"displayValue()\"\n (input)=\"onInput($event)\"\n (blur)=\"onBlur()\" />\n @if (showClearButton()) {\n <rt-icon-button\n rtElem=\"clear\"\n qa-dataid=\"input-number-clear\"\n icon=\"ico-close\"\n size=\"sm\"\n variant=\"ghost\"\n iconColor=\"muted\"\n [ariaLabel]=\"t().uiClear\"\n [tabIndex]=\"-1\"\n (clicked)=\"clear($event)\" />\n }\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-input-number{position:relative;display:inline-flex;width:100%;min-height:var(--rt-input-height);box-sizing:border-box;align-items:center;padding:var(--rt-input-padding-y) var(--rt-input-padding-x);border:var(--rt-input-border-width) solid var(--rt-input-color-border);border-radius:var(--rt-input-radius);background-color:var(--rt-input-color-bg);color:var(--rt-input-color-text);cursor:text;font-size:var(--rt-input-font-size);gap:var(--rt-space-sm);line-height:var(--rt-input-line-height);transition:var(--rt-input-transition)}.rt-input-number:focus-within{border-color:var(--rt-input-color-border-focus);box-shadow:var(--rt-input-focus-shadow);outline:2px solid transparent;outline-offset:var(--rt-input-focus-outline-offset)}.rt-input-number--borderless{border-color:transparent}.rt-input-number--invalid{border-color:var(--rt-input-color-border-error)}.rt-input-number--invalid:focus-within{box-shadow:var(--rt-input-focus-shadow-error)}.rt-input-number--disabled{cursor:not-allowed;opacity:var(--rt-input-disabled-opacity)}.rt-input-number--size--sm{min-height:var(--rt-input-height-sm)}.rt-input-number--size--lg{min-height:var(--rt-input-height-lg)}.rt-input-number--readonly{padding-right:0;padding-left:0;border-color:transparent;background-color:transparent;cursor:default}.rt-input-number__readonly{color:var(--rt-field-value-color);font-size:var(--rt-field-value-font-size)}.rt-input-number__readonly--empty{color:var(--rt-field-value-empty-color)}.rt-input-number__field{min-width:0;flex:1 1 auto;padding:0;border:none;background:transparent;color:inherit;font:inherit;outline:none}.rt-input-number__field::placeholder{color:var(--rt-input-color-text-placeholder);opacity:1}.rt-input-number__field:disabled{cursor:not-allowed}.rt-input-number__icon-left,.rt-input-number__prefix,.rt-input-number__clear{flex-shrink:0}.rt-input-number__prefix{color:var(--rt-color-text-muted);-webkit-user-select:none;user-select:none}}\n"] }]
9364
9631
  }], propDecorators: { fieldEl: [{ type: i0.ViewChild, args: ['fieldEl', { isSignal: true }] }], iconLeft: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconLeft", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "minFractionDigits", required: false }] }], maxFractionDigits: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFractionDigits", required: false }] }] } });
9365
9632
 
9366
- const BEM_BLOCK$v = 'rt-live-badge';
9633
+ const BEM_BLOCK$u = 'rt-live-badge';
9367
9634
  /**
9368
9635
  * Пилюля живого счётчика: точка состояния, подпись и число. `active` означает
9369
9636
  * «поток жив», а не «счётчик больше нуля»: ноль посетителей при живом потоке —
@@ -9397,11 +9664,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9397
9664
  ElemDirective,
9398
9665
  ModDirective,
9399
9666
  ], host: {
9400
- class: BEM_BLOCK$v,
9667
+ class: BEM_BLOCK$u,
9401
9668
  }, template: "<span rtBlock=\"rt-live-badge\" qa-dataid=\"live-badge\" [attr.data-active]=\"active()\" [rtMod]=\"{ active: active() }\">\n <span rtElem=\"dot\"></span>\n <span rtElem=\"label\" qa-dataid=\"live-badge-label\">{{ labelText() }}</span>\n <span rtElem=\"count\" qa-dataid=\"live-badge-count\">{{ count() ?? '\u2014' }}</span>\n</span>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{rt-live-badge{display:inline-flex}rt-live-badge .rt-live-badge{--rt-live-badge-dot-radius: var(--rt-radius-full);display:inline-flex;align-items:center;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);gap:var(--rt-space-1)}rt-live-badge .rt-live-badge--active .rt-live-badge__dot{background:var(--rt-color-state-success)}rt-live-badge .rt-live-badge__dot{width:8px;height:8px;border-radius:var(--rt-live-badge-dot-radius);background:var(--rt-color-border-subtle)}rt-live-badge .rt-live-badge__count{color:var(--rt-color-text-primary)}}\n"] }]
9402
9669
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], count: [{ type: i0.Input, args: [{ isSignal: true, alias: "count", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }] } });
9403
9670
 
9404
- const BEM_BLOCK$u = 'rt-markdown-text';
9671
+ const BEM_BLOCK$t = 'rt-markdown-text';
9405
9672
  /**
9406
9673
  * Текст, написанный разметкой, показанный узлами.
9407
9674
  *
@@ -9440,11 +9707,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9440
9707
  ModDirective,
9441
9708
  NgTemplateOutlet,
9442
9709
  ], host: {
9443
- class: BEM_BLOCK$u,
9710
+ class: BEM_BLOCK$t,
9444
9711
  }, template: "<ng-container rtBlock=\"rt-markdown-text\">\n @for (item of blocks(); track $index) {\n <ng-container *ngTemplateOutlet=\"blockTpl; context: { $implicit: item }\" />\n }\n\n <ng-template #blockTpl let-item>\n @switch (item.kind) {\n @case (blockKind.Heading) {\n <div\n rtElem=\"heading\"\n qa-dataid=\"markdown-heading\"\n role=\"heading\"\n [attr.aria-level]=\"item.level\"\n [rtMod]=\"{ level: item.levelName }\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: item.content }\" />\n </div>\n }\n @case (blockKind.List) {\n @if (item.ordered) {\n <ol rtElem=\"list\" qa-dataid=\"markdown-list\" [rtMod]=\"{ ordered: true }\">\n <ng-container *ngTemplateOutlet=\"itemsTpl; context: { $implicit: item.items }\" />\n </ol>\n } @else {\n <ul rtElem=\"list\" qa-dataid=\"markdown-list\">\n <ng-container *ngTemplateOutlet=\"itemsTpl; context: { $implicit: item.items }\" />\n </ul>\n }\n }\n @case (blockKind.Quote) {\n <blockquote rtElem=\"quote\" qa-dataid=\"markdown-quote\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: item.content }\" />\n </blockquote>\n }\n @case (blockKind.Code) {\n <pre rtElem=\"code\" qa-dataid=\"markdown-code\"><code>{{ item.code }}</code></pre>\n }\n @case (blockKind.Table) {\n <ng-container *ngTemplateOutlet=\"tableTpl; context: { $implicit: item }\" />\n }\n @default {\n <p rtElem=\"paragraph\" qa-dataid=\"markdown-paragraph\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: item.content }\" />\n </p>\n }\n }\n </ng-template>\n\n <ng-template #itemsTpl let-items>\n @for (point of items; track $index) {\n <li rtElem=\"item\" qa-dataid=\"markdown-item\" [rtMod]=\"{ level: point.levelName }\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: point.content }\" />\n </li>\n }\n </ng-template>\n\n <ng-template #tableTpl let-item>\n <div rtElem=\"table-scroll\" qa-dataid=\"markdown-table-scroll\">\n <table rtElem=\"table\" qa-dataid=\"markdown-table\">\n <thead>\n <tr>\n @for (cell of item.head; track $index) {\n <th rtElem=\"head-cell\" qa-dataid=\"markdown-head-cell\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: cell }\" />\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of item.rows; track $index) {\n <tr>\n @for (cell of row; track $index) {\n <td rtElem=\"cell\" qa-dataid=\"markdown-cell\">\n <ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: cell }\" />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n </ng-template>\n\n <!--\n \u0412\u0435\u0442\u043A\u0438 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u0443\u0441\u043A\u043E\u0432 \u043F\u0438\u0448\u0443\u0442\u0441\u044F \u0431\u0435\u0437 \u043F\u0435\u0440\u0435\u043D\u043E\u0441\u043E\u0432, \u0430 \u0444\u043E\u0440\u043C\u0430\u0442\u0442\u0435\u0440 \u043E\u0442\u043A\u043B\u044E\u0447\u0451\u043D \u0434\u043B\u044F \u043D\u0438\u0445 \u043D\u0430\u0440\u043E\u0447\u043D\u043E: \u043F\u0435\u0440\u0435\u043D\u043E\u0441\n \u0441\u0442\u0440\u043E\u043A\u0438 \u0432 \u0440\u0430\u0437\u043C\u0435\u0442\u043A\u0435 \u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0441\u044F \u043F\u0440\u043E\u0431\u0435\u043B\u043E\u043C, \u0438 \u0440\u0430\u0437\u043C\u0435\u0447\u0435\u043D\u043D\u044B\u0439 \u043A\u0443\u0441\u043E\u043A \u043E\u0442\u044A\u0435\u0437\u0436\u0430\u0435\u0442 \u043E\u0442 \u0441\u043E\u0441\u0435\u0434\u043D\u0435\u0433\u043E \u0437\u043D\u0430\u043A\u0430 \u2014\n \u00AB\u0436\u0438\u0440\u043D\u043E , \u0430 \u0440\u044F\u0434\u043E\u043C\u00BB.\n -->\n <!-- prettier-ignore -->\n <ng-template #inlineTpl let-nodes>@for (node of nodes; track $index) {@switch (node.kind) {@case (inlineKind.Strong) {<strong rtElem=\"strong\"><ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: node.children }\" /></strong>}@case (inlineKind.Emphasis) {<em rtElem=\"emphasis\"><ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: node.children }\" /></em>}@case (inlineKind.Strike) {<s rtElem=\"strike\"><ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: node.children }\" /></s>}@case (inlineKind.Code) {<code rtElem=\"code-inline\" qa-dataid=\"markdown-code-inline\">{{ node.text }}</code>}@case (inlineKind.Link) {<a rtElem=\"link\" qa-dataid=\"markdown-link\" rel=\"noopener noreferrer\" target=\"_blank\" [href]=\"node.href\"><ng-container *ngTemplateOutlet=\"inlineTpl; context: { $implicit: node.children }\" /></a>}@case (inlineKind.Break) {<br />}@default {{{ node.text }}}}}</ng-template>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-markdown-text{--rt-markdown-text-radius: var(--rt-radius-sm);--rt-markdown-text-border-width: var(--rt-border-width-thin);display:flex;min-width:0;max-width:100%;flex-direction:column;color:var(--rt-color-text-primary);font-family:var(--rt-font-family-sans);font-size:var(--rt-text-xs);font-weight:var(--rt-font-weight-regular);gap:var(--rt-space-sm);letter-spacing:var(--rt-letter-spacing-wide);line-height:var(--rt-leading-snug)}.rt-markdown-text__heading{margin:0;color:var(--rt-color-text-primary);font-weight:var(--rt-font-weight-semibold)}.rt-markdown-text__heading--level--1{font-size:var(--rt-text-lg)}.rt-markdown-text__heading--level--2{font-size:var(--rt-text-md)}.rt-markdown-text__heading--level--3,.rt-markdown-text__heading--level--4,.rt-markdown-text__heading--level--5,.rt-markdown-text__heading--level--6{font-size:var(--rt-text-sm)}.rt-markdown-text__paragraph{margin:0}.rt-markdown-text__list{padding-left:var(--rt-space-md);margin:0;list-style:disc}.rt-markdown-text__list--ordered{list-style:decimal}.rt-markdown-text__item--level--1{margin-left:var(--rt-space-md)}.rt-markdown-text__item--level--2{margin-left:var(--rt-space-lg)}.rt-markdown-text__quote{padding-left:var(--rt-space-sm);border-left:var(--rt-markdown-text-border-width) solid var(--rt-color-border-default);margin:0;color:var(--rt-color-text-muted)}.rt-markdown-text__code{max-width:100%;padding:var(--rt-space-sm);border-radius:var(--rt-markdown-text-radius);margin:0;background:var(--rt-color-bg-subtle);color:var(--rt-color-text-primary);font-family:var(--rt-font-family-mono);font-size:var(--rt-text-2xs);line-height:var(--rt-leading-snug);overflow-x:auto}.rt-markdown-text__code-inline{padding:0 var(--rt-space-2xs);border-radius:var(--rt-markdown-text-radius);background:var(--rt-color-bg-subtle);font-family:var(--rt-font-family-mono);font-size:var(--rt-text-2xs)}.rt-markdown-text__table-scroll{max-width:100%;overflow-x:auto}.rt-markdown-text__table{width:100%;border-collapse:collapse}.rt-markdown-text__head-cell,.rt-markdown-text__cell{padding:var(--rt-space-2xs) var(--rt-space-sm);border:var(--rt-markdown-text-border-width) solid var(--rt-color-border-default);text-align:left}.rt-markdown-text__head-cell{color:var(--rt-color-text-muted);font-weight:var(--rt-font-weight-semibold)}.rt-markdown-text__strong{font-weight:var(--rt-font-weight-semibold)}.rt-markdown-text__emphasis{font-style:italic}.rt-markdown-text__strike{text-decoration:line-through}.rt-markdown-text__link{color:var(--rt-color-text-link);text-decoration:underline}}\n"] }]
9445
9712
  }], propDecorators: { text: [{ type: i0.Input, args: [{ isSignal: true, alias: "text", required: true }] }] } });
9446
9713
 
9447
- const BEM_BLOCK$t = 'rt-menu-confirm-dialog';
9714
+ const BEM_BLOCK$s = 'rt-menu-confirm-dialog';
9448
9715
  /**
9449
9716
  * Confirm-модалка деструктивного пункта `rt-menu-item`. Открывается через
9450
9717
  * `RtDialogService` (центрированный overlay поверх меню), возвращает `true`
@@ -9482,7 +9749,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9482
9749
  BlockDirective,
9483
9750
  ElemDirective,
9484
9751
  ], host: {
9485
- class: BEM_BLOCK$t,
9752
+ class: BEM_BLOCK$s,
9486
9753
  }, template: "<rt-dialog width=\"360px\" [ariaLabel]=\"t().uiConfirmAction\">\n <div rtBlock=\"rt-menu-confirm-dialog\">\n <div rtElem=\"text\">\n @if (data.title; as title) {\n <h2 rtElem=\"title\" qa-dataid=\"menu-confirm-title\">{{ title }}</h2>\n }\n <p rtElem=\"message\" qa-dataid=\"menu-confirm-message\">{{ data.message }}</p>\n </div>\n <div rtElem=\"footer\">\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content -->\n <button\n rtButton\n qa-dataid=\"menu-confirm-cancel\"\n type=\"button\"\n appearance=\"text\"\n theme=\"secondary\"\n [label]=\"data.cancelLabel\"\n (click)=\"onCancel()\"></button>\n <!-- eslint-disable-next-line @angular-eslint/template/elements-content -->\n <button\n rtButton\n qa-dataid=\"menu-confirm-accept\"\n type=\"button\"\n appearance=\"filled\"\n [label]=\"data.confirmLabel\"\n [theme]=\"data.tone\"\n (click)=\"onConfirm()\"></button>\n </div>\n </div>\n</rt-dialog>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu-confirm-dialog{display:flex;box-sizing:border-box;flex-direction:column;padding:var(--rt-space-xl);gap:var(--rt-space-lg)}.rt-menu-confirm-dialog__text{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-menu-confirm-dialog__title{margin:0;color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-bold)}.rt-menu-confirm-dialog__message{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-menu-confirm-dialog__footer{display:flex;justify-content:flex-end;gap:var(--rt-space-sm)}}\n"] }]
9487
9754
  }] });
9488
9755
 
@@ -9494,7 +9761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9494
9761
  */
9495
9762
  const RT_MENU_SELECT_EVENT = 'rtMenuSelect';
9496
9763
 
9497
- const BEM_BLOCK$s = 'rt-menu-item';
9764
+ const BEM_BLOCK$r = 'rt-menu-item';
9498
9765
  /**
9499
9766
  * Пункт меню `rt-menu`: иконка (опц.) + лейбл. Кликабелен и активируется с
9500
9767
  * клавиатуры (Enter/Space). Выбор пункта эмитит `(selected)` для потребителя
@@ -9630,7 +9897,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9630
9897
  inputs: ['rtTooltip: tooltip'],
9631
9898
  },
9632
9899
  ], host: {
9633
- class: BEM_BLOCK$s,
9900
+ class: BEM_BLOCK$r,
9634
9901
  role: 'menuitem',
9635
9902
  '[class.rt-menu-item--danger]': 'danger()',
9636
9903
  '[class.rt-menu-item--disabled]': 'disabled()',
@@ -9642,117 +9909,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
9642
9909
  }, template: "<ng-container rtBlock=\"rt-menu-item\">\n @if (icon(); as iconName) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"iconName\" />\n }\n\n <span rtElem=\"label\" qa-dataid=\"menu-item-label\">{{ label() }}</span>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu-item{--rt-menu-item-radius: var(--rt-radius-sm);--rt-menu-item-transition-duration: var(--rt-duration-fast);display:flex;width:100%;box-sizing:border-box;align-items:center;padding:var(--rt-space-sm) var(--rt-space-md);border-radius:var(--rt-menu-item-radius);color:var(--rt-color-text-primary);cursor:pointer;font-size:var(--rt-text-sm);gap:var(--rt-space-sm);text-align:start;transition:background-color var(--rt-menu-item-transition-duration) ease}.rt-menu-item:hover{background:var(--rt-color-bg-hover)}.rt-menu-item:focus-visible{background:var(--rt-color-bg-hover);outline:none}.rt-menu-item__icon{flex-shrink:0;color:var(--rt-color-text-muted)}.rt-menu-item__label{flex:1 1 auto;white-space:nowrap}.rt-menu-item--danger,.rt-menu-item--danger .rt-menu-item__icon{color:var(--rt-color-state-error-text)}.rt-menu-item--disabled{color:var(--rt-color-text-muted);cursor:default;opacity:.5}.rt-menu-item--disabled:hover{background:transparent}}\n"] }]
9643
9910
  }], ctorParameters: () => [], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], danger: [{ type: i0.Input, args: [{ isSignal: true, alias: "danger", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], confirmMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmMessage", required: false }] }], confirmTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmTitle", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], confirmCancelLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmCancelLabel", required: false }] }], confirmTone: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmTone", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
9644
9911
 
9645
- const BEM_BLOCK$r = 'rt-menu';
9646
- const POSITION_BELOW_END = {
9647
- originX: 'end',
9648
- originY: 'bottom',
9649
- overlayX: 'end',
9650
- overlayY: 'top',
9651
- offsetY: 4,
9652
- };
9653
- const POSITION_ABOVE_END = {
9654
- originX: 'end',
9655
- originY: 'top',
9656
- overlayX: 'end',
9657
- overlayY: 'bottom',
9658
- offsetY: -4,
9659
- };
9660
- const POSITION_BELOW_START = {
9661
- originX: 'start',
9662
- originY: 'bottom',
9663
- overlayX: 'start',
9664
- overlayY: 'top',
9665
- offsetY: 4,
9666
- };
9667
- const POSITION_ABOVE_START = {
9668
- originX: 'start',
9669
- originY: 'top',
9670
- overlayX: 'start',
9671
- overlayY: 'bottom',
9672
- offsetY: -4,
9673
- };
9674
- /**
9675
- * Dropdown-меню действий: icon-only триггер «…» + плавающая панель с пунктами
9676
- * `rt-menu-item`, спроецированными через `<ng-content>`. Типовое применение —
9677
- * per-row действия в конце строки `rt-table` (см. `[showRowActions]`).
9678
- *
9679
- * Движок — CDK `cdkConnectedOverlay` с прозрачным backdrop: авто-flip у края
9680
- * viewport (4 fallback-позиции), backdrop-click и `(detach)` закрывают, Escape
9681
- * закрывает через `(overlayKeydown)`. Панель не режется `overflow: hidden`
9682
- * предками (таблицы, карточки), т.к. рендерится в overlay-контейнере.
9683
- *
9684
- * Выбор пункта закрывает меню: `rt-menu-item` всплывающе диспатчит DOM-событие
9685
- * `rtMenuSelect`, которое панель ловит, — это переживает content-projection.
9686
- *
9687
- * @example
9688
- * ```html
9689
- * <rt-menu ariaLabel="Действия">
9690
- * <rt-menu-item icon="ico-edit" label="Редактировать" (selected)="edit()" />
9691
- * <rt-menu-item icon="ico-trash" label="Удалить" danger (selected)="remove()" />
9692
- * </rt-menu>
9693
- * ```
9694
- */
9695
- class RtMenuComponent {
9696
- constructor() {
9697
- this.#t_uiActions = rtKitLabel('uiActions');
9698
- this.isOpen = signal(false, /* @ts-ignore */
9699
- ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
9700
- this.ariaText = computed(() => this.ariaLabel() || this.#t_uiActions(), /* @ts-ignore */
9701
- ...(ngDevMode ? [{ debugName: "ariaText" }] : /* istanbul ignore next */ []));
9702
- /** Fallback-цепочка позиций панели: первой идёт сторона по `align`. */
9703
- this.overlayPositions = computed(() => this.align() === 'end'
9704
- ? [POSITION_BELOW_END, POSITION_ABOVE_END, POSITION_BELOW_START, POSITION_ABOVE_START]
9705
- : [POSITION_BELOW_START, POSITION_ABOVE_START, POSITION_BELOW_END, POSITION_ABOVE_END], /* @ts-ignore */
9706
- ...(ngDevMode ? [{ debugName: "overlayPositions" }] : /* istanbul ignore next */ []));
9707
- /** Иконка триггера. Default `ellipsis-h` («…» — три точки в конце строки). */
9708
- this.icon = input('ellipsis-h', /* @ts-ignore */
9709
- ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
9710
- /** ARIA-метка триггера (icon-only кнопка) + текст tooltip'а. */
9711
- /** Пусто — берётся переведённая подпись по умолчанию */
9712
- this.ariaLabel = input('', /* @ts-ignore */
9713
- ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
9714
- /** Сторона раскрытия панели относительно триггера. */
9715
- this.align = input('end', /* @ts-ignore */
9716
- ...(ngDevMode ? [{ debugName: "align" }] : /* istanbul ignore next */ []));
9717
- /** Триггер отключён — меню не открыть. */
9718
- this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
9719
- }
9720
- #t_uiActions;
9721
- toggle(event) {
9722
- // Клик по триггеру не должен активировать строку таблицы под ним.
9723
- event.stopPropagation();
9724
- this.isOpen.update((open) => !open);
9725
- }
9726
- close() {
9727
- this.isOpen.set(false);
9728
- }
9729
- onKeydown(event) {
9730
- if (event.key === 'Escape') {
9731
- this.close();
9732
- }
9733
- }
9734
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
9735
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: RtMenuComponent, isStandalone: true, selector: "rt-menu", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "align", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.rt-menu--open": "isOpen()" }, classAttribute: "rt-menu" }, ngImport: i0, template: "<rt-icon-button\n cdkOverlayOrigin\n qa-dataid=\"menu-trigger\"\n #trigger=\"cdkOverlayOrigin\"\n variant=\"ghost\"\n [icon]=\"icon()\"\n [ariaLabel]=\"ariaText()\"\n [tooltip]=\"ariaText()\"\n [disabled]=\"disabled()\"\n (clicked)=\"toggle($event)\" />\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n (overlayKeydown)=\"onKeydown($event)\">\n <ng-container rtBlock=\"rt-menu\">\n <div rtElem=\"panel\" qa-dataid=\"menu-panel\" role=\"menu\" (rtMenuSelect)=\"close()\">\n <ng-content />\n </div>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu{display:inline-flex;align-items:center;justify-content:center}.rt-menu__panel{--rt-menu-panel-radius: var(--rt-radius-md);--rt-menu-panel-border-width: var(--rt-border-width-thin);--rt-menu-panel-shadow: var(--rt-shadow-lg);display:flex;min-width:180px;flex-direction:column;padding:var(--rt-space-xs);border:var(--rt-menu-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-menu-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-menu-panel-shadow)}}\n"], dependencies: [{ kind: "ngmodule", type:
9736
- // @angular/cdk
9737
- OverlayModule }, { kind: "directive", type: i1$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation", "cdkConnectedOverlayUsePopover", "cdkConnectedOverlayMatchWidth", "cdkConnectedOverlay"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i1$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type:
9738
- // standalone components / directives
9739
- RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
9740
- }
9741
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtMenuComponent, decorators: [{
9742
- type: Component,
9743
- args: [{ selector: 'rt-menu', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
9744
- // @angular/cdk
9745
- OverlayModule,
9746
- // standalone components / directives
9747
- RtIconButtonComponent,
9748
- BlockDirective,
9749
- ElemDirective,
9750
- ], host: {
9751
- class: BEM_BLOCK$r,
9752
- '[class.rt-menu--open]': 'isOpen()',
9753
- }, template: "<rt-icon-button\n cdkOverlayOrigin\n qa-dataid=\"menu-trigger\"\n #trigger=\"cdkOverlayOrigin\"\n variant=\"ghost\"\n [icon]=\"icon()\"\n [ariaLabel]=\"ariaText()\"\n [tooltip]=\"ariaText()\"\n [disabled]=\"disabled()\"\n (clicked)=\"toggle($event)\" />\n\n<ng-template\n cdkConnectedOverlay\n cdkConnectedOverlayHasBackdrop\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n [cdkConnectedOverlayOrigin]=\"trigger\"\n [cdkConnectedOverlayOpen]=\"isOpen()\"\n [cdkConnectedOverlayPositions]=\"overlayPositions()\"\n (backdropClick)=\"close()\"\n (detach)=\"close()\"\n (overlayKeydown)=\"onKeydown($event)\">\n <ng-container rtBlock=\"rt-menu\">\n <div rtElem=\"panel\" qa-dataid=\"menu-panel\" role=\"menu\" (rtMenuSelect)=\"close()\">\n <ng-content />\n </div>\n </ng-container>\n</ng-template>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-menu{display:inline-flex;align-items:center;justify-content:center}.rt-menu__panel{--rt-menu-panel-radius: var(--rt-radius-md);--rt-menu-panel-border-width: var(--rt-border-width-thin);--rt-menu-panel-shadow: var(--rt-shadow-lg);display:flex;min-width:180px;flex-direction:column;padding:var(--rt-space-xs);border:var(--rt-menu-panel-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-menu-panel-radius);background:var(--rt-color-bg-surface);box-shadow:var(--rt-menu-panel-shadow)}}\n"] }]
9754
- }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "align", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
9755
-
9756
9912
  const BEM_BLOCK$q = 'rt-message';
9757
9913
  /**
9758
9914
  * Alert / banner для уведомлений с 6 severities и default icon mapping.
@@ -10550,7 +10706,7 @@ class RtPhotoViewerComponent {
10550
10706
  track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });
10551
10707
  }
10552
10708
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtPhotoViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10553
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtPhotoViewerComponent, isStandalone: true, selector: "rt-photo-viewer", host: { attributes: { "role": "dialog", "aria-modal": "true", "tabindex": "-1" }, listeners: { "keydown.escape": "close()", "keydown.arrowLeft": "move(-1)", "keydown.arrowRight": "move(1)" }, classAttribute: "rt-photo-viewer" }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-color-text-inverse);--rt-icon-button-color-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);border-radius:var(--rt-photo-viewer-control-radius);background:color-mix(in srgb,var(--rt-color-bg-inverse) 55%,transparent)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"], dependencies: [{ kind: "directive", type:
10709
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtPhotoViewerComponent, isStandalone: true, selector: "rt-photo-viewer", host: { attributes: { "role": "dialog", "aria-modal": "true", "tabindex": "-1" }, listeners: { "keydown.escape": "close()", "keydown.arrowLeft": "move(-1)", "keydown.arrowRight": "move(1)" }, classAttribute: "rt-photo-viewer" }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"], dependencies: [{ kind: "directive", type:
10554
10710
  // angular
10555
10711
  CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
10556
10712
  // rt-tools
@@ -10581,7 +10737,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
10581
10737
  '(keydown.escape)': 'close()',
10582
10738
  '(keydown.arrowLeft)': 'move(-1)',
10583
10739
  '(keydown.arrowRight)': 'move(1)',
10584
- }, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-color-text-inverse);--rt-icon-button-color-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);border-radius:var(--rt-photo-viewer-control-radius);background:color-mix(in srgb,var(--rt-color-bg-inverse) 55%,transparent)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"] }]
10740
+ }, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"] }]
10585
10741
  }], ctorParameters: () => [], propDecorators: { track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
10586
10742
 
10587
10743
  /** Сколько всего страниц. Пустой список — одна страница, листать нечего. */
@@ -10692,7 +10848,7 @@ class RtPaginationComponent {
10692
10848
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtPaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
10693
10849
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtPaginationComponent, isStandalone: true, selector: "rt-pagination", inputs: { pageModel: { classPropertyName: "pageModel", publicName: "pageModel", isSignal: true, isRequired: true, transformFunction: null }, perPageOptions: { classPropertyName: "perPageOptions", publicName: "perPageOptions", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", perPageChange: "perPageChange" }, host: { properties: { "style.display": "isVisible() ? null : 'none'" }, classAttribute: "rt-pagination" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-pagination\">\n <!--\n \u0412\u0435\u0442\u043A\u0438 \u00AB\u043D\u0438\u0447\u0435\u0433\u043E \u043D\u0435 \u043D\u0430\u0439\u0434\u0435\u043D\u043E\u00BB \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442 \u043D\u0430\u043C\u0435\u0440\u0435\u043D\u043D\u043E: \u043F\u0443\u0441\u0442\u043E\u0439 \u043D\u0430\u0431\u043E\u0440 \u043F\u0440\u044F\u0447\u0435\u0442 \u0432\u0435\u0441\u044C\n \u0431\u0430\u0440 \u0446\u0435\u043B\u0438\u043A\u043E\u043C (`isVisible`), \u0442\u0430\u043A \u0447\u0442\u043E \u0434\u043E \u044D\u0442\u043E\u0439 \u0441\u0442\u0440\u043E\u043A\u0438 \u0434\u0435\u043B\u043E \u043D\u0435 \u0434\u043E\u0445\u043E\u0434\u0438\u0442. \u041F\u043E\u043A\u0430\n \u0442\u0430\u043A\u0430\u044F \u0432\u0435\u0442\u043A\u0430 \u0442\u0443\u0442 \u0432\u0438\u0441\u0435\u043B\u0430, \u043E\u043D\u0430 \u0447\u0438\u0442\u0430\u043B\u0430\u0441\u044C \u043A\u0430\u043A \u0440\u0430\u0431\u043E\u0447\u0435\u0435 \u0441\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435, \u0430 \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C\n \u0435\u0451 \u0431\u044B\u043B\u043E \u043D\u0435\u043B\u044C\u0437\u044F \u043D\u0438 \u043F\u0440\u0438 \u043A\u0430\u043A\u0438\u0445 \u0432\u0445\u043E\u0434\u0430\u0445.\n -->\n <span rtElem=\"range\" qa-dataid=\"pagination-range\">\n {{ rangeLabel() }}\n </span>\n\n @if (pageItems().length) {\n <nav rtElem=\"compact\" qa-dataid=\"pagination-compact\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-compact-prev\"\n icon=\"chevron-left\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n <span rtElem=\"compact-label\" qa-dataid=\"pagination-compact-label\">\n {{ pageLabel() }}\n </span>\n\n <rt-icon-button\n qa-dataid=\"pagination-compact-next\"\n icon=\"chevron-right\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n\n <nav rtElem=\"nav\" qa-dataid=\"pagination-nav\" [attr.aria-label]=\"t().uiPagination\">\n <rt-icon-button\n qa-dataid=\"pagination-prev\"\n icon=\"chevron-left\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().uiPrevPage\"\n [tooltip]=\"t().uiBack\"\n [disabled]=\"isFirst() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber - 1)\" />\n\n @for (item of pageItems(); track $index) {\n @if (item === 'gap') {\n <span rtElem=\"gap\" aria-hidden=\"true\">\u2026</span>\n } @else {\n <button\n rtElem=\"page\"\n qa-dataid=\"pagination-page\"\n type=\"button\"\n [attr.data-page]=\"item\"\n [rtMod]=\"{ current: item === pageModel().pageNumber }\"\n [attr.aria-current]=\"item === pageModel().pageNumber ? 'page' : null\"\n [disabled]=\"loading()\"\n (click)=\"goTo(item)\">\n {{ item }}\n </button>\n }\n }\n\n <rt-icon-button\n qa-dataid=\"pagination-next\"\n icon=\"chevron-right\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().uiNextPage\"\n [tooltip]=\"t().uiForward\"\n [disabled]=\"isLast() || loading()\"\n (clicked)=\"goTo(pageModel().pageNumber + 1)\" />\n </nav>\n }\n\n <div rtElem=\"per-page\">\n <span rtElem=\"per-page-label\">{{ t().uiPerPage }}</span>\n <rt-select\n qa-dataid=\"pagination-per-page\"\n [ariaLabel]=\"t().uiItemsPerPage\"\n [options]=\"perPageSelectOptions()\"\n [clearable]=\"false\"\n [ngModel]=\"pageModel().pageSize\"\n [ngModelOptions]=\"{ standalone: true }\"\n (selectionChange)=\"onPerPage($event)\" />\n </div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-pagination{--rt-pagination-page-radius: var(--rt-radius-md);--rt-pagination-page-border-width: var(--rt-border-width-thin);--rt-pagination-focus-shadow: var(--rt-shadow-focus-ring);--rt-pagination-transition-duration: var(--rt-duration-fast);display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-xs);container-type:inline-size;gap:var(--rt-space-md)}.rt-pagination__range{color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);white-space:nowrap}.rt-pagination__nav{display:flex;align-items:center;gap:var(--rt-space-xs)}.rt-pagination__compact{display:none;align-items:center;gap:var(--rt-space-xs)}.rt-pagination__compact-label{color:var(--rt-color-text-primary);font-size:var(--rt-text-sm);white-space:nowrap}.rt-pagination__page{min-width:var(--rt-space-xl);height:var(--rt-space-xl);padding:0 var(--rt-space-sm);border:var(--rt-pagination-page-border-width) solid var(--rt-color-border-default);border-radius:var(--rt-pagination-page-radius);background:transparent;color:var(--rt-color-text-primary);cursor:pointer;transition:background-color var(--rt-pagination-transition-duration) ease,border-color var(--rt-pagination-transition-duration) ease,color var(--rt-pagination-transition-duration) ease}.rt-pagination__page:hover:not(:disabled,.rt-pagination__page--current){border-color:var(--rt-color-border-strong);background:var(--rt-color-bg-surface-subtle)}.rt-pagination__page:focus-visible{box-shadow:var(--rt-pagination-focus-shadow);outline:2px solid transparent;outline-offset:2px}.rt-pagination__page--current{border-color:var(--rt-color-border-strong);background:var(--rt-color-bg-surface-subtle);font-weight:var(--rt-font-weight-semibold)}.rt-pagination__page:disabled{cursor:default;opacity:.6}.rt-pagination__gap{padding:0 var(--rt-space-xs);color:var(--rt-color-text-muted)}.rt-pagination__per-page{display:flex;align-items:center;gap:var(--rt-space-sm)}.rt-pagination__per-page-label{color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}@container (max-width: 460px){.rt-pagination__nav,.rt-pagination__range{display:none}.rt-pagination__compact{display:flex}}}\n"], dependencies: [{ kind: "ngmodule", type:
10694
10850
  // Angular
10695
- FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
10851
+ FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
10696
10852
  // standalone components / directives
10697
10853
  RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtSelectComponent, selector: "rt-select", inputs: ["options", "placeholder", "iconLeft", "filter", "filterPlaceholder"], outputs: ["selectionChange"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
10698
10854
  }
@@ -11274,6 +11430,105 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
11274
11430
  }]
11275
11431
  }], propDecorators: { rtTableCardRowType: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtTableCardRowType", required: false }] }] } });
11276
11432
 
11433
+ /** Имя внутренней «…»-колонки действий, добавляемой при `[showRowActions]`. */
11434
+ const RT_TABLE_ROW_ACTIONS_COLUMN = 'rtRowActions';
11435
+ /**
11436
+ * Колонки конфига без применённых настроек пользователя — источник для кнопки
11437
+ * «Сбросить» в панели. Закрытая колонка видима всегда: скрыть её панель не даёт.
11438
+ */
11439
+ function defaultColumnItems(config) {
11440
+ return config.map((column) => ({
11441
+ ...column,
11442
+ hidden: column.locked === true ? false : column.hidden === true,
11443
+ }));
11444
+ }
11445
+ /**
11446
+ * Разрешённые колонки настраиваемой таблицы: конфиг в применённом порядке с текущим
11447
+ * признаком скрытия.
11448
+ *
11449
+ * Порядок берётся из сохранённых настроек, а колонки, появившиеся в конфиге после
11450
+ * сохранения, встают в конец: настройки, снятые с прежнего состава, иначе прятали бы
11451
+ * новую колонку молча. Настроек нет — порядок и скрытие берутся из самого конфига.
11452
+ */
11453
+ function resolveColumns(config, settings) {
11454
+ if (config.length === 0) {
11455
+ return [];
11456
+ }
11457
+ const byKey = new Map(config.map((column) => [column.key, column]));
11458
+ const orderedKeys = settings
11459
+ ? [
11460
+ ...settings.order.filter((key) => byKey.has(key)),
11461
+ ...config
11462
+ .filter((column) => !settings.order.includes(column.key))
11463
+ .map((column) => column.key),
11464
+ ]
11465
+ : config.map((column) => column.key);
11466
+ const hiddenKeys = new Set(settings
11467
+ ? settings.hidden
11468
+ : config
11469
+ .filter((column) => column.hidden === true)
11470
+ .map((column) => column.key));
11471
+ return orderedKeys
11472
+ .map((key) => byKey.get(key))
11473
+ .filter((column) => column !== undefined)
11474
+ .map((column) => ({
11475
+ ...column,
11476
+ hidden: column.locked === true ? false : hiddenKeys.has(column.key),
11477
+ }));
11478
+ }
11479
+ /**
11480
+ * Имена колонок для строк CDK: у настраиваемой таблицы — видимые в применённом порядке,
11481
+ * у остальных — то, что назвал вход `[columns]`. Колонка действий встаёт последней, когда
11482
+ * действия строки включены.
11483
+ */
11484
+ function displayedColumnKeys(resolved, legacyColumns, configurable, showRowActions) {
11485
+ const base = configurable
11486
+ ? resolved
11487
+ .filter((column) => !column.hidden)
11488
+ .map((column) => column.key)
11489
+ : [...legacyColumns];
11490
+ return showRowActions ? [...base, RT_TABLE_ROW_ACTIONS_COLUMN] : [...base];
11491
+ }
11492
+ /**
11493
+ * Настройки панели, приведённые к конфигу: ключи закрытых колонок из списка скрытых
11494
+ * вычищаются. Панель их не предлагает, но настройки приезжают и из хранилища — снятые с
11495
+ * той поры, когда колонка закрытой ещё не была.
11496
+ */
11497
+ function withoutLockedHidden(config, settings) {
11498
+ const lockedKeys = new Set(config
11499
+ .filter((column) => column.locked === true)
11500
+ .map((column) => column.key));
11501
+ return {
11502
+ order: [...settings.order],
11503
+ hidden: settings.hidden.filter((key) => !lockedKeys.has(key)),
11504
+ };
11505
+ }
11506
+
11507
+ /**
11508
+ * Строки, которые можно показать карточками. Источник данных таблицы бывает массивом,
11509
+ * потоком и своим объектом, а карточки строятся только по массиву: у остальных состав строк
11510
+ * известен лишь самой таблице, и брать его отсюда нечем.
11511
+ */
11512
+ function cardRowsOf(dataSource) {
11513
+ return Array.isArray(dataSource) ? dataSource : [];
11514
+ }
11515
+ /**
11516
+ * Поля авто-карточки: видимые колонки, кроме колонки действий, с подписью из конфига и
11517
+ * шаблоном ячейки из объявления колонки. Колонка без шаблона ячейки пропускается — рисовать
11518
+ * её значение нечем.
11519
+ */
11520
+ function cardColumnsOf(displayedColumns, columnDefs, columnsConfig) {
11521
+ const defsByName = new Map(columnDefs.map((def) => [def.name, def]));
11522
+ const labelByKey = new Map(columnsConfig.map((column) => [column.key, column.label]));
11523
+ return displayedColumns
11524
+ .filter((key) => key !== RT_TABLE_ROW_ACTIONS_COLUMN)
11525
+ .map((key) => {
11526
+ const cell = defsByName.get(key)?.cell?.template;
11527
+ return cell === undefined ? null : { key, cell, label: labelByKey.get(key) ?? key };
11528
+ })
11529
+ .filter((column) => column !== null);
11530
+ }
11531
+
11277
11532
  /**
11278
11533
  * Захватывает `<ng-template rtTableRowActions let-row>` — содержимое «…»-меню
11279
11534
  * действий строки. `rt-table` (`[showRowActions]`) рендерит этот шаблон в конце
@@ -11356,6 +11611,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
11356
11611
  args: [{ name: 'rtRowHasActions' }]
11357
11612
  }] });
11358
11613
 
11614
+ /** Ключ настроек одной таблицы в хранилище браузера. */
11615
+ function settingsKey(tableId) {
11616
+ return `${ERtStorageKeys.TableColumnsPrefix}${tableId}`;
11617
+ }
11618
+ /**
11619
+ * Хранение настроек колонок одной таблицы.
11620
+ *
11621
+ * Запись объявлена одним потоком, а не подпиской в методе: запросы приходят по одному на
11622
+ * каждое движение панели настроек, и каждый из них — своя независимая запись. Зависимости
11623
+ * приходят доводами, потому что заводится это в конструкторе компонента, где внедрение уже
11624
+ * доступно, а сам класс службой не объявлен — он живёт ровно столько, сколько таблица.
11625
+ */
11626
+ class RtTableSettingsPersistence {
11627
+ #storage;
11628
+ #requestsSource = new Subject();
11629
+ constructor(storage, destroyRef) {
11630
+ this.#storage = storage;
11631
+ this.#requestsSource
11632
+ .pipe(mergeMap((request) => this.#storage.set(settingsKey(request.tableId), request.settings)), takeUntilDestroyed(destroyRef))
11633
+ .subscribe();
11634
+ }
11635
+ /** Сохранённые настройки этой таблицы. Записи может не быть — тогда применять нечего. */
11636
+ load(tableId) {
11637
+ return this.#storage.get(settingsKey(tableId));
11638
+ }
11639
+ /** Кладёт настройки в очередь записи: сама запись идёт объявленным потоком. */
11640
+ save(tableId, settings) {
11641
+ this.#requestsSource.next({ tableId, settings });
11642
+ }
11643
+ }
11644
+
11359
11645
  /**
11360
11646
  * Реестр настраиваемых таблиц — мост между `rt-table` (shared UI) и route-асайдом
11361
11647
  * настроек колонок, которые живут в разных ветках дерева и не видят друг друга
@@ -11428,15 +11714,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
11428
11714
  }] });
11429
11715
 
11430
11716
  const BEM_BLOCK$c = 'rt-table';
11431
- /** Ключ настроек одной таблицы в IndexedDB. */
11432
- function settingsKey(tableId) {
11433
- return `${ERtStorageKeys.TableColumnsPrefix}${tableId}`;
11434
- }
11435
11717
  const DEFAULT_SKELETON_ROWS = 5;
11436
11718
  /** Ключ подписи пустой таблицы: язык известен только после старта приложения */
11437
11719
  const DEFAULT_EMPTY_KEY = 'uiNoRows';
11438
- /** Имя внутренней «…»-колонки действий, добавляемой при `[showRowActions]`. */
11439
- const RT_TABLE_ROW_ACTIONS_COLUMN = 'rtRowActions';
11440
11720
  /**
11441
11721
  * Таблица — стилизованная обёртка над `cdk-table` (`@angular/cdk/table`).
11442
11722
  *
@@ -11463,36 +11743,11 @@ const RT_TABLE_ROW_ACTIONS_COLUMN = 'rtRowActions';
11463
11743
  * используется — explicit input проще и testable.
11464
11744
  */
11465
11745
  class RtTableComponent extends CdkTable {
11466
- #t_uiNoRows;
11467
- #registry;
11468
- #settingsStorage;
11469
- #destroyRef;
11470
- #breakpoints;
11471
- #dataSourceVersion;
11472
- /**
11473
- * Выбранная сортировка. Заводится от входа `[sort]` и переключается заголовками:
11474
- * таблица показывает порядок и без потребителя, который его слушает.
11475
- */
11476
- #sort;
11477
- #persistSettingsSource;
11478
- /**
11479
- * Пользовательские настройки колонок (порядок + скрытые), применённые поверх
11480
- * `[columnsConfig]`. `null` — используются дефолты из конфига. Мутируется из
11481
- * панели настроек через `applyColumnSettings()`.
11482
- */
11483
- #columnSettings;
11484
- /**
11485
- * Дефолтные колонки из конфига (порядок массива + `config.hidden`), без применённых
11486
- * пользовательских настроек — источник для кнопки «Сбросить» в панели.
11487
- */
11488
- #defaultColumns;
11489
- /** tableId для персиста настроек (ключ порта). `null` — таблица не настраиваемая (нет tableId/config). */
11490
- #persistableTableId;
11491
11746
  constructor() {
11492
- super();
11747
+ super(...arguments);
11493
11748
  this.#t_uiNoRows = rtKitLabel(DEFAULT_EMPTY_KEY);
11494
11749
  this.#registry = inject(RtTableSettingsRegistry);
11495
- this.#settingsStorage = inject(IDBStorageService);
11750
+ this.#settings = new RtTableSettingsPersistence(inject(IDBStorageService), inject(DestroyRef));
11496
11751
  this.#destroyRef = inject(DestroyRef);
11497
11752
  this.#breakpoints = inject(BreakpointsService);
11498
11753
  this.#dataSourceVersion = signal(0, /* @ts-ignore */
@@ -11503,22 +11758,11 @@ class RtTableComponent extends CdkTable {
11503
11758
  */
11504
11759
  this.#sort = linkedSignal(() => this.sort(), /* @ts-ignore */
11505
11760
  ...(ngDevMode ? [{ debugName: "#sort" }] : /* istanbul ignore next */ []));
11506
- this.#persistSettingsSource = new Subject();
11507
- /**
11508
- * Пользовательские настройки колонок (порядок + скрытые), применённые поверх
11509
- * `[columnsConfig]`. `null` — используются дефолты из конфига. Мутируется из
11510
- * панели настроек через `applyColumnSettings()`.
11511
- */
11761
+ /** Настройки колонок, применённые поверх `[columnsConfig]`; `null` — умолчания конфига. */
11512
11762
  this.#columnSettings = signal(null, /* @ts-ignore */
11513
11763
  ...(ngDevMode ? [{ debugName: "#columnSettings" }] : /* istanbul ignore next */ []));
11514
- /**
11515
- * Дефолтные колонки из конфига (порядок массива + `config.hidden`), без применённых
11516
- * пользовательских настроек — источник для кнопки «Сбросить» в панели.
11517
- */
11518
- this.#defaultColumns = computed(() => this.columnsConfig().map((c) => ({
11519
- ...c,
11520
- hidden: c.locked === true ? false : c.hidden === true,
11521
- })), /* @ts-ignore */
11764
+ /** Колонки конфига без настроек пользователя — с них панель начинает после сброса. */
11765
+ this.#defaultColumns = computed(() => defaultColumnItems(this.columnsConfig()), /* @ts-ignore */
11522
11766
  ...(ngDevMode ? [{ debugName: "#defaultColumns" }] : /* istanbul ignore next */ []));
11523
11767
  /** tableId для персиста настроек (ключ порта). `null` — таблица не настраиваемая (нет tableId/config). */
11524
11768
  this.#persistableTableId = computed(() => {
@@ -11560,36 +11804,16 @@ class RtTableComponent extends CdkTable {
11560
11804
  this.showCards = computed(() => this.cards() && this.isNarrow(), /* @ts-ignore */
11561
11805
  ...(ngDevMode ? [{ debugName: "showCards" }] : /* istanbul ignore next */ []));
11562
11806
  /**
11563
- * Строки для card-рендера. CdkTable хранит `dataSource` как array | Observable |
11564
- * DataSource — карточки строим только для массива (client-side pattern);
11565
- * для остальных типов пусто (карточки не поддерживаются). `#dataSourceVersion`
11566
- * инвалидирует computed при смене источника.
11807
+ * Строки для карточек узкого экрана. Признак `#dataSourceVersion` пересчитывает список
11808
+ * при смене источника: CDK хранит его полем, а не сигналом.
11567
11809
  */
11568
11810
  this.cardRows = computed(() => {
11569
11811
  this.#dataSourceVersion();
11570
- const ds = this.dataSource;
11571
- return Array.isArray(ds) ? ds : [];
11812
+ return cardRowsOf(this.dataSource);
11572
11813
  }, /* @ts-ignore */
11573
11814
  ...(ngDevMode ? [{ debugName: "cardRows" }] : /* istanbul ignore next */ []));
11574
- /**
11575
- * Поля авто-карточки: видимые колонки (кроме «…»-действий) с лейблом из
11576
- * `columnsConfig` (fallback — ключ колонки) и cell-шаблоном из `cdkColumnDef`.
11577
- * Колонки без cell-шаблона пропускаются.
11578
- */
11579
- this.cardColumns = computed(() => {
11580
- const defsByName = new Map(this.columnDefs().map((d) => [d.name, d]));
11581
- const labelByKey = new Map(this.columnsConfig().map((c) => [c.key, c.label]));
11582
- return this.displayedColumns()
11583
- .filter((key) => key !== RT_TABLE_ROW_ACTIONS_COLUMN)
11584
- .map((key) => {
11585
- const cell = defsByName.get(key)?.cell?.template;
11586
- if (cell === undefined) {
11587
- return null;
11588
- }
11589
- return { key, cell, label: labelByKey.get(key) ?? key };
11590
- })
11591
- .filter((c) => c !== null);
11592
- }, /* @ts-ignore */
11815
+ /** Поля авто-карточки узкого экрана: видимые колонки с подписью и шаблоном ячейки. */
11816
+ this.cardColumns = computed(() => cardColumnsOf(this.displayedColumns(), this.columnDefs(), this.columnsConfig()), /* @ts-ignore */
11593
11817
  ...(ngDevMode ? [{ debugName: "cardColumns" }] : /* istanbul ignore next */ []));
11594
11818
  /** ContentChild template для кастом empty-placeholder'а. Опционален; fallback — emptyMessage. */
11595
11819
  this.emptyTemplate = contentChild('rtTableEmpty', { ...(ngDevMode ? { debugName: "emptyTemplate" } : /* istanbul ignore next */ {}), read: TemplateRef });
@@ -11659,36 +11883,7 @@ class RtTableComponent extends CdkTable {
11659
11883
  * читает этот список для отрисовки; из него же строится `displayedColumns()`.
11660
11884
  * Пустой при отсутствии `[columnsConfig]` (legacy-режим).
11661
11885
  */
11662
- this.resolvedColumns = computed(() => {
11663
- const config = this.columnsConfig();
11664
- if (config.length === 0) {
11665
- return [];
11666
- }
11667
- const settings = this.#columnSettings();
11668
- const byKey = new Map(config.map((c) => [c.key, c]));
11669
- // Порядок: сохранённый из настроек, затем новые колонки (появившиеся в
11670
- // конфиге после сохранения) — в конец.
11671
- const orderedKeys = settings
11672
- ? [
11673
- ...settings.order.filter((k) => byKey.has(k)),
11674
- ...config
11675
- .filter((c) => !settings.order.includes(c.key))
11676
- .map((c) => c.key),
11677
- ]
11678
- : config.map((c) => c.key);
11679
- const hiddenKeys = new Set(settings
11680
- ? settings.hidden
11681
- : config
11682
- .filter((c) => c.hidden === true)
11683
- .map((c) => c.key));
11684
- return orderedKeys
11685
- .map((key) => byKey.get(key))
11686
- .filter((c) => c !== undefined)
11687
- .map((c) => ({
11688
- ...c,
11689
- hidden: c.locked === true ? false : hiddenKeys.has(c.key),
11690
- }));
11691
- }, /* @ts-ignore */
11886
+ this.resolvedColumns = computed(() => resolveColumns(this.columnsConfig(), this.#columnSettings()), /* @ts-ignore */
11692
11887
  ...(ngDevMode ? [{ debugName: "resolvedColumns" }] : /* istanbul ignore next */ []));
11693
11888
  /**
11694
11889
  * Колонки для `*cdkHeaderRowDef`/`*cdkRowDef`. При заданном `[columnsConfig]` —
@@ -11697,22 +11892,14 @@ class RtTableComponent extends CdkTable {
11697
11892
  * template-ref: `<rt-table #t="rtTable" ...>` →
11698
11893
  * `*cdkRowDef="let row; columns: t.displayedColumns()"`.
11699
11894
  */
11700
- this.displayedColumns = computed(() => {
11701
- const base = this.columnsConfig().length > 0
11702
- ? this.resolvedColumns()
11703
- .filter((c) => !c.hidden)
11704
- .map((c) => c.key)
11705
- : [...this.columns()];
11706
- return this.showRowActions() ? [...base, RT_TABLE_ROW_ACTIONS_COLUMN] : [...base];
11707
- }, /* @ts-ignore */
11895
+ this.displayedColumns = computed(() => displayedColumnKeys(this.resolvedColumns(), this.columns(), this.columnsConfig().length > 0, this.showRowActions()), /* @ts-ignore */
11708
11896
  ...(ngDevMode ? [{ debugName: "displayedColumns" }] : /* istanbul ignore next */ []));
11709
11897
  /** `true` когда таблица настраиваемая: есть и `[columnsConfig]`, и `[tableId]`. */
11710
11898
  this.canConfigure = computed(() => this.columnsConfig().length > 0 && this.tableId() !== null, /* @ts-ignore */
11711
11899
  ...(ngDevMode ? [{ debugName: "canConfigure" }] : /* istanbul ignore next */ []));
11712
11900
  /** Сколько skeleton-строк рендерить во время initial-load. Default 5. */
11713
11901
  this.skeletonRows = input(DEFAULT_SKELETON_ROWS, { ...(ngDevMode ? { debugName: "skeletonRows" } : /* istanbul ignore next */ {}), transform: numberAttribute });
11714
- /** Текст empty-placeholder'а если `rtTableEmpty` ContentChild template не задан. */
11715
- /** Пусто — берётся переведённая подпись по умолчанию */
11902
+ /** Подпись пустой таблицы; пусто — берётся переведённое умолчание. */
11716
11903
  this.emptyMessage = input('', /* @ts-ignore */
11717
11904
  ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
11718
11905
  /** Своя подпись пустой таблицы важнее умолчания */
@@ -11724,13 +11911,24 @@ class RtTableComponent extends CdkTable {
11724
11911
  /** Описание под заголовком дефолтного empty-placeholder'а. Опционально. */
11725
11912
  this.emptyDescription = input(null, /* @ts-ignore */
11726
11913
  ...(ngDevMode ? [{ debugName: "emptyDescription" }] : /* istanbul ignore next */ []));
11727
- // Персист настроек колонок объявлен одним конструкторным стримом:
11728
- // #persistSettings эмитит запрос, mergeMap подписывает store.set
11729
- // (fire-and-forget, каждый запрос — независимая запись, как раньше).
11730
- this.#persistSettingsSource
11731
- .pipe(mergeMap((request) => this.#settingsStorage.set(settingsKey(request.tableId), request.settings)), takeUntilDestroyed(this.#destroyRef))
11732
- .subscribe();
11733
11914
  }
11915
+ #t_uiNoRows;
11916
+ #registry;
11917
+ #settings;
11918
+ #destroyRef;
11919
+ #breakpoints;
11920
+ #dataSourceVersion;
11921
+ /**
11922
+ * Выбранная сортировка. Заводится от входа `[sort]` и переключается заголовками:
11923
+ * таблица показывает порядок и без потребителя, который его слушает.
11924
+ */
11925
+ #sort;
11926
+ /** Настройки колонок, применённые поверх `[columnsConfig]`; `null` — умолчания конфига. */
11927
+ #columnSettings;
11928
+ /** Колонки конфига без настроек пользователя — с них панель начинает после сброса. */
11929
+ #defaultColumns;
11930
+ /** tableId для персиста настроек (ключ порта). `null` — таблица не настраиваемая (нет tableId/config). */
11931
+ #persistableTableId;
11734
11932
  ngOnInit() {
11735
11933
  super.ngOnInit();
11736
11934
  // Регистрируем view-колонку действий до первого рендера строк, чтобы
@@ -11746,8 +11944,8 @@ class RtTableComponent extends CdkTable {
11746
11944
  // IndexedDB отдаёт в этом случае undefined, применять нечего.
11747
11945
  const persistedTableId = this.#persistableTableId();
11748
11946
  if (persistedTableId !== null) {
11749
- this.#settingsStorage
11750
- .get(settingsKey(persistedTableId))
11947
+ this.#settings
11948
+ .load(persistedTableId)
11751
11949
  .pipe(takeUntilDestroyed(this.#destroyRef))
11752
11950
  .subscribe((settings) => {
11753
11951
  if (settings !== undefined) {
@@ -11766,10 +11964,6 @@ class RtTableComponent extends CdkTable {
11766
11964
  }
11767
11965
  super.ngOnDestroy();
11768
11966
  }
11769
- /**
11770
- * Применяет новые настройки колонок (из панели настроек): порядок + скрытые.
11771
- * Locked-колонки принудительно остаются видимыми (их ключи вычищаются из hidden).
11772
- */
11773
11967
  /**
11774
11968
  * Переключает порядок по колонке: по возрастанию → по убыванию → без сортировки.
11775
11969
  * Выбранное состояние уходит наружу — выборку делает потребитель.
@@ -11780,19 +11974,11 @@ class RtTableComponent extends CdkTable {
11780
11974
  this.sortChange.emit(selected);
11781
11975
  }
11782
11976
  applyColumnSettings(settings) {
11783
- const lockedKeys = new Set(this.columnsConfig()
11784
- .filter((c) => c.locked === true)
11785
- .map((c) => c.key));
11786
- this.#columnSettings.set({
11787
- order: [...settings.order],
11788
- hidden: settings.hidden.filter((k) => !lockedKeys.has(k)),
11789
- });
11977
+ this.#columnSettings.set(withoutLockedHidden(this.columnsConfig(), settings));
11790
11978
  }
11791
11979
  /**
11792
- * Регистрирует настраиваемую таблицу в реестре: её колонки/дефолты (сигналы) +
11793
- * apply-callback (применяет настройки к таблице и персистит их в IndexedDB).
11794
- * Реестр — мост к route-асайду настроек, который
11795
- * открывается потребителем через навигацию.
11980
+ * Объявляет таблицу реестру: её колонки, умолчания и приём применения настроек. Реестр —
11981
+ * мост к панели настроек, которую потребитель открывает переходом по адресу.
11796
11982
  */
11797
11983
  #registerSettings() {
11798
11984
  const tableId = this.tableId();
@@ -11809,13 +11995,12 @@ class RtTableComponent extends CdkTable {
11809
11995
  };
11810
11996
  this.#registry.register(tableId, registration);
11811
11997
  }
11812
- /** Сохраняет настройки колонок через порт по `tableId` (запись — в конструкторном стриме). */
11998
+ /** Сохраняет настройки колонок у настраиваемой таблицы; у прочих сохранять нечего. */
11813
11999
  #persistSettings(settings) {
11814
12000
  const tableId = this.#persistableTableId();
11815
- if (tableId === null) {
11816
- return;
12001
+ if (tableId !== null) {
12002
+ this.#settings.save(tableId, settings);
11817
12003
  }
11818
- this.#persistSettingsSource.next({ tableId, settings });
11819
12004
  }
11820
12005
  /**
11821
12006
  * Пробрасывает `dataSource` в CdkTable и отдельно инвалидирует signals,
@@ -11843,7 +12028,7 @@ class RtTableComponent extends CdkTable {
11843
12028
  }
11844
12029
  return true;
11845
12030
  }
11846
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtTableComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12031
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtTableComponent, deps: null, target: i0.ɵɵFactoryTarget.Component }); }
11847
12032
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtTableComponent, isStandalone: true, selector: "rt-table, table[rt-table]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, cards: { classPropertyName: "cards", publicName: "cards", isSignal: true, isRequired: false, transformFunction: null }, clickable: { classPropertyName: "clickable", publicName: "clickable", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fetching: { classPropertyName: "fetching", publicName: "fetching", isSignal: true, isRequired: false, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: false, transformFunction: null }, columnsConfig: { classPropertyName: "columnsConfig", publicName: "columnsConfig", isSignal: true, isRequired: false, transformFunction: null }, tableId: { classPropertyName: "tableId", publicName: "tableId", isSignal: true, isRequired: false, transformFunction: null }, showRowActions: { classPropertyName: "showRowActions", publicName: "showRowActions", isSignal: true, isRequired: false, transformFunction: null }, rowHasActions: { classPropertyName: "rowHasActions", publicName: "rowHasActions", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, skeletonRows: { classPropertyName: "skeletonRows", publicName: "skeletonRows", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, emptyIcon: { classPropertyName: "emptyIcon", publicName: "emptyIcon", isSignal: true, isRequired: false, transformFunction: null }, emptyDescription: { classPropertyName: "emptyDescription", publicName: "emptyDescription", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sortChange: "sortChange" }, host: { properties: { "class.rt-table--density--compact": "density() === 'compact'", "class.rt-table--clickable": "clickable()", "class.rt-table--loading": "isInitialLoading()", "class.rt-table--fetching": "fetching()", "class.rt-table--empty": "isEmpty()", "class.rt-table--cards": "showCards()", "attr.role": "hostRole", "attr.aria-label": "ariaLabel()", "attr.aria-busy": "isInitialLoading() || fetching() ? 'true' : null" }, classAttribute: "rt-table" }, providers: [
11848
12033
  {
11849
12034
  provide: CDK_TABLE,
@@ -11901,7 +12086,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
11901
12086
  '[attr.aria-label]': 'ariaLabel()',
11902
12087
  '[attr.aria-busy]': "isInitialLoading() || fetching() ? 'true' : null",
11903
12088
  }, template: "<ng-container rtBlock=\"rt-table\">\n <ng-content select=\"caption\" />\n <ng-content select=\"colgroup, col\" />\n\n <ng-container cdkColumnDef=\"rtRowActions\">\n <th *cdkHeaderCellDef rtElem=\"actions-header\" cdk-header-cell aria-hidden=\"true\"></th>\n <td *cdkCellDef=\"let row\" rtElem=\"actions-cell\" qa-dataid=\"table-row-actions\" cdk-cell>\n @if (rowActions()?.template; as tpl) {\n @if (row | rtRowHasActions: rowHasActions()) {\n <div rtElem=\"actions-float\">\n <rt-menu>\n <ng-container [ngTemplateOutlet]=\"tpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n </rt-menu>\n </div>\n }\n }\n </td>\n </ng-container>\n\n <ng-container headerRowOutlet />\n\n @if (isInitialLoading()) {\n @for (i of skeletonRowIndexes(); track i) {\n <tr rtElem=\"skeleton-row\" qa-dataid=\"table-skeleton-row\">\n @for (col of displayedColumns(); track col) {\n <td rtElem=\"skeleton-cell\">\n <rt-skeleton rtElem=\"skeleton-bar\" />\n </td>\n }\n </tr>\n }\n }\n\n <ng-container rowOutlet />\n <ng-container noDataRowOutlet />\n <ng-container footerRowOutlet />\n\n @if (isEmpty()) {\n <tr rtElem=\"empty-row\" qa-dataid=\"table-empty-row\">\n <td rtElem=\"empty\" [attr.colspan]=\"displayedColumns().length || null\">\n @if (emptyTemplate(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n } @else {\n <rt-empty-state\n rtElem=\"empty-message\"\n qa-dataid=\"table-empty\"\n [icon]=\"emptyIcon()\"\n [title]=\"emptyText()\"\n [description]=\"emptyDescription()\" />\n }\n </td>\n </tr>\n }\n\n @if (showCards()) {\n <div rtElem=\"cards\" qa-dataid=\"table-cards\" role=\"list\">\n @if (isInitialLoading()) {\n @for (i of skeletonRowIndexes(); track i) {\n <article rtElem=\"card\" qa-dataid=\"table-skeleton-card\" [rtMod]=\"{ skeleton: true }\">\n <rt-skeleton rtElem=\"card-skeleton-bar\" />\n <rt-skeleton rtElem=\"card-skeleton-bar\" />\n </article>\n }\n } @else if (isEmpty()) {\n @if (emptyTemplate(); as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\" />\n } @else {\n <rt-empty-state\n rtElem=\"empty-message\"\n qa-dataid=\"table-empty\"\n [icon]=\"emptyIcon()\"\n [title]=\"emptyText()\"\n [description]=\"emptyDescription()\" />\n }\n } @else {\n @for (row of cardRows(); track row) {\n <article rtElem=\"card\" qa-dataid=\"table-card\" role=\"listitem\">\n @if (cardTemplate()?.template; as tpl) {\n <ng-container [ngTemplateOutlet]=\"tpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n } @else {\n @for (col of cardColumns(); track col.key) {\n <div rtElem=\"card-field\" qa-dataid=\"table-card-field\" [attr.data-key]=\"col.key\">\n <span rtElem=\"card-label\" qa-dataid=\"table-card-label\">{{ col.label }}</span>\n <div rtElem=\"card-value\" qa-dataid=\"table-card-value\">\n <ng-container [ngTemplateOutlet]=\"col.cell\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n </div>\n </div>\n }\n @if (rowActions()?.template; as tpl) {\n @if (row | rtRowHasActions: rowHasActions()) {\n <div rtElem=\"card-actions\" qa-dataid=\"table-card-actions\">\n <rt-menu>\n <ng-container [ngTemplateOutlet]=\"tpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n </rt-menu>\n </div>\n }\n }\n }\n </article>\n }\n }\n </div>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-table{--rt-table-border-width: var(--rt-border-width-thin);--rt-table-card-radius: var(--rt-radius-md);position:relative;display:table;width:100%;background:var(--rt-color-bg-surface);border-collapse:collapse;color:var(--rt-color-text-primary)}.rt-table .cdk-header-cell,.rt-table .cdk-cell,.rt-table__skeleton-cell{position:relative;height:50px;padding:var(--rt-space-2) var(--rt-space-5);border-bottom:var(--rt-table-border-width) solid var(--rt-color-border-subtle);border-left:var(--rt-table-border-width) solid var(--rt-color-border-subtle);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium);text-align:start;vertical-align:middle}.rt-table .cdk-header-cell[data-align=right],.rt-table .cdk-cell[data-align=right],.rt-table__skeleton-cell[data-align=right],.rt-table .cdk-header-cell[data-align=right] input,.rt-table .cdk-cell[data-align=right] input,.rt-table__skeleton-cell[data-align=right] input{text-align:right}.rt-table .cdk-header-cell:first-child,.rt-table .cdk-cell:first-child,.rt-table__skeleton-cell:first-child{border-left:none}.rt-table .cdk-header-cell{border-top:var(--rt-table-border-width) solid var(--rt-color-border-subtle);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted)}.rt-table .cdk-cell{color:var(--rt-color-text-primary);overflow-wrap:anywhere}.rt-table__actions-header.cdk-header-cell{width:0;padding:0;border:none}.rt-table__actions-cell.cdk-cell{position:sticky;z-index:1;right:0;width:0;padding:0;border:none;background:transparent}.rt-table__actions-float{position:absolute;display:flex;height:100%;align-items:center;padding:0 var(--rt-space-2) 0 var(--rt-space-8);background:linear-gradient(to right,transparent 0%,var(--rt-color-bg-hover) 45%);inset:0 0 0 auto;pointer-events:none}.rt-table__actions-float .rt-menu{pointer-events:auto}@media(hover:hover){.rt-table__actions-cell.cdk-cell{visibility:hidden}.cdk-row:hover .rt-table__actions-cell.cdk-cell,.cdk-row:focus-within .rt-table__actions-cell.cdk-cell,.rt-table__actions-cell.cdk-cell:has(.rt-menu--open){visibility:visible}}.rt-table .cdk-row:hover{background:var(--rt-color-bg-hover)}.rt-table .cdk-row.rt-table__row--danger,.rt-table .cdk-row.rt-table__row--danger:hover{background:var(--rt-color-state-danger-bg)}.rt-table--clickable .cdk-row{cursor:pointer}.rt-table--clickable .cdk-row:focus-visible{outline:2px solid var(--rt-color-action-primary);outline-offset:-2px}.rt-table a{text-decoration:none}.rt-table--density--compact .cdk-header-cell,.rt-table--density--compact .cdk-cell,.rt-table--density--compact .rt-table__skeleton-cell{height:36px;padding:var(--rt-space-xs) var(--rt-space-sm)}.rt-table__skeleton-bar{display:block;width:80%;height:14px}.rt-table__empty-row{display:table-row}.rt-table__empty{padding:var(--rt-space-6) var(--rt-space-5);border-bottom:var(--rt-table-border-width) solid var(--rt-color-border-subtle);background:var(--rt-color-bg-surface);color:var(--rt-color-text-muted);text-align:center}.rt-table__empty-message{margin:0;font-size:var(--rt-text-sm)}.rt-table__overlay{position:absolute;z-index:1;display:grid;min-height:100%;inset:0;place-items:center;pointer-events:none}.rt-table__overlay-backdrop{position:absolute;background:var(--rt-color-bg-surface);inset:0;opacity:.72}.rt-table__overlay-spinner{position:sticky;z-index:1;top:50%;display:flex;align-items:center;justify-content:center;transform:translateY(-50%)}.rt-table__cards{display:none}.rt-table--cards{display:block;min-width:0;max-width:100%}.rt-table--cards .cdk-header-row,.rt-table--cards .cdk-row,.rt-table--cards .rt-table__skeleton-row,.rt-table--cards .rt-table__empty-row{display:none}.rt-table--cards .rt-table__cards{display:flex;flex-direction:column;gap:var(--rt-space-sm)}.rt-table__card{display:flex;flex-direction:column;padding:var(--rt-space-md);border:var(--rt-table-border-width) solid var(--rt-color-border-default);border-radius:var(--rt-table-card-radius);background:var(--rt-color-bg-surface);gap:var(--rt-space-xs)}.rt-table__card-field{display:flex;align-items:baseline;justify-content:space-between;gap:var(--rt-space-md)}.rt-table__card-label{flex:0 0 auto;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-table__card-value{min-width:0;overflow-wrap:anywhere;text-align:right}.rt-table__card-value .cdk-cell{display:inline;height:auto;padding:0;border:none;font:inherit;min-inline-size:0;text-align:inherit}.rt-table__card-actions{display:flex;justify-content:flex-end}.rt-table__card-skeleton-bar{display:block;width:60%;height:14px}.rt-table__card-skeleton-bar:first-child{width:80%}:root[data-theme=dark] .rt-table,html.rt-theme-dark .rt-table{background:var(--rt-color-bg-page)}:root[data-theme=dark] .rt-table .cdk-header-cell,:root[data-theme=dark] .rt-table .rt-table__empty,:root[data-theme=dark] .rt-table .rt-table__overlay-backdrop,html.rt-theme-dark .rt-table .cdk-header-cell,html.rt-theme-dark .rt-table .rt-table__empty,html.rt-theme-dark .rt-table .rt-table__overlay-backdrop{background:var(--rt-color-bg-page)}}\n"] }]
11904
- }], ctorParameters: () => [], propDecorators: { actionsColumnDef: [{
12089
+ }], propDecorators: { actionsColumnDef: [{
11905
12090
  type: ViewChild,
11906
12091
  args: [CdkColumnDef, { static: true }]
11907
12092
  }], actionsCellDef: [{
@@ -12764,7 +12949,7 @@ class RtThemeToggleComponent {
12764
12949
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtThemeToggleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12765
12950
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtThemeToggleComponent, isStandalone: true, selector: "rt-theme-toggle", inputs: { appearance: { classPropertyName: "appearance", publicName: "appearance", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "rt-theme-toggle" }, ngImport: i0, template: "@if (isSwitch()) {\n <rt-toggle-switch\n qa-dataid=\"theme-toggle-control\"\n size=\"md\"\n iconOff=\"ico-sun\"\n iconOn=\"moon\"\n [ariaLabel]=\"label\"\n [ngModel]=\"isDark()\"\n (ngModelChange)=\"onToggle()\" />\n} @else {\n <rt-icon-button\n qa-dataid=\"theme-toggle-control\"\n variant=\"ghost\"\n size=\"md\"\n shape=\"circle\"\n [ariaLabel]=\"label\"\n [icon]=\"iconName()\"\n [active]=\"isDark()\"\n (clicked)=\"onToggle()\" />\n}\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-theme-toggle{display:inline-flex}}\n"], dependencies: [{ kind: "ngmodule", type:
12766
12951
  // angular
12767
- FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
12952
+ FormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type:
12768
12953
  // standalone components / directives
12769
12954
  RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtToggleSwitchComponent, selector: "rt-toggle-switch", inputs: ["inputId", "ariaLabel", "size", "iconOff", "iconOn", "disabled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
12770
12955
  }
@@ -13012,7 +13197,7 @@ class RtThreadListComponent {
13012
13197
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtThreadListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
13013
13198
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtThreadListComponent, isStandalone: true, selector: "rt-thread-list", inputs: { rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, activeId: { classPropertyName: "activeId", publicName: "activeId", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyText: { classPropertyName: "emptyText", publicName: "emptyText", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, fetching: { classPropertyName: "fetching", publicName: "fetching", isSignal: true, isRequired: false, transformFunction: null }, hasMore: { classPropertyName: "hasMore", publicName: "hasMore", isSignal: true, isRequired: false, transformFunction: null }, filtersActive: { classPropertyName: "filtersActive", publicName: "filtersActive", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectRow: "selectRow", openInNewTab: "openInNewTab", searchChange: "searchChange", loadMore: "loadMore" }, host: { classAttribute: "rt-thread-list" }, queries: [{ propertyName: "rowTemplate", first: true, predicate: (RtThreadListRowDirective), descendants: true, isSignal: true }, { propertyName: "filtersTemplate", first: true, predicate: RtThreadListFiltersDirective, descendants: true, isSignal: true }, { propertyName: "searchTemplate", first: true, predicate: RtThreadListSearchDirective, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-thread-list\">\n <header rtElem=\"head\">\n @if (searchTemplate()) {\n <div rtElem=\"search\" qa-dataid=\"thread-list-search\">\n <ng-container [ngTemplateOutlet]=\"searchTemplate()?.templateRef ?? null\" />\n </div>\n } @else {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"thread-list-search\"\n iconLeft=\"search\"\n [placeholder]=\"searchPlaceholderText()\"\n [formControl]=\"searchControl\" />\n }\n @if (filtersTemplate()) {\n <rt-icon-button\n rtElem=\"filter-trigger\"\n qa-dataid=\"thread-list-filter\"\n icon=\"filter\"\n variant=\"secondary\"\n rtPopoverAlign=\"end\"\n [ariaLabel]=\"t().uiFilters\"\n [indicator]=\"filtersActive()\"\n [rtPopover]=\"filtersPanel\" />\n }\n </header>\n\n <ng-template #filtersPanel>\n <div rtElem=\"filters-panel\" qa-dataid=\"thread-list-filters-panel\">\n <ng-container [ngTemplateOutlet]=\"filtersTemplate()?.templateRef ?? null\" />\n </div>\n </ng-template>\n\n <div rtElem=\"rows\" qa-dataid=\"thread-list-rows\">\n @if (loading() && !rows().length) {\n @for (i of skeletonRows; track i) {\n <div rtElem=\"row-skeleton\" qa-dataid=\"thread-list-row-skeleton\" aria-hidden=\"true\">\n <rt-skeleton width=\"60%\" height=\"14px\" />\n <rt-skeleton width=\"90%\" height=\"12px\" />\n </div>\n }\n } @else if (!rows().length) {\n <rt-empty-state rtElem=\"empty\" qa-dataid=\"thread-list-empty\" [description]=\"t().uiThreadListEmptyHint\" [title]=\"emptyTitle()\">\n <div rtEmptyIllustration rtElem=\"empty-preview\">\n @for (row of emptyPreviewRows; track row.id) {\n <div rtElem=\"empty-card\" [rtMod]=\"{ offset: row.offset }\">\n <span rtElem=\"empty-avatar\">\n <rt-icon size=\"sm\" [name]=\"row.icon\" />\n </span>\n <span rtElem=\"empty-lines\">\n <span rtElem=\"empty-line\" [rtMod]=\"{ short: true }\"></span>\n <span rtElem=\"empty-line\"></span>\n </span>\n </div>\n }\n </div>\n </rt-empty-state>\n } @else {\n @for (row of rows(); track row.id) {\n <button\n rtElem=\"row\"\n qa-dataid=\"thread-list-row\"\n type=\"button\"\n [attr.data-id]=\"row.id\"\n [attr.data-unread]=\"row.hasUnread\"\n [attr.aria-current]=\"row.id === activeId() ? 'true' : null\"\n [rtMod]=\"{\n active: row.id === activeId(),\n unread: row.hasUnread,\n overdue: !!row.overdue,\n }\"\n (click)=\"onSelect(row.id, $event)\">\n <ng-container [ngTemplateOutlet]=\"rowTemplate()?.templateRef ?? null\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n </button>\n }\n @if (fetching()) {\n <div rtElem=\"row-loader\" qa-dataid=\"thread-list-row-loader\">\n <rt-skeleton width=\"90%\" height=\"12px\" />\n </div>\n }\n @if (hasMore()) {\n <div\n rtElem=\"load-anchor\"\n qa-dataid=\"thread-list-load-more\"\n aria-hidden=\"true\"\n rtInfiniteScroll\n [disabled]=\"fetching()\"\n (loadMore)=\"onLoadMore()\"></div>\n }\n }\n </div>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-thread-list{--rt-thread-list-border-width: var(--rt-border-width-thin);--rt-thread-list-card-radius: var(--rt-radius-lg);--rt-thread-list-tag-radius: var(--rt-radius-sm);--rt-thread-list-dot-radius: var(--rt-radius-full);--rt-thread-list-avatar-radius: var(--rt-radius-full);--rt-thread-list-line-radius: var(--rt-radius-full);display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0}.rt-thread-list__head{display:flex;flex:0 0 auto;align-items:center;block-size:56px;border-block-end:var(--rt-thread-list-border-width) solid var(--rt-color-border-strong);gap:var(--rt-space-2);padding-inline:var(--rt-space-4)}.rt-thread-list__search{flex:1 1 auto;max-inline-size:400px;min-inline-size:0}.rt-thread-list__filter-trigger{flex:0 0 auto}.rt-thread-list__filters-panel{display:flex;flex-direction:column;padding:var(--rt-space-4);gap:var(--rt-space-2);min-inline-size:220px}.rt-thread-list__rows{display:flex;flex:1 1 auto;flex-direction:column;min-block-size:0;overflow-y:auto}.rt-thread-list__row{display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-2) var(--rt-space-4);border:none;background:none;border-inline-start:3px solid transparent;cursor:pointer;font-family:inherit;font-size:var(--rt-text-sm);gap:var(--rt-space-4);inline-size:100%;text-align:start}.rt-thread-list__row:hover{background:var(--rt-color-bg-subtle)}.rt-thread-list__row--active,.rt-thread-list__row--active:hover{background:var(--rt-color-state-info-bg);border-inline-start-color:var(--rt-color-action-primary)}.rt-thread-list__row--unread{position:relative;background:var(--rt-color-bg-subtle);border-inline-start-color:var(--rt-color-action-primary)}.rt-thread-list__row--unread .rt-thread-list__row-title{font-weight:var(--rt-font-weight-semibold)}.rt-thread-list__row--unread:before{position:absolute;border-radius:var(--rt-thread-list-dot-radius);background:var(--rt-color-action-primary);block-size:var(--rt-space-2);content:\"\";inline-size:var(--rt-space-2);inset-block-start:50%;inset-inline-start:var(--rt-space-2);transform:translateY(-50%)}.rt-thread-list__row--active,.rt-thread-list__row--active:hover{background:var(--rt-color-state-info-bg)}.rt-thread-list__row--overdue .rt-thread-list__row-time{color:var(--rt-color-state-error-text)}.rt-thread-list__row-body{display:flex;flex-direction:column;gap:var(--rt-space-0-5);min-inline-size:0}.rt-thread-list__row-title{overflow:hidden;color:var(--rt-color-text-primary);text-overflow:ellipsis;white-space:nowrap}.rt-thread-list__row-id{color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-thread-list__row:hover .rt-copy-cell__button,.rt-thread-list__row:focus-within .rt-copy-cell__button{opacity:1}.rt-thread-list__row-subtitle{overflow:hidden;color:var(--rt-color-text-muted);text-overflow:ellipsis;white-space:nowrap}.rt-thread-list__row-meta{display:flex;flex:0 0 auto;align-items:center;gap:var(--rt-space-2)}.rt-thread-list__row-time{color:var(--rt-color-text-muted);white-space:nowrap}.rt-thread-list__row-tag{padding:var(--rt-space-0-5) var(--rt-space-2);border-radius:var(--rt-thread-list-tag-radius);background:var(--rt-color-state-warning-bg);color:var(--rt-color-state-warning-text);font-size:var(--rt-text-xs);white-space:nowrap}.rt-thread-list__row-dot{display:inline-block;border-radius:var(--rt-thread-list-dot-radius);background:var(--rt-color-text-link);block-size:6px;inline-size:6px}.rt-thread-list__row-skeleton{display:flex;flex-direction:column;padding:var(--rt-space-2) var(--rt-space-4);gap:var(--rt-space-2)}.rt-thread-list__row-loader{display:flex;justify-content:center;padding:var(--rt-space-2) var(--rt-space-4)}.rt-thread-list__load-anchor{block-size:1px}.rt-thread-list__empty{padding:var(--rt-space-6) var(--rt-space-4)}.rt-thread-list__empty-preview{display:flex;flex-direction:column;gap:var(--rt-space-1)}.rt-thread-list__empty-card{display:flex;align-items:center;padding:var(--rt-space-2);border:var(--rt-thread-list-border-width) solid var(--rt-color-border-subtle);border-radius:var(--rt-thread-list-card-radius);background:var(--rt-color-bg-surface-subtle);gap:var(--rt-space-2)}.rt-thread-list__empty-card--offset{transform:translate(20px)}.rt-thread-list__empty-avatar{display:grid;flex:0 0 auto;border-radius:var(--rt-thread-list-avatar-radius);background:var(--rt-color-text-muted);block-size:24px;color:var(--rt-color-bg-surface);inline-size:24px;place-items:center}.rt-thread-list__empty-lines{display:flex;flex-direction:column;gap:var(--rt-space-2)}.rt-thread-list__empty-line{border-radius:var(--rt-thread-list-line-radius);background:var(--rt-color-border-strong);block-size:6px;inline-size:112px}.rt-thread-list__empty-line--short{inline-size:48px}}\n"], dependencies: [{ kind: "directive", type:
13014
13199
  // Angular
13015
- NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type:
13200
+ NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type:
13016
13201
  // standalone components / directives
13017
13202
  RtEmptyStateComponent, selector: "rt-empty-state", inputs: ["icon", "title", "description"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "size", "color", "rotate"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtInputComponent, selector: "rt-input", inputs: ["type", "placeholder", "iconLeft", "iconRight", "passwordToggle", "autocomplete"] }, { kind: "directive", type: RtPopoverDirective, selector: "[rtPopover]", inputs: ["rtPopover", "rtPopoverTrigger", "rtPopoverWidth", "rtPopoverAlign", "rtPopoverFitViewport", "rtPopoverContext", "rtPopoverPanelClass", "rtPopoverDisabled", "rtPopoverOffsetY", "rtPopoverOffsetX"], outputs: ["rtPopoverOpened", "rtPopoverClosed"], exportAs: ["rtPopover"] }, { kind: "component", type: RtSkeletonComponent, selector: "rt-skeleton", inputs: ["shape", "size", "width", "height", "borderRadius", "animation"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: RtInfiniteScrollDirective, selector: "[rtInfiniteScroll]", inputs: ["disabled", "rootMargin"], outputs: ["loadMore"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
13018
13203
  }
@@ -13589,7 +13774,7 @@ class RtWorkspaceDetailsComponent {
13589
13774
  this.actionClicked.emit(action.id);
13590
13775
  }
13591
13776
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtWorkspaceDetailsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
13592
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtWorkspaceDetailsComponent, isStandalone: true, selector: "rt-workspace-details", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, agentEdit: { classPropertyName: "agentEdit", publicName: "agentEdit", isSignal: true, isRequired: false, transformFunction: null }, money: { classPropertyName: "money", publicName: "money", isSignal: true, isRequired: false, transformFunction: null }, toggles: { classPropertyName: "toggles", publicName: "toggles", isSignal: true, isRequired: false, transformFunction: null }, toggleHint: { classPropertyName: "toggleHint", publicName: "toggleHint", isSignal: true, isRequired: false, transformFunction: null }, transition: { classPropertyName: "transition", publicName: "transition", isSignal: true, isRequired: false, transformFunction: null }, audit: { classPropertyName: "audit", publicName: "audit", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleChange: "toggleChange", agentReassign: "agentReassign", transitionSubmit: "transitionSubmit", transitionSuccessClose: "transitionSuccessClose", transitionErrorClose: "transitionErrorClose", auditLoadMore: "auditLoadMore", actionClicked: "actionClicked" }, host: { classAttribute: "rt-workspace-details" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-workspace-details\">\n @if (title(); as headTitle) {\n <header rtElem=\"head\">\n <span rtElem=\"head-title\" qa-dataid=\"workspace-details-title\">{{ headTitle }}</span>\n </header>\n }\n\n @if (hasTabs()) {\n <rt-tabs rtElem=\"tabs\" qa-dataid=\"workspace-details-tabs\">\n <ng-template rtTab=\"details\" [label]=\"t().uiDetails\">\n <ng-container [ngTemplateOutlet]=\"detailsTpl\" />\n </ng-template>\n\n <ng-template rtTab=\"transition\" [label]=\"t().uiStageTransition\" [hidden]=\"transition() === null\">\n @if (transition(); as stageTransition) {\n @if (stageTransition.currentStageLabel; as stageLabel) {\n <rt-detail-list>\n <rt-detail-row qa-dataid=\"workspace-details-current-stage\" [label]=\"t().uiCurrentStage\">\n {{ stageLabel }}\n </rt-detail-row>\n </rt-detail-list>\n }\n\n @if (noTransitions()) {\n <rt-note qa-dataid=\"workspace-details-no-transitions\">{{ stageTransition.noTransitionsText }}</rt-note>\n } @else {\n <form\n rtElem=\"transition-form\"\n qa-dataid=\"workspace-details-transition-form\"\n [formGroup]=\"transitionForm\"\n (ngSubmit)=\"onTransitionSubmit()\">\n <rt-field\n [label]=\"t().uiTargetStage\"\n [loading]=\"stageTransition.loading\"\n [errors]=\"{ required: t().uiTargetStageRequired }\">\n <rt-select\n formControlName=\"to_key\"\n qa-dataid=\"workspace-details-transition-stage\"\n [placeholder]=\"t().uiPickTransition\"\n [options]=\"stageTransition.options\" />\n </rt-field>\n\n <rt-field [label]=\"t().uiCommentOptional\" [hint]=\"t().uiStageNoteHint\" [loading]=\"stageTransition.loading\">\n <rt-textarea formControlName=\"note\" qa-dataid=\"workspace-details-transition-note\" rows=\"3\" />\n </rt-field>\n\n <button\n rtButton\n rtElem=\"transition-submit\"\n qa-dataid=\"workspace-details-transition-submit\"\n type=\"submit\"\n [label]=\"t().uiMoveStage\"\n [attr.aria-label]=\"t().uiMoveStageAria\"\n [loading]=\"stageTransition.submitting\"\n [disabled]=\"stageTransition.loading || busy()\"></button>\n </form>\n }\n }\n </ng-template>\n\n <ng-template rtTab=\"audit\" [label]=\"t().uiHistory\" [hidden]=\"audit() === null\">\n @if (audit(); as auditFeed) {\n <div rtElem=\"audit\" qa-dataid=\"workspace-details-audit\">\n @if (auditFeed.loading) {\n <rt-spinner rtElem=\"audit-loading\" qa-dataid=\"workspace-details-audit-loading\" />\n } @else if (auditFeed.steps.length === 0) {\n <rt-empty-state\n qa-dataid=\"workspace-details-audit-empty\"\n icon=\"history\"\n [description]=\"t().uiAuditEmptyHint\"\n [title]=\"auditFeed.emptyText\" />\n } @else {\n <rt-timeline qa-dataid=\"workspace-details-audit-timeline\" [steps]=\"auditFeed.steps\" />\n }\n\n @if (auditFeed.hasMore) {\n <button\n rtButton\n rtElem=\"audit-more\"\n qa-dataid=\"workspace-details-audit-more\"\n type=\"button\"\n appearance=\"text\"\n [label]=\"t().uiLoadMore\"\n [attr.aria-label]=\"t().uiLoadMore\"\n [loading]=\"auditFeed.loadingMore\"\n [disabled]=\"auditFeed.loadingMore\"\n (click)=\"onAuditLoadMore()\"></button>\n }\n </div>\n }\n </ng-template>\n </rt-tabs>\n } @else {\n <div rtElem=\"body\">\n <ng-container [ngTemplateOutlet]=\"detailsTpl\" />\n </div>\n }\n\n <ng-template #detailsTpl>\n @if (rows().length > 0) {\n <rt-detail-list>\n @for (row of rows(); track row.label) {\n <rt-detail-row qa-dataid=\"workspace-details-row\" [label]=\"row.label\" [loading]=\"loading()\">\n @if (row.agent && agentEdit(); as edit) {\n @if (!editingAgent()) {\n <div rtElem=\"agent-view\">\n <span rtElem=\"agent-name\" qa-dataid=\"workspace-details-agent-name\">{{ edit.currentLabel }}</span>\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-edit\"\n icon=\"pencil\"\n size=\"sm\"\n [ariaLabel]=\"t().uiChangeAgent\"\n [tooltip]=\"edit.canEdit ? edit.editTooltip : edit.cannotEditTooltip\"\n [disabled]=\"!edit.canEdit || busy()\"\n (clicked)=\"startEditAgent()\" />\n </div>\n } @else {\n <div rtElem=\"agent-edit\">\n <rt-select\n qa-dataid=\"workspace-details-agent-select\"\n [placeholder]=\"t().uiPickAgent\"\n [ngModel]=\"selectedAgentId()\"\n [options]=\"edit.options\"\n [clearable]=\"false\"\n (ngModelChange)=\"onSelectedAgentChange($event)\" />\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-save\"\n icon=\"check\"\n size=\"sm\"\n rtConfirmTone=\"primary\"\n [ariaLabel]=\"t().uiSaveNewAgent\"\n [rtConfirmTitle]=\"t().uiReplaceAgentTitle\"\n [rtConfirmLabel]=\"t().uiReplace\"\n [tooltip]=\"canSaveReassign() ? t().uiSaveNewAgent : t().uiPickAnotherAgent\"\n [loading]=\"edit.loading\"\n [disabled]=\"!canSaveReassign() || busy()\"\n [rtConfirm]=\"edit.confirmText\"\n [rtConfirmDisabled]=\"!canSaveReassign() || busy()\"\n (confirmed)=\"onReassign()\" />\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-cancel\"\n icon=\"ico-close\"\n size=\"sm\"\n [ariaLabel]=\"t().uiRevert\"\n [tooltip]=\"t().uiRevert\"\n [disabled]=\"edit.loading\"\n (clicked)=\"cancelEditAgent()\" />\n </div>\n }\n } @else {\n {{ row.value }}\n }\n </rt-detail-row>\n }\n </rt-detail-list>\n }\n\n @if (money().length > 0) {\n <rt-money-list>\n @for (moneyRow of money(); track moneyRow.label) {\n <rt-money-row\n qa-dataid=\"workspace-details-money-row\"\n [label]=\"moneyRow.label\"\n [total]=\"moneyRow.total ?? false\"\n [loading]=\"loading()\">\n {{ moneyRow.amount | number: moneyRow.format ?? '1.0-0' }} \u20BD\n </rt-money-row>\n }\n </rt-money-list>\n }\n\n @if (toggles().length > 0 || toggleHint()) {\n <rt-aside-section>\n @for (toggle of toggles(); track toggle.id) {\n <div rtElem=\"toggle-row\" qa-dataid=\"workspace-details-toggle-row\" [attr.data-id]=\"toggle.id\">\n <span rtElem=\"toggle-label\" qa-dataid=\"workspace-details-toggle-label\">{{ toggle.label }}</span>\n <rt-toggle-switch\n qa-dataid=\"workspace-details-toggle\"\n [inputId]=\"toggle.id\"\n [ariaLabel]=\"toggle.label\"\n [ngModel]=\"toggle.value\"\n [disabled]=\"(toggle.disabled ?? false) || busy()\"\n (ngModelChange)=\"onToggleChange(toggle, $event)\" />\n </div>\n }\n @if (toggleHint(); as hint) {\n <p rtElem=\"toggle-hint\" qa-dataid=\"workspace-details-toggle-hint\">{{ hint }}</p>\n }\n </rt-aside-section>\n }\n </ng-template>\n\n <footer rtElem=\"footer\">\n @for (panelAction of actions(); track panelAction.id) {\n @if (panelAction.confirm; as confirm) {\n <button\n rtButton\n rtElem=\"footer-action\"\n qa-dataid=\"workspace-details-action\"\n type=\"button\"\n [attr.data-id]=\"panelAction.id\"\n [theme]=\"panelAction.theme ?? 'primary'\"\n [appearance]=\"panelAction.appearance ?? 'filled'\"\n [label]=\"panelAction.label\"\n [icon]=\"panelAction.icon\"\n [attr.aria-label]=\"panelAction.label\"\n [rtConfirmTitle]=\"confirm.title\"\n [rtConfirmLabel]=\"confirm.label\"\n [rtConfirmTone]=\"confirm.tone\"\n [loading]=\"panelAction.loading ?? false\"\n [disabled]=\"(panelAction.disabled ?? false) || busy()\"\n [rtConfirm]=\"confirm.text\"\n [rtConfirmDisabled]=\"(panelAction.disabled ?? false) || busy()\"\n (confirmed)=\"onAction(panelAction)\"></button>\n } @else {\n <button\n rtButton\n rtElem=\"footer-action\"\n qa-dataid=\"workspace-details-action\"\n type=\"button\"\n [attr.data-id]=\"panelAction.id\"\n [theme]=\"panelAction.theme ?? 'primary'\"\n [appearance]=\"panelAction.appearance ?? 'filled'\"\n [label]=\"panelAction.label\"\n [icon]=\"panelAction.icon\"\n [attr.aria-label]=\"panelAction.label\"\n [loading]=\"panelAction.loading ?? false\"\n [disabled]=\"(panelAction.disabled ?? false) || busy()\"\n (click)=\"onAction(panelAction)\"></button>\n }\n }\n </footer>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-workspace-details{--rt-workspace-details-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;block-size:100%;min-block-size:0}.rt-workspace-details__head{display:flex;flex:0 0 auto;align-items:center;block-size:56px;border-block-end:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);padding-inline:var(--rt-space-lg)}.rt-workspace-details__head-title{color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-workspace-details__tabs{flex:1 1 auto;min-block-size:0}.rt-workspace-details__tabs .rt-tabs__header{align-items:flex-end;block-size:56px;border-block-end:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);padding-inline:var(--rt-space-lg)}.rt-workspace-details__tabs .rt-tabs__tab-list{border-block-end:0}.rt-workspace-details__tabs .rt-tabs__content{padding-block-end:var(--rt-space-5);padding-inline:var(--rt-space-lg)}.rt-workspace-details__body{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-5) var(--rt-space-lg);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-workspace-details__agent-view,.rt-workspace-details__agent-edit{display:flex;align-items:center;gap:var(--rt-space-sm)}.rt-workspace-details__agent-name{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-workspace-details__toggle-row{display:flex;align-items:center;justify-content:space-between;gap:var(--rt-space-md)}.rt-workspace-details__toggle-label{color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-workspace-details__toggle-hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-workspace-details__transition-form{display:flex;flex-direction:column;gap:var(--rt-space-md)}.rt-workspace-details__transition-submit{align-self:flex-start}.rt-workspace-details__audit{display:flex;flex-direction:column;gap:var(--rt-space-md)}.rt-workspace-details__audit-loading,.rt-workspace-details__audit-more{align-self:center}.rt-workspace-details__footer{display:flex;flex:0 0 auto;flex-direction:column;padding:var(--rt-space-md) var(--rt-space-lg);border-block-start:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);gap:var(--rt-space-sm)}.rt-workspace-details__footer:empty{display:none}.rt-workspace-details__footer-action{justify-content:center;inline-size:100%}.rt-workspace-details__error{margin-inline:var(--rt-space-lg)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type:
13777
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtWorkspaceDetailsComponent, isStandalone: true, selector: "rt-workspace-details", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, entityId: { classPropertyName: "entityId", publicName: "entityId", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, agentEdit: { classPropertyName: "agentEdit", publicName: "agentEdit", isSignal: true, isRequired: false, transformFunction: null }, money: { classPropertyName: "money", publicName: "money", isSignal: true, isRequired: false, transformFunction: null }, toggles: { classPropertyName: "toggles", publicName: "toggles", isSignal: true, isRequired: false, transformFunction: null }, toggleHint: { classPropertyName: "toggleHint", publicName: "toggleHint", isSignal: true, isRequired: false, transformFunction: null }, transition: { classPropertyName: "transition", publicName: "transition", isSignal: true, isRequired: false, transformFunction: null }, audit: { classPropertyName: "audit", publicName: "audit", isSignal: true, isRequired: false, transformFunction: null }, actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggleChange: "toggleChange", agentReassign: "agentReassign", transitionSubmit: "transitionSubmit", transitionSuccessClose: "transitionSuccessClose", transitionErrorClose: "transitionErrorClose", auditLoadMore: "auditLoadMore", actionClicked: "actionClicked" }, host: { classAttribute: "rt-workspace-details" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-workspace-details\">\n @if (title(); as headTitle) {\n <header rtElem=\"head\">\n <span rtElem=\"head-title\" qa-dataid=\"workspace-details-title\">{{ headTitle }}</span>\n </header>\n }\n\n @if (hasTabs()) {\n <rt-tabs rtElem=\"tabs\" qa-dataid=\"workspace-details-tabs\">\n <ng-template rtTab=\"details\" [label]=\"t().uiDetails\">\n <ng-container [ngTemplateOutlet]=\"detailsTpl\" />\n </ng-template>\n\n <ng-template rtTab=\"transition\" [label]=\"t().uiStageTransition\" [hidden]=\"transition() === null\">\n @if (transition(); as stageTransition) {\n @if (stageTransition.currentStageLabel; as stageLabel) {\n <rt-detail-list>\n <rt-detail-row qa-dataid=\"workspace-details-current-stage\" [label]=\"t().uiCurrentStage\">\n {{ stageLabel }}\n </rt-detail-row>\n </rt-detail-list>\n }\n\n @if (noTransitions()) {\n <rt-note qa-dataid=\"workspace-details-no-transitions\">{{ stageTransition.noTransitionsText }}</rt-note>\n } @else {\n <form\n rtElem=\"transition-form\"\n qa-dataid=\"workspace-details-transition-form\"\n [formGroup]=\"transitionForm\"\n (ngSubmit)=\"onTransitionSubmit()\">\n <rt-field\n [label]=\"t().uiTargetStage\"\n [loading]=\"stageTransition.loading\"\n [errors]=\"{ required: t().uiTargetStageRequired }\">\n <rt-select\n formControlName=\"to_key\"\n qa-dataid=\"workspace-details-transition-stage\"\n [placeholder]=\"t().uiPickTransition\"\n [options]=\"stageTransition.options\" />\n </rt-field>\n\n <rt-field [label]=\"t().uiCommentOptional\" [hint]=\"t().uiStageNoteHint\" [loading]=\"stageTransition.loading\">\n <rt-textarea formControlName=\"note\" qa-dataid=\"workspace-details-transition-note\" rows=\"3\" />\n </rt-field>\n\n <button\n rtButton\n rtElem=\"transition-submit\"\n qa-dataid=\"workspace-details-transition-submit\"\n type=\"submit\"\n [label]=\"t().uiMoveStage\"\n [attr.aria-label]=\"t().uiMoveStageAria\"\n [loading]=\"stageTransition.submitting\"\n [disabled]=\"stageTransition.loading || busy()\"></button>\n </form>\n }\n }\n </ng-template>\n\n <ng-template rtTab=\"audit\" [label]=\"t().uiHistory\" [hidden]=\"audit() === null\">\n @if (audit(); as auditFeed) {\n <div rtElem=\"audit\" qa-dataid=\"workspace-details-audit\">\n @if (auditFeed.loading) {\n <rt-spinner rtElem=\"audit-loading\" qa-dataid=\"workspace-details-audit-loading\" />\n } @else if (auditFeed.steps.length === 0) {\n <rt-empty-state\n qa-dataid=\"workspace-details-audit-empty\"\n icon=\"history\"\n [description]=\"t().uiAuditEmptyHint\"\n [title]=\"auditFeed.emptyText\" />\n } @else {\n <rt-timeline qa-dataid=\"workspace-details-audit-timeline\" [steps]=\"auditFeed.steps\" />\n }\n\n @if (auditFeed.hasMore) {\n <button\n rtButton\n rtElem=\"audit-more\"\n qa-dataid=\"workspace-details-audit-more\"\n type=\"button\"\n appearance=\"text\"\n [label]=\"t().uiLoadMore\"\n [attr.aria-label]=\"t().uiLoadMore\"\n [loading]=\"auditFeed.loadingMore\"\n [disabled]=\"auditFeed.loadingMore\"\n (click)=\"onAuditLoadMore()\"></button>\n }\n </div>\n }\n </ng-template>\n </rt-tabs>\n } @else {\n <div rtElem=\"body\">\n <ng-container [ngTemplateOutlet]=\"detailsTpl\" />\n </div>\n }\n\n <ng-template #detailsTpl>\n @if (rows().length > 0) {\n <rt-detail-list>\n @for (row of rows(); track row.label) {\n <rt-detail-row qa-dataid=\"workspace-details-row\" [label]=\"row.label\" [loading]=\"loading()\">\n @if (row.agent && agentEdit(); as edit) {\n @if (!editingAgent()) {\n <div rtElem=\"agent-view\">\n <span rtElem=\"agent-name\" qa-dataid=\"workspace-details-agent-name\">{{ edit.currentLabel }}</span>\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-edit\"\n icon=\"pencil\"\n size=\"sm\"\n [ariaLabel]=\"t().uiChangeAgent\"\n [tooltip]=\"edit.canEdit ? edit.editTooltip : edit.cannotEditTooltip\"\n [disabled]=\"!edit.canEdit || busy()\"\n (clicked)=\"startEditAgent()\" />\n </div>\n } @else {\n <div rtElem=\"agent-edit\">\n <rt-select\n qa-dataid=\"workspace-details-agent-select\"\n [placeholder]=\"t().uiPickAgent\"\n [ngModel]=\"selectedAgentId()\"\n [options]=\"edit.options\"\n [clearable]=\"false\"\n (ngModelChange)=\"onSelectedAgentChange($event)\" />\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-save\"\n icon=\"check\"\n size=\"sm\"\n rtConfirmTone=\"primary\"\n [ariaLabel]=\"t().uiSaveNewAgent\"\n [rtConfirmTitle]=\"t().uiReplaceAgentTitle\"\n [rtConfirmLabel]=\"t().uiReplace\"\n [tooltip]=\"canSaveReassign() ? t().uiSaveNewAgent : t().uiPickAnotherAgent\"\n [loading]=\"edit.loading\"\n [disabled]=\"!canSaveReassign() || busy()\"\n [rtConfirm]=\"edit.confirmText\"\n [rtConfirmDisabled]=\"!canSaveReassign() || busy()\"\n (confirmed)=\"onReassign()\" />\n <rt-icon-button\n qa-dataid=\"workspace-details-agent-cancel\"\n icon=\"ico-close\"\n size=\"sm\"\n [ariaLabel]=\"t().uiRevert\"\n [tooltip]=\"t().uiRevert\"\n [disabled]=\"edit.loading\"\n (clicked)=\"cancelEditAgent()\" />\n </div>\n }\n } @else {\n {{ row.value }}\n }\n </rt-detail-row>\n }\n </rt-detail-list>\n }\n\n @if (money().length > 0) {\n <rt-money-list>\n @for (moneyRow of money(); track moneyRow.label) {\n <rt-money-row\n qa-dataid=\"workspace-details-money-row\"\n [label]=\"moneyRow.label\"\n [total]=\"moneyRow.total ?? false\"\n [loading]=\"loading()\">\n {{ moneyRow.amount | number: moneyRow.format ?? '1.0-0' }} \u20BD\n </rt-money-row>\n }\n </rt-money-list>\n }\n\n @if (toggles().length > 0 || toggleHint()) {\n <rt-aside-section>\n @for (toggle of toggles(); track toggle.id) {\n <div rtElem=\"toggle-row\" qa-dataid=\"workspace-details-toggle-row\" [attr.data-id]=\"toggle.id\">\n <span rtElem=\"toggle-label\" qa-dataid=\"workspace-details-toggle-label\">{{ toggle.label }}</span>\n <rt-toggle-switch\n qa-dataid=\"workspace-details-toggle\"\n [inputId]=\"toggle.id\"\n [ariaLabel]=\"toggle.label\"\n [ngModel]=\"toggle.value\"\n [disabled]=\"(toggle.disabled ?? false) || busy()\"\n (ngModelChange)=\"onToggleChange(toggle, $event)\" />\n </div>\n }\n @if (toggleHint(); as hint) {\n <p rtElem=\"toggle-hint\" qa-dataid=\"workspace-details-toggle-hint\">{{ hint }}</p>\n }\n </rt-aside-section>\n }\n </ng-template>\n\n <footer rtElem=\"footer\">\n @for (panelAction of actions(); track panelAction.id) {\n @if (panelAction.confirm; as confirm) {\n <button\n rtButton\n rtElem=\"footer-action\"\n qa-dataid=\"workspace-details-action\"\n type=\"button\"\n [attr.data-id]=\"panelAction.id\"\n [theme]=\"panelAction.theme ?? 'primary'\"\n [appearance]=\"panelAction.appearance ?? 'filled'\"\n [label]=\"panelAction.label\"\n [icon]=\"panelAction.icon\"\n [attr.aria-label]=\"panelAction.label\"\n [rtConfirmTitle]=\"confirm.title\"\n [rtConfirmLabel]=\"confirm.label\"\n [rtConfirmTone]=\"confirm.tone\"\n [loading]=\"panelAction.loading ?? false\"\n [disabled]=\"(panelAction.disabled ?? false) || busy()\"\n [rtConfirm]=\"confirm.text\"\n [rtConfirmDisabled]=\"(panelAction.disabled ?? false) || busy()\"\n (confirmed)=\"onAction(panelAction)\"></button>\n } @else {\n <button\n rtButton\n rtElem=\"footer-action\"\n qa-dataid=\"workspace-details-action\"\n type=\"button\"\n [attr.data-id]=\"panelAction.id\"\n [theme]=\"panelAction.theme ?? 'primary'\"\n [appearance]=\"panelAction.appearance ?? 'filled'\"\n [label]=\"panelAction.label\"\n [icon]=\"panelAction.icon\"\n [attr.aria-label]=\"panelAction.label\"\n [loading]=\"panelAction.loading ?? false\"\n [disabled]=\"(panelAction.disabled ?? false) || busy()\"\n (click)=\"onAction(panelAction)\"></button>\n }\n }\n </footer>\n</ng-container>\n", styles: ["@layer rt-kit.components{.rt-workspace-details{--rt-workspace-details-border-width: var(--rt-border-width-thin);display:flex;flex-direction:column;block-size:100%;min-block-size:0}.rt-workspace-details__head{display:flex;flex:0 0 auto;align-items:center;block-size:56px;border-block-end:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);padding-inline:var(--rt-space-lg)}.rt-workspace-details__head-title{color:var(--rt-color-text-primary);font-size:var(--rt-text-md);font-weight:var(--rt-font-weight-semibold)}.rt-workspace-details__tabs{flex:1 1 auto;min-block-size:0}.rt-workspace-details__tabs .rt-tabs__header{align-items:flex-end;block-size:56px;border-block-end:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);padding-inline:var(--rt-space-lg)}.rt-workspace-details__tabs .rt-tabs__tab-list{border-block-end:0}.rt-workspace-details__tabs .rt-tabs__content{padding-block-end:var(--rt-space-5);padding-inline:var(--rt-space-lg)}.rt-workspace-details__body{display:flex;flex:1 1 auto;flex-direction:column;padding:var(--rt-space-5) var(--rt-space-lg);gap:var(--rt-space-md);min-block-size:0;overflow-y:auto}.rt-workspace-details__agent-view,.rt-workspace-details__agent-edit{display:flex;align-items:center;gap:var(--rt-space-sm)}.rt-workspace-details__agent-name{overflow:hidden;min-inline-size:0;text-overflow:ellipsis;white-space:nowrap}.rt-workspace-details__toggle-row{display:flex;align-items:center;justify-content:space-between;gap:var(--rt-space-md)}.rt-workspace-details__toggle-label{color:var(--rt-color-text-muted);font-size:var(--rt-text-sm);font-weight:var(--rt-font-weight-medium)}.rt-workspace-details__toggle-hint{margin:0;color:var(--rt-color-text-muted);font-size:var(--rt-text-sm)}.rt-workspace-details__transition-form{display:flex;flex-direction:column;gap:var(--rt-space-md)}.rt-workspace-details__transition-submit{align-self:flex-start}.rt-workspace-details__audit{display:flex;flex-direction:column;gap:var(--rt-space-md)}.rt-workspace-details__audit-loading,.rt-workspace-details__audit-more{align-self:center}.rt-workspace-details__footer{display:flex;flex:0 0 auto;flex-direction:column;padding:var(--rt-space-md) var(--rt-space-lg);border-block-start:var(--rt-workspace-details-border-width) solid var(--rt-color-border-strong);gap:var(--rt-space-sm)}.rt-workspace-details__footer:empty{display:none}.rt-workspace-details__footer-action{justify-content:center;inline-size:100%}.rt-workspace-details__error{margin-inline:var(--rt-space-lg)}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type:
13593
13778
  // standalone components / directives
13594
13779
  RtAsideSectionComponent, selector: "rt-aside-section", inputs: ["heading"] }, { kind: "directive", type: RtButtonDirective, selector: "button[rtButton], a[rtButton]", inputs: ["label", "icon", "iconPos", "theme", "appearance", "size", "rounded", "loading", "loadingIcon"] }, { kind: "directive", type: RtConfirmDirective, selector: "[rtConfirm]", inputs: ["rtConfirm", "rtConfirmTitle", "rtConfirmLabel", "rtConfirmCancelLabel", "rtConfirmTone", "rtConfirmDisabled"], outputs: ["confirmed"], exportAs: ["rtConfirm"] }, { kind: "component", type: RtDetailListComponent, selector: "rt-detail-list" }, { kind: "component", type: RtDetailRowComponent, selector: "rt-detail-row", inputs: ["label", "loading"] }, { kind: "component", type: RtEmptyStateComponent, selector: "rt-empty-state", inputs: ["icon", "title", "description"] }, { kind: "component", type: RtFieldComponent, selector: "rt-field", inputs: ["label", "hint", "help", "readonly", "loading", "hideRequiredMark", "reserveHintSpace", "errors"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "ariaLabel", "variant", "iconColor", "size", "iconSize", "shape", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtMoneyListComponent, selector: "rt-money-list" }, { kind: "component", type: RtMoneyRowComponent, selector: "rt-money-row", inputs: ["label", "total", "loading"] }, { kind: "component", type: RtNoteComponent, selector: "rt-note" }, { kind: "component", type: RtSelectComponent, selector: "rt-select", inputs: ["options", "placeholder", "iconLeft", "filter", "filterPlaceholder"], outputs: ["selectionChange"] }, { kind: "component", type: RtSpinnerComponent, selector: "rt-spinner", inputs: ["diameter", "color"] }, { kind: "directive", type: RtTabDirective, selector: "[rtTab]", inputs: ["rtTab", "label", "titleTemplate", "icon", "iconColor", "badge", "disabled", "hidden", "invalid", "invalidMessage"] }, { kind: "component", type: RtTabsComponent, selector: "rt-tabs", inputs: ["activeId", "direction", "stretch", "contentScrollable"], outputs: ["activeIdChange"] }, { kind: "component", type: RtTextareaComponent, selector: "rt-textarea", inputs: ["placeholder", "readonly", "rows", "resize"] }, { kind: "component", type: RtTimelineComponent, selector: "rt-timeline", inputs: ["steps"] }, { kind: "component", type: RtToggleSwitchComponent, selector: "rt-toggle-switch", inputs: ["inputId", "ariaLabel", "size", "iconOff", "iconOn", "disabled"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "pipe", type:
13595
13780
  // Angular
@@ -14073,5 +14258,5 @@ const RT_CONSUMER_HANDLE_NAMES = [
14073
14258
  * Generated bundle index. Do not edit.
14074
14259
  */
14075
14260
 
14076
- export { BreakpointsService, CATEGORY_ORDER, ERtCalendarDayState, ERtChatMessageStatus, ERtMarkdownBlock, ERtMarkdownInline, ERtStorageKeys, ICON_CATEGORY_RULES, MessageBus, NotificationBus, RT_ASIDE_DATA, RT_CONSUMER_HANDLE_NAMES, RT_COUNTER_DECREASE_KEY, RT_COUNTER_INCREASE_KEY, RT_DESIGN_TOKEN_NAMES, RT_DIALOG_DATA, RT_FIELD_DEFAULT_ERROR_KEYS, RT_FIELD_EMPTY_VALUE, RT_ICONS_BASE_URL, RT_ICON_SPRITE_ID, RT_ICON_SYMBOL_ID_PREFIX, RT_KIT_LABELS, RT_KIT_LABELS_EN, RT_KIT_LOCALE, RT_KIT_TRANSLATOR, RT_MENU_SELECT_EVENT, RT_TABLE_ROW_ACTIONS_COLUMN, RT_TOASTER_GAP_PX, RtAsideComponent, RtAsideFooterComponent, RtAsideHeaderComponent, RtAsideRef, RtAsideSectionComponent, RtAsideService, RtAutocompleteComponent, RtBarListComponent, RtBottomSheetComponent, RtButtonDirective, RtCalendarComponent, RtCardComponent, RtChatComponent, RtChatStatusLabelPipe, RtCheckboxComponent, RtCollapsibleTextComponent, RtConfirmDirective, RtConfirmPopoverComponent, RtContainerComponent, RtContainerContentDirective, RtContainerContentPanelDirective, RtContainerHeaderDirective, RtContainerLeftSidenavDirective, RtContainerLeftSidenavPanelDirective, RtContainerRightSidenavDirective, RtContainerRightSidenavPanelDirective, RtContainerStateService, RtContainerToolbarCenterDirective, RtContainerToolbarLeftDirective, RtContainerToolbarRightDirective, RtCopyCellComponent, RtCounterComponent, RtCounterRowComponent, RtDatePickerComponent, RtDayStampPipe, RtDeltaViewComponent, RtDetailListComponent, RtDetailRowComponent, RtDialogComponent, RtDialogFooterComponent, RtDialogHeaderComponent, RtDialogRef, RtDialogService, RtDownloadLinkComponent, RtEmptyStateComponent, RtFieldComponent, RtFieldHintDirective, RtFileCardComponent, RtFileDropComponent, RtFileInputComponent, RtFileListComponent, RtFilterControlComponent, RtFormControlBase, RtHeaderComponent, RtIconButtonComponent, RtIconComponent, RtIconRegistry, RtInfiniteScrollDirective, RtInfoItemComponent, RtInputComponent, RtInputNumberComponent, RtKitLabelPipe, RtLiveBadgeComponent, RtLogoComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtMessageComposerComponent, RtMoneyListComponent, RtMoneyRowComponent, RtMultiselectComponent, RtNightGridComponent, RtNoteComponent, RtNotificationsBellComponent, RtPageHeaderComponent, RtPaginationComponent, RtPhotoViewerComponent, RtPopoverDirective, RtRichEditorComponent, RtRouteAsideComponent, RtRowHasActionsPipe, RtSectionNavComponent, RtSelectComponent, RtSkeletonComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtSplitButtonComponent, RtStatTileComponent, RtStepperComponent, RtTabDirective, RtTableCardDirective, RtTableComponent, RtTableRowActionsDirective, RtTableRowDirective, RtTableSettingsAsideComponent, RtTableSettingsPanelComponent, RtTableSettingsRegistry, RtTableSortHeaderComponent, RtTabsComponent, RtTabsControlDirective, RtTagComponent, RtTextareaComponent, RtThemeToggleComponent, RtThreadListComponent, RtThreadListFiltersDirective, RtThreadListRowDirective, RtThreadListSearchDirective, RtTimelineComponent, RtToastComponent, RtToasterComponent, RtToggleButtonGroupComponent, RtToggleSwitchComponent, RtToolbarCenterDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, RtTooltipComponent, RtTooltipDirective, RtTruncateMiddlePipe, RtWelcomeDialogComponent, RtWorkspaceAsideDirective, RtWorkspaceCenterDirective, RtWorkspaceComponent, RtWorkspaceDesktopOnlyDirective, RtWorkspaceDetailsComponent, RtWorkspaceListDirective, SEARCH_DEBOUNCE_MS, ThemeService, ariaSortOf, breakNode, categoryOf, coerceQuillDeltaOutput, deliveryStatus, deriveFileTitle, fileNameFromUrl, formatFileSize, getFileTypeIcon, iconsName, indexFromParam, indexFromScroll, isColumnSortable, nextPhotoIndex, nextSort, normalizeQuillDelta, paramFromIndex, parseInline, parseMarkdown, pendingToRtMessage, provideRtIcons, provideRtKitLabels, rowHasAvailableActions, rtAsideUnsavedGuard, rtKitLabel, searchDebounce, shouldRenderPhoto, sortDirectionOf, toRtMessage };
14261
+ export { BreakpointsService, CATEGORY_ORDER, ERtCalendarDayState, ERtChatMessageStatus, ERtMarkdownBlock, ERtMarkdownInline, ERtStorageKeys, ICON_CATEGORY_RULES, MessageBus, NotificationBus, RT_ASIDE_DATA, RT_CONSUMER_HANDLE_NAMES, RT_COUNTER_DECREASE_KEY, RT_COUNTER_INCREASE_KEY, RT_DESIGN_TOKEN_NAMES, RT_DIALOG_DATA, RT_FIELD_DEFAULT_ERROR_KEYS, RT_FIELD_EMPTY_VALUE, RT_ICONS_BASE_URL, RT_ICON_SPRITE_ID, RT_ICON_SYMBOL_ID_PREFIX, RT_KIT_LABELS, RT_KIT_LABELS_EN, RT_KIT_LOCALE, RT_KIT_TRANSLATOR, RT_MENU_SELECT_EVENT, RT_TABLE_ROW_ACTIONS_COLUMN, RT_TOASTER_GAP_PX, RtAsideComponent, RtAsideFooterComponent, RtAsideHeaderComponent, RtAsideRef, RtAsideSectionComponent, RtAsideService, RtAutocompleteComponent, RtBarListComponent, RtBottomSheetComponent, RtButtonDirective, RtCalendarComponent, RtCardComponent, RtChatComponent, RtChatMessageActionsDirective, RtChatMessageHasActionsPipe, RtChatStatusLabelPipe, RtCheckboxComponent, RtCollapsibleTextComponent, RtConfirmDirective, RtConfirmPopoverComponent, RtContainerComponent, RtContainerContentDirective, RtContainerContentPanelDirective, RtContainerHeaderDirective, RtContainerLeftSidenavDirective, RtContainerLeftSidenavPanelDirective, RtContainerRightSidenavDirective, RtContainerRightSidenavPanelDirective, RtContainerStateService, RtContainerToolbarCenterDirective, RtContainerToolbarLeftDirective, RtContainerToolbarRightDirective, RtCopyCellComponent, RtCounterComponent, RtCounterRowComponent, RtDatePickerComponent, RtDayStampPipe, RtDeltaViewComponent, RtDetailListComponent, RtDetailRowComponent, RtDialogComponent, RtDialogFooterComponent, RtDialogHeaderComponent, RtDialogRef, RtDialogService, RtDownloadLinkComponent, RtEmptyStateComponent, RtFieldComponent, RtFieldHintDirective, RtFileCardComponent, RtFileDropComponent, RtFileInputComponent, RtFileListComponent, RtFilterControlComponent, RtFormControlBase, RtHeaderComponent, RtIconButtonComponent, RtIconComponent, RtIconRegistry, RtInfiniteScrollDirective, RtInfoItemComponent, RtInputComponent, RtInputNumberComponent, RtKitLabelPipe, RtLiveBadgeComponent, RtLogoComponent, RtMarkdownTextComponent, RtMenuComponent, RtMenuItemComponent, RtMessageComponent, RtMessageComposerComponent, RtMoneyListComponent, RtMoneyRowComponent, RtMultiselectComponent, RtNightGridComponent, RtNoteComponent, RtNotificationsBellComponent, RtPageHeaderComponent, RtPaginationComponent, RtPhotoViewerComponent, RtPopoverDirective, RtRichEditorComponent, RtRouteAsideComponent, RtRowHasActionsPipe, RtSectionNavComponent, RtSelectComponent, RtSkeletonComponent, RtSkeletonWrapperComponent, RtSpinnerComponent, RtSplitButtonComponent, RtStatTileComponent, RtStepperComponent, RtTabDirective, RtTableCardDirective, RtTableComponent, RtTableRowActionsDirective, RtTableRowDirective, RtTableSettingsAsideComponent, RtTableSettingsPanelComponent, RtTableSettingsRegistry, RtTableSortHeaderComponent, RtTabsComponent, RtTabsControlDirective, RtTagComponent, RtTextareaComponent, RtThemeToggleComponent, RtThreadListComponent, RtThreadListFiltersDirective, RtThreadListRowDirective, RtThreadListSearchDirective, RtTimelineComponent, RtToastComponent, RtToasterComponent, RtToggleButtonGroupComponent, RtToggleSwitchComponent, RtToolbarCenterDirective, RtToolbarComponent, RtToolbarLeftDirective, RtToolbarRightDirective, RtTooltipComponent, RtTooltipDirective, RtTruncateMiddlePipe, RtWelcomeDialogComponent, RtWorkspaceAsideDirective, RtWorkspaceCenterDirective, RtWorkspaceComponent, RtWorkspaceDesktopOnlyDirective, RtWorkspaceDetailsComponent, RtWorkspaceListDirective, SEARCH_DEBOUNCE_MS, ThemeService, ariaSortOf, breakNode, categoryOf, coerceQuillDeltaOutput, defaultColumnItems, deliveryStatus, deriveFileTitle, displayedColumnKeys, fileNameFromUrl, formatFileSize, getFileTypeIcon, iconsName, indexFromParam, indexFromScroll, isColumnSortable, messageHasAvailableActions, nextPhotoIndex, nextSort, normalizeQuillDelta, paramFromIndex, parseInline, parseMarkdown, pendingToRtMessage, provideRtIcons, provideRtKitLabels, resolveColumns, rowHasAvailableActions, rtAsideUnsavedGuard, rtKitLabel, searchDebounce, shouldRenderPhoto, sortDirectionOf, toRtMessage, withoutLockedHidden };
14077
14262
  //# sourceMappingURL=rt-tools-ui-kit-v2.mjs.map