@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.
- package/fesm2022/rt-tools-ui-kit-v2.mjs +794 -609
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +3 -3
- package/rt-tools-ui-kit-v2-0.8.1.tgz +0 -0
- package/src/lib/components/chat/CONTEXT.md +5 -0
- package/src/lib/components/chat/rt-chat.component.scss +12 -1
- package/src/lib/components/form-control/CONTEXT.md +7 -0
- package/src/lib/components/photo-viewer/rt-photo-viewer.component.scss +11 -4
- package/types/rt-tools-ui-kit-v2.d.ts +195 -115
- package/rt-tools-ui-kit-v2-0.7.0.tgz +0 -0
|
@@ -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,
|
|
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
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
-
|
|
1953
|
-
#
|
|
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
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
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
|
-
|
|
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
|
|
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.#
|
|
2029
|
+
this.errors = computed(() => this.#state().errors, /* @ts-ignore */
|
|
2030
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
2012
2031
|
/** Помечен ли контрол как обязательный — для авто-«*» в rt-field. */
|
|
2013
|
-
this.required = this.#
|
|
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.#
|
|
2030
|
-
this.#syncState(control);
|
|
2056
|
+
this.#pushedState.set(stateOf(control));
|
|
2031
2057
|
control.events.pipe(takeUntilDestroyed(this.#destroyRef)).subscribe(() => {
|
|
2032
|
-
this.#
|
|
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-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
|
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
|
|
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
|
|
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$
|
|
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$
|
|
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$
|
|
4802
|
-
[`${BEM_BLOCK$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
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
|
-
*
|
|
4957
|
-
*
|
|
4958
|
-
*
|
|
4959
|
-
*
|
|
5185
|
+
* Прилипает ли лента к низу после этой прокрутки.
|
|
5186
|
+
*
|
|
5187
|
+
* Прилипание снимается только тогда, когда читатель уехал вверх сам. Прокрутка, сделанная
|
|
5188
|
+
* самим компонентом, и рост содержимого приходят с тем же или большим положением — на них
|
|
5189
|
+
* прилипание лишь подтверждается: иначе позднее событие от собственной прокрутки читалось бы
|
|
5190
|
+
* отрывом от низа, и лента переставала бы догонять последнее сообщение.
|
|
4960
5191
|
*/
|
|
4961
|
-
|
|
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(
|
|
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 =
|
|
5494
|
+
const picked = pickedFiles(inputEl.files);
|
|
5241
5495
|
if (picked.length > 0) {
|
|
5242
|
-
this.replyForm.controls.files.setValue(
|
|
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
|
-
|
|
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
|
-
|
|
5266
|
-
|
|
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
|
-
* Серия отложенных
|
|
5277
|
-
*
|
|
5278
|
-
*
|
|
5279
|
-
* заново берёт актуальный элемент треда и его текущий scrollHeight и, пока
|
|
5280
|
-
* пользователь у нижнего края, дотягивает ленту до конца.
|
|
5514
|
+
* Серия отложенных подтягиваний ленты к низу: присланное содержимое дорисовывается
|
|
5515
|
+
* несколькими проходами, и одной прокрутки мало. Каждый тик берёт тред заново — он
|
|
5516
|
+
* бывает пересоздан — и дотягивает ленту, пока читатель стоит у нижнего края.
|
|
5281
5517
|
*/
|
|
5282
5518
|
#pinToBottomDeferred() {
|
|
5283
|
-
for (const delay of
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
5772
|
-
[`${BEM_BLOCK$
|
|
5773
|
-
[`${BEM_BLOCK$
|
|
5774
|
-
[`${BEM_BLOCK$
|
|
5775
|
-
[`${BEM_BLOCK$
|
|
5776
|
-
[`${BEM_BLOCK$
|
|
5777
|
-
[`${BEM_BLOCK$
|
|
5778
|
-
[`${BEM_BLOCK$
|
|
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$
|
|
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$
|
|
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$
|
|
5925
|
-
[`${BEM_BLOCK$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
*
|
|
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
|
-
#
|
|
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.#
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
7346
|
-
|
|
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
|
|
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.#
|
|
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
|
-
|
|
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.#
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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-
|
|
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-
|
|
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.#
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
* Строки для
|
|
11564
|
-
*
|
|
11565
|
-
* для остальных типов пусто (карточки не поддерживаются). `#dataSourceVersion`
|
|
11566
|
-
* инвалидирует computed при смене источника.
|
|
11807
|
+
* Строки для карточек узкого экрана. Признак `#dataSourceVersion` пересчитывает список
|
|
11808
|
+
* при смене источника: CDK хранит его полем, а не сигналом.
|
|
11567
11809
|
*/
|
|
11568
11810
|
this.cardRows = computed(() => {
|
|
11569
11811
|
this.#dataSourceVersion();
|
|
11570
|
-
|
|
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
|
-
/**
|
|
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.#
|
|
11750
|
-
.
|
|
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
|
-
|
|
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
|
-
*
|
|
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
|
-
/** Сохраняет настройки колонок
|
|
11998
|
+
/** Сохраняет настройки колонок у настраиваемой таблицы; у прочих сохранять нечего. */
|
|
11813
11999
|
#persistSettings(settings) {
|
|
11814
12000
|
const tableId = this.#persistableTableId();
|
|
11815
|
-
if (tableId
|
|
11816
|
-
|
|
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:
|
|
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
|
-
}],
|
|
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
|