@rt-tools/ui-kit-v2 0.4.0 → 0.5.0
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 +231 -24
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +1 -1
- package/rt-tools-ui-kit-v2-0.5.0.tgz +0 -0
- package/src/lib/components/container/CONTEXT.md +22 -0
- package/src/lib/components/table/sort-header/CONTEXT.md +40 -0
- package/src/lib/components/tabs/CONTEXT.md +5 -0
- package/src/lib/components/toast/CONTEXT.md +8 -0
- package/types/rt-tools-ui-kit-v2.d.ts +9 -16
- package/rt-tools-ui-kit-v2-0.4.0.tgz +0 -0
|
@@ -5,7 +5,7 @@ import * as i1$1 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, of, shareReplay, forkJoin, tap, firstValueFrom, switchMap, debounce, timer, filter, catchError, exhaustMap, startWith, take, fromEvent, debounceTime, distinctUntilChanged } from 'rxjs';
|
|
8
|
+
import { Subject, mergeMap, map, EMPTY, merge, takeUntil, of, shareReplay, forkJoin, tap, firstValueFrom, switchMap, debounce, timer, filter, catchError, exhaustMap, startWith, take, finalize, 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
11
|
import * as i1 from '@angular/forms';
|
|
@@ -1665,7 +1665,13 @@ function provideRtIcons(baseUrl) {
|
|
|
1665
1665
|
// Не блокируем bootstrap: sprite иконок грузится в фоне, приложение
|
|
1666
1666
|
// стартует сразу. Иначе Angular ждёт forkJoin из сотен SVG-запросов
|
|
1667
1667
|
// (первая загрузка +5с). rt-icon рендерит через <use> по мере готовности sprite.
|
|
1668
|
-
|
|
1668
|
+
//
|
|
1669
|
+
// Умолчание обязательно. Приложение, снесённое до прихода набора, обрывает запросы,
|
|
1670
|
+
// `forkJoin` завершается ничего не отдав, и без умолчания обещание отказывает
|
|
1671
|
+
// необработанным `no elements in sequence`. Видно это там, где приложения живут
|
|
1672
|
+
// коротко: витрина роняла на этом по одной-две истории за заход, и причина читалась
|
|
1673
|
+
// как дефект обвязки снимков.
|
|
1674
|
+
void firstValueFrom(registry.preloadAll(), { defaultValue: undefined });
|
|
1669
1675
|
}),
|
|
1670
1676
|
]);
|
|
1671
1677
|
}
|
|
@@ -2663,7 +2669,7 @@ const LOADING_FADE_OUT_BUFFER_MS = 50;
|
|
|
2663
2669
|
* ```html
|
|
2664
2670
|
* <button rtButton label="Войти" theme="primary"></button>
|
|
2665
2671
|
* <button rtButton label="Скачать" icon="ico-download" theme="success"></button>
|
|
2666
|
-
* <button rtButton icon="
|
|
2672
|
+
* <button rtButton icon="pencil" theme="info" appearance="text"></button>
|
|
2667
2673
|
* <button rtButton label="Сохранение..." [loading]="saving()" [disabled]="saving()"></button>
|
|
2668
2674
|
* <a rtButton label="Главная" [routerLink]="'/'"></a>
|
|
2669
2675
|
* ```
|
|
@@ -6534,6 +6540,77 @@ function rootSegmentsOf(commands) {
|
|
|
6534
6540
|
return [...head.split('/').filter(Boolean), ...rest];
|
|
6535
6541
|
}
|
|
6536
6542
|
|
|
6543
|
+
/**
|
|
6544
|
+
* Учёт панелей, стоящих на экране: из него роутерный гард узнаёт, кого спрашивать
|
|
6545
|
+
* о несохранённых правках.
|
|
6546
|
+
*
|
|
6547
|
+
* Спрашивать компонент, который роутер подставляет в гард, нельзя: когда
|
|
6548
|
+
* ro-маршрут объявлен под уровнем без своего компонента, Angular отдаёт детям
|
|
6549
|
+
* контекст родителя, и в гард приходит компонент первичного аутлета — экран
|
|
6550
|
+
* раздела, а не панель.
|
|
6551
|
+
*
|
|
6552
|
+
* Ключ записи — объявление маршрута, а не порядок постановки. Гард стоит на
|
|
6553
|
+
* каждом ro-маршруте, и одна навигация зовёт его за каждый уходящий маршрут
|
|
6554
|
+
* отдельно: учёт стеком дал бы ответ не той панели и два вопроса о правках
|
|
6555
|
+
* подряд. Объявление маршрута — та же ссылка на объект и у панели, и у снимка,
|
|
6556
|
+
* который роутер отдаёт гарду, поэтому два каркаса на экране не путаются даже
|
|
6557
|
+
* при одинаково названных аутлетах: маршруты у них разные.
|
|
6558
|
+
*
|
|
6559
|
+
* Служба корневая: гард живёт в инжекторе маршрута и до службы, поставленной на
|
|
6560
|
+
* уровне компонента, не дотянулся бы вовсе. Одного учёта на приложение хватает —
|
|
6561
|
+
* записи различает маршрут. Переменной уровня модуля учёт быть не может: та
|
|
6562
|
+
* пережила бы запрос при отдаче страницы сервером и оказалась бы общей на два
|
|
6563
|
+
* приложения, поднятых на одной странице.
|
|
6564
|
+
*/
|
|
6565
|
+
class RtRouteAsideRegistry {
|
|
6566
|
+
#panels = new Map();
|
|
6567
|
+
/**
|
|
6568
|
+
* Панель встала на экран. Повторная постановка под тем же маршрутом заменяет
|
|
6569
|
+
* запись: аутлет держит одну панель, и отвечать должна нынешняя.
|
|
6570
|
+
*/
|
|
6571
|
+
register(route, panel) {
|
|
6572
|
+
const key = route.routeConfig;
|
|
6573
|
+
if (key === null) {
|
|
6574
|
+
return;
|
|
6575
|
+
}
|
|
6576
|
+
this.#panels.set(key, panel);
|
|
6577
|
+
}
|
|
6578
|
+
/**
|
|
6579
|
+
* Панель ушла с экрана. Запись снимается только своя: пока уходящая панель
|
|
6580
|
+
* доигрывает уход, её место мог занять сосед, и снятие вслепую стёрло бы
|
|
6581
|
+
* запись того, кто на экране остался.
|
|
6582
|
+
*/
|
|
6583
|
+
unregister(route, panel) {
|
|
6584
|
+
const key = route.routeConfig;
|
|
6585
|
+
if (key === null || this.#panels.get(key) !== panel) {
|
|
6586
|
+
return;
|
|
6587
|
+
}
|
|
6588
|
+
this.#panels.delete(key);
|
|
6589
|
+
}
|
|
6590
|
+
/** Снятие панели, чей ответ о правках отказал: спрашивать её больше нечем. */
|
|
6591
|
+
forget(panel) {
|
|
6592
|
+
for (const [key, registered] of this.#panels) {
|
|
6593
|
+
if (registered === panel) {
|
|
6594
|
+
this.#panels.delete(key);
|
|
6595
|
+
}
|
|
6596
|
+
}
|
|
6597
|
+
}
|
|
6598
|
+
/** Панель, отвечающая за уход с этого маршрута, или её отсутствие. */
|
|
6599
|
+
find(route) {
|
|
6600
|
+
const key = route.routeConfig;
|
|
6601
|
+
if (key === null) {
|
|
6602
|
+
return null;
|
|
6603
|
+
}
|
|
6604
|
+
return this.#panels.get(key) ?? null;
|
|
6605
|
+
}
|
|
6606
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtRouteAsideRegistry, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
6607
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtRouteAsideRegistry, providedIn: 'root' }); }
|
|
6608
|
+
}
|
|
6609
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtRouteAsideRegistry, decorators: [{
|
|
6610
|
+
type: Injectable,
|
|
6611
|
+
args: [{ providedIn: 'root' }]
|
|
6612
|
+
}] });
|
|
6613
|
+
|
|
6537
6614
|
/**
|
|
6538
6615
|
* Открытие ждёт `panel.ready()`, а не `ngAfterViewInit`: на F5 rt-container
|
|
6539
6616
|
* создаёт overlay асинхронно, иначе `open()` гонится с ещё не готовым overlay.
|
|
@@ -6563,7 +6640,20 @@ class RtRouteAsideComponent {
|
|
|
6563
6640
|
#notificationBus;
|
|
6564
6641
|
#dialog;
|
|
6565
6642
|
#injector;
|
|
6643
|
+
#registry;
|
|
6566
6644
|
#opened;
|
|
6645
|
+
/**
|
|
6646
|
+
* Уход санкционирован самой панелью, и о правках спрашивать не надо: они либо
|
|
6647
|
+
* записаны, либо пользователь уже решил их судьбу. Признак живёт до первого
|
|
6648
|
+
* же вопроса о правках и снимается вместе с ответом на него.
|
|
6649
|
+
*/
|
|
6650
|
+
#leaveAllowed;
|
|
6651
|
+
/**
|
|
6652
|
+
* Вопрос о правках, заданный роутерному уходу и ещё не отвеченный. Второй
|
|
6653
|
+
* уход, пришедший пока окно открыто, ждёт того же ответа: своё окно он
|
|
6654
|
+
* поставил бы поверх первого, и убрать его было бы некому.
|
|
6655
|
+
*/
|
|
6656
|
+
#pendingAsk$;
|
|
6567
6657
|
constructor() {
|
|
6568
6658
|
this.#entity = signal(null, /* @ts-ignore */
|
|
6569
6659
|
...(ngDevMode ? [{ debugName: "#entity" }] : /* istanbul ignore next */ []));
|
|
@@ -6587,7 +6677,20 @@ class RtRouteAsideComponent {
|
|
|
6587
6677
|
this.#notificationBus = inject(NotificationBus);
|
|
6588
6678
|
this.#dialog = inject(RtDialogService);
|
|
6589
6679
|
this.#injector = inject(Injector);
|
|
6680
|
+
this.#registry = inject(RtRouteAsideRegistry);
|
|
6590
6681
|
this.#opened = false;
|
|
6682
|
+
/**
|
|
6683
|
+
* Уход санкционирован самой панелью, и о правках спрашивать не надо: они либо
|
|
6684
|
+
* записаны, либо пользователь уже решил их судьбу. Признак живёт до первого
|
|
6685
|
+
* же вопроса о правках и снимается вместе с ответом на него.
|
|
6686
|
+
*/
|
|
6687
|
+
this.#leaveAllowed = false;
|
|
6688
|
+
/**
|
|
6689
|
+
* Вопрос о правках, заданный роутерному уходу и ещё не отвеченный. Второй
|
|
6690
|
+
* уход, пришедший пока окно открыто, ждёт того же ответа: своё окно он
|
|
6691
|
+
* поставил бы поверх первого, и убрать его было бы некому.
|
|
6692
|
+
*/
|
|
6693
|
+
this.#pendingAsk$ = null;
|
|
6591
6694
|
this.router = inject(Router);
|
|
6592
6695
|
this.route = inject(ActivatedRoute);
|
|
6593
6696
|
this.destroyRef = inject(DestroyRef);
|
|
@@ -6623,7 +6726,14 @@ class RtRouteAsideComponent {
|
|
|
6623
6726
|
}
|
|
6624
6727
|
this.#opened = true;
|
|
6625
6728
|
panel.open();
|
|
6729
|
+
// Панель встала в аутлет — с этого мгновения о правках спрашивают её,
|
|
6730
|
+
// а не тот компонент, который роутер подставит гарду.
|
|
6731
|
+
this.#registry.register(this.route.snapshot, this);
|
|
6626
6732
|
});
|
|
6733
|
+
// Снятие с учёта идёт по уничтожению, а не по уходу с экрана: уход может и
|
|
6734
|
+
// не состояться — роутер отклоняет навигацию молча, — и панель, снятая с
|
|
6735
|
+
// учёта раньше времени, осталась бы на экране, не отвечая о своих правках.
|
|
6736
|
+
this.destroyRef.onDestroy(() => this.#registry.unregister(this.route.snapshot, this));
|
|
6627
6737
|
// Мутации из runMutation(): mergeMap подписывает каждый op$ независимо
|
|
6628
6738
|
// (как раньше — по подписке на вызов, без отмены предыдущей in-flight
|
|
6629
6739
|
// мутации); handler'ы замыкают opts вызова, catchError гасит ошибку
|
|
@@ -6692,16 +6802,21 @@ class RtRouteAsideComponent {
|
|
|
6692
6802
|
*/
|
|
6693
6803
|
canDeactivate() {
|
|
6694
6804
|
const intent = this.#closeIntent();
|
|
6805
|
+
// Признак идущей записи старше разрешения на уход: панель, выдавшая себе
|
|
6806
|
+
// разрешение и не дождавшаяся ответа сервера, уходить не должна — исход
|
|
6807
|
+
// записи решит её судьбу сам.
|
|
6695
6808
|
if (intent === ERtAsideCloseIntent.Ignore) {
|
|
6696
6809
|
return of(false);
|
|
6697
6810
|
}
|
|
6811
|
+
// Уход начала сама панель: правки либо записаны секунду назад, либо их
|
|
6812
|
+
// судьбу пользователь уже решил. Вопрос был бы задан про то, чего нет.
|
|
6813
|
+
if (this.#consumeLeaveAllowance()) {
|
|
6814
|
+
return of(true);
|
|
6815
|
+
}
|
|
6698
6816
|
if (intent === ERtAsideCloseIntent.Close) {
|
|
6699
6817
|
return of(true);
|
|
6700
6818
|
}
|
|
6701
|
-
return this.#
|
|
6702
|
-
.open(RtAsideUnsavedDialogComponent)
|
|
6703
|
-
.afterClosed()
|
|
6704
|
-
.pipe(switchMap((outcome) => this.#deactivateAfter(outcome)));
|
|
6819
|
+
return this.#askUnsaved();
|
|
6705
6820
|
}
|
|
6706
6821
|
setEntity(entity) {
|
|
6707
6822
|
this.#entity.set(entity);
|
|
@@ -6802,9 +6917,11 @@ class RtRouteAsideComponent {
|
|
|
6802
6917
|
opts.onSuccess();
|
|
6803
6918
|
return;
|
|
6804
6919
|
}
|
|
6805
|
-
// Закрытие после успеха идёт мимо
|
|
6806
|
-
// ещё тронута, и вопрос о
|
|
6807
|
-
// ровно про то, что только что
|
|
6920
|
+
// Закрытие после успеха идёт мимо обоих гардов — и кнопочного, и
|
|
6921
|
+
// роутерного. Форма к этому моменту ещё тронута, и вопрос о
|
|
6922
|
+
// несохранённых правках был бы задан ровно про то, что только что
|
|
6923
|
+
// сохранилось; роутерный гард закрывает разрешение на уход,
|
|
6924
|
+
// которое панель выдаёт себе перед вызовом роутера.
|
|
6808
6925
|
if (opts?.closeOnSuccess === true || this.#isCreateMode()) {
|
|
6809
6926
|
this.#closePanel();
|
|
6810
6927
|
return;
|
|
@@ -6885,6 +7002,53 @@ class RtRouteAsideComponent {
|
|
|
6885
7002
|
}
|
|
6886
7003
|
return toObservable(this.#submitting, { injector: this.#injector }).pipe(filter((submitting) => !submitting), take(1), map(() => this.submitError() === null));
|
|
6887
7004
|
}
|
|
7005
|
+
/**
|
|
7006
|
+
* Вопрос о правках роутерному уходу. Окно открывается одно на все уходы,
|
|
7007
|
+
* пришедшие пока оно висит: второе поставило бы поверх первого оверлей,
|
|
7008
|
+
* убрать который было бы некому — от того же и `exhaustMap` на пути кнопок.
|
|
7009
|
+
*/
|
|
7010
|
+
#askUnsaved() {
|
|
7011
|
+
const pending = this.#pendingAsk$;
|
|
7012
|
+
if (pending !== null) {
|
|
7013
|
+
return pending;
|
|
7014
|
+
}
|
|
7015
|
+
const ask$ = this.#dialog
|
|
7016
|
+
.open(RtAsideUnsavedDialogComponent)
|
|
7017
|
+
.afterClosed()
|
|
7018
|
+
.pipe(switchMap((outcome) => this.#deactivateAfter(outcome)), finalize(() => {
|
|
7019
|
+
this.#pendingAsk$ = null;
|
|
7020
|
+
}), shareReplay({ bufferSize: 1, refCount: false }));
|
|
7021
|
+
this.#pendingAsk$ = ask$;
|
|
7022
|
+
return ask$;
|
|
7023
|
+
}
|
|
7024
|
+
/**
|
|
7025
|
+
* Разрешение на уход читается один раз: иначе первое же закрытие после записи
|
|
7026
|
+
* сняло бы вопрос о правках со всех следующих уходов, и тронутая форма
|
|
7027
|
+
* уезжала бы молча.
|
|
7028
|
+
*/
|
|
7029
|
+
#consumeLeaveAllowance() {
|
|
7030
|
+
const allowed = this.#leaveAllowed;
|
|
7031
|
+
this.#leaveAllowed = false;
|
|
7032
|
+
return allowed;
|
|
7033
|
+
}
|
|
7034
|
+
/**
|
|
7035
|
+
* Уход, начатый самой панелью. Разрешение выдаётся здесь, вплотную к вызову
|
|
7036
|
+
* роутера, а не при закрытии панели: между закрытием и навигацией оверлей
|
|
7037
|
+
* доигрывает уход, и разрешение, выданное раньше, достаётся чужому уходу,
|
|
7038
|
+
* пришедшему в это окно, — тронутая форма уехала бы без вопроса.
|
|
7039
|
+
*
|
|
7040
|
+
* Невостребованное разрешение снимается итогом навигации: роутер отклоняет
|
|
7041
|
+
* навигацию молча, её может отменить другой гард, и оставшееся разрешение
|
|
7042
|
+
* сняло бы вопрос о правках со следующего ухода, которого панель не начинала.
|
|
7043
|
+
*/
|
|
7044
|
+
#navigateAllowed(navigate) {
|
|
7045
|
+
this.#leaveAllowed = true;
|
|
7046
|
+
void navigate()
|
|
7047
|
+
.catch(() => false)
|
|
7048
|
+
.then(() => {
|
|
7049
|
+
this.#leaveAllowed = false;
|
|
7050
|
+
});
|
|
7051
|
+
}
|
|
6888
7052
|
#closePanel() {
|
|
6889
7053
|
this.panel()?.close();
|
|
6890
7054
|
}
|
|
@@ -6892,7 +7056,7 @@ class RtRouteAsideComponent {
|
|
|
6892
7056
|
const params = queryParams ?? this.#relatedQueryParams;
|
|
6893
7057
|
this.#relatedCommands = null;
|
|
6894
7058
|
this.#relatedQueryParams = null;
|
|
6895
|
-
|
|
7059
|
+
this.#navigateAllowed(() => this.router.navigateByUrl(this.#relatedTree(commands, params)));
|
|
6896
7060
|
}
|
|
6897
7061
|
/**
|
|
6898
7062
|
* Адрес, на который уходит панель. Свой аутлет она снимает здесь же, тем же
|
|
@@ -6931,9 +7095,9 @@ class RtRouteAsideComponent {
|
|
|
6931
7095
|
this.#relatedCommands = null;
|
|
6932
7096
|
}
|
|
6933
7097
|
#navigateAway() {
|
|
6934
|
-
|
|
7098
|
+
this.#navigateAllowed(() => this.router.navigate([{ outlets: this.closeOutlets() }], {
|
|
6935
7099
|
relativeTo: this.route.parent,
|
|
6936
|
-
});
|
|
7100
|
+
}));
|
|
6937
7101
|
}
|
|
6938
7102
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtRouteAsideComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
6939
7103
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.2.0", version: "22.0.5", type: RtRouteAsideComponent, isStandalone: true, viewQueries: [{ propertyName: "panel", first: true, predicate: RtContainerRightSidenavPanelDirective, descendants: true, isSignal: true }], ngImport: i0 }); }
|
|
@@ -6950,12 +7114,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
6950
7114
|
* Ставится на каждый ro-маршрут: гард висит на кнопках, нажатии мимо и Esc, а
|
|
6951
7115
|
* смену маршрута ловит только роутер.
|
|
6952
7116
|
*
|
|
6953
|
-
*
|
|
6954
|
-
*
|
|
6955
|
-
*
|
|
6956
|
-
*
|
|
6957
|
-
|
|
6958
|
-
|
|
7117
|
+
* Спрашивается панель, учтённая под маршрутом ухода, а не компонент, который
|
|
7118
|
+
* подставил роутер. Компонент — не панель, когда ro-маршрут объявлен под уровнем
|
|
7119
|
+
* без своего компонента: Angular отдаёт детям контекст родителя, и в гард
|
|
7120
|
+
* приходит компонент первичного аутлета. Ответ такого компонента к правкам
|
|
7121
|
+
* отношения не имеет, а вызов у него метода, которого нет, ронял навигацию — она
|
|
7122
|
+
* падала необработанным отказом, адрес не менялся, и приложение оставалось
|
|
7123
|
+
* запертым на панели.
|
|
7124
|
+
*
|
|
7125
|
+
* Компонент от роутера остаётся запасным путём: гард выведен наружу кита, и
|
|
7126
|
+
* панель приложения может отвечать о правках, не наследуя базовый класс, — на
|
|
7127
|
+
* учёт такая панель не встаёт никогда. Без запасного пути у неё вопрос о правках
|
|
7128
|
+
* исчез бы молча, а это тихая потеря правок.
|
|
7129
|
+
*
|
|
7130
|
+
* Спрашивать некого — уступить место можно: роутер зовёт гард и для маршрута,
|
|
7131
|
+
* чей компонент не создан. Без этого первая же такая навигация падает, а следом
|
|
7132
|
+
* отказывает любая другая.
|
|
7133
|
+
*
|
|
7134
|
+
* Отказом навигации гард не отвечает никогда, в том числе когда ответ панели
|
|
7135
|
+
* отказал: разрешить уход значило бы унести правки, о которых спросить не
|
|
7136
|
+
* удалось, а отменить и оставить всё как есть — запереть приложение на панели
|
|
7137
|
+
* навсегда. Поэтому уход отменяется один раз, а панель снимается с учёта, и
|
|
7138
|
+
* следующий уход идёт запасным путём.
|
|
7139
|
+
*/
|
|
7140
|
+
/** Компонент от роутера годится в ответчики, только если умеет отвечать. */
|
|
7141
|
+
function fallbackPanel(component) {
|
|
7142
|
+
return typeof component?.canDeactivate === 'function' ? component : null;
|
|
7143
|
+
}
|
|
7144
|
+
const rtAsideUnsavedGuard = (component, currentRoute) => {
|
|
7145
|
+
const registry = inject(RtRouteAsideRegistry);
|
|
7146
|
+
const registered = registry.find(currentRoute);
|
|
7147
|
+
const panel = registered ?? fallbackPanel(component);
|
|
7148
|
+
if (panel === null) {
|
|
7149
|
+
return of(true);
|
|
7150
|
+
}
|
|
7151
|
+
// defer: ответ панели может отказать и синхронным броском, а гард обязан
|
|
7152
|
+
// пережить и его.
|
|
7153
|
+
return defer(() => panel.canDeactivate()).pipe(catchError(() => {
|
|
7154
|
+
registry.forget(panel);
|
|
7155
|
+
return of(false);
|
|
7156
|
+
}));
|
|
7157
|
+
};
|
|
6959
7158
|
|
|
6960
7159
|
/** Контракт счётчика common/ui. */
|
|
6961
7160
|
/**
|
|
@@ -11221,11 +11420,11 @@ class RtTableSortHeaderComponent {
|
|
|
11221
11420
|
this.#table.toggleSort(this.rtSortHeader());
|
|
11222
11421
|
}
|
|
11223
11422
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtTableSortHeaderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
11224
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtTableSortHeaderComponent, isStandalone: true, selector: "rt-table-sort-header, th[rtSortHeader]", inputs: { rtSortHeader: { classPropertyName: "rtSortHeader", publicName: "rtSortHeader", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.aria-sort": "ariaSort()" }, classAttribute: "rt-table-sort-header" }, ngImport: i0, template: "<ng-template #label>\n <ng-content />\n</ng-template>\n\n@if (sortable()) {\n
|
|
11423
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: RtTableSortHeaderComponent, isStandalone: true, selector: "rt-table-sort-header, th[rtSortHeader]", inputs: { rtSortHeader: { classPropertyName: "rtSortHeader", publicName: "rtSortHeader", isSignal: true, isRequired: true, transformFunction: null } }, host: { properties: { "attr.aria-sort": "ariaSort()" }, classAttribute: "rt-table-sort-header" }, ngImport: i0, template: "<ng-template #label>\n <ng-content />\n</ng-template>\n\n<ng-container rtBlock=\"rt-table-sort-header\">\n @if (sortable()) {\n <button\n rtElem=\"button\"\n qa-dataid=\"table-sort-header\"\n type=\"button\"\n [attr.data-column]=\"rtSortHeader()\"\n [attr.data-direction]=\"direction()\"\n [rtMod]=\"{ active: direction() !== null }\"\n (click)=\"onToggle()\">\n <ng-container [ngTemplateOutlet]=\"label\" />\n <rt-icon rtElem=\"icon\" size=\"xs\" [name]=\"icon()\" />\n </button>\n } @else {\n <ng-container [ngTemplateOutlet]=\"label\" />\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";.rt-table-sort-header__button{display:inline-flex;align-items:center;padding:0;border:none;background:none;color:inherit;cursor:pointer;font:inherit;gap:var(--rt-space-xs);text-align:inherit}.rt-table-sort-header__button:hover{color:var(--rt-color-text-primary)}.rt-table-sort-header__button:focus-visible{outline:2px solid var(--rt-color-action-primary);outline-offset:2px}.rt-table-sort-header__icon{flex:0 0 auto;color:var(--rt-color-text-muted)}.rt-table-sort-header__button--active{color:var(--rt-color-text-primary)}.rt-table-sort-header__button--active .rt-table-sort-header__icon{color:var(--rt-color-action-primary)}\n"], dependencies: [{ kind: "directive", type:
|
|
11225
11424
|
// angular
|
|
11226
11425
|
NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
11227
11426
|
// rt-tools
|
|
11228
|
-
ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type:
|
|
11427
|
+
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type:
|
|
11229
11428
|
// components
|
|
11230
11429
|
RtIconComponent, selector: "rt-icon", inputs: ["name", "size", "color", "rotate"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
11231
11430
|
}
|
|
@@ -11235,6 +11434,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
11235
11434
|
// angular
|
|
11236
11435
|
NgTemplateOutlet,
|
|
11237
11436
|
// rt-tools
|
|
11437
|
+
BlockDirective,
|
|
11238
11438
|
ElemDirective,
|
|
11239
11439
|
ModDirective,
|
|
11240
11440
|
// components
|
|
@@ -11242,7 +11442,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
|
|
|
11242
11442
|
], host: {
|
|
11243
11443
|
class: BEM_BLOCK$b,
|
|
11244
11444
|
'[attr.aria-sort]': 'ariaSort()',
|
|
11245
|
-
}, template: "<ng-template #label>\n <ng-content />\n</ng-template>\n\n@if (sortable()) {\n
|
|
11445
|
+
}, template: "<ng-template #label>\n <ng-content />\n</ng-template>\n\n<ng-container rtBlock=\"rt-table-sort-header\">\n @if (sortable()) {\n <button\n rtElem=\"button\"\n qa-dataid=\"table-sort-header\"\n type=\"button\"\n [attr.data-column]=\"rtSortHeader()\"\n [attr.data-direction]=\"direction()\"\n [rtMod]=\"{ active: direction() !== null }\"\n (click)=\"onToggle()\">\n <ng-container [ngTemplateOutlet]=\"label\" />\n <rt-icon rtElem=\"icon\" size=\"xs\" [name]=\"icon()\" />\n </button>\n } @else {\n <ng-container [ngTemplateOutlet]=\"label\" />\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";.rt-table-sort-header__button{display:inline-flex;align-items:center;padding:0;border:none;background:none;color:inherit;cursor:pointer;font:inherit;gap:var(--rt-space-xs);text-align:inherit}.rt-table-sort-header__button:hover{color:var(--rt-color-text-primary)}.rt-table-sort-header__button:focus-visible{outline:2px solid var(--rt-color-action-primary);outline-offset:2px}.rt-table-sort-header__icon{flex:0 0 auto;color:var(--rt-color-text-muted)}.rt-table-sort-header__button--active{color:var(--rt-color-text-primary)}.rt-table-sort-header__button--active .rt-table-sort-header__icon{color:var(--rt-color-action-primary)}\n"] }]
|
|
11246
11446
|
}], propDecorators: { rtSortHeader: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtSortHeader", required: true }] }] } });
|
|
11247
11447
|
|
|
11248
11448
|
/**
|
|
@@ -11543,8 +11743,15 @@ class RtTabsControlDirective {
|
|
|
11543
11743
|
constructor() {
|
|
11544
11744
|
/** Шаблон контрола, выводимый в header-слоте. */
|
|
11545
11745
|
this.templateRef = inject(TemplateRef);
|
|
11546
|
-
/**
|
|
11547
|
-
|
|
11746
|
+
/**
|
|
11747
|
+
* Сторона размещения относительно полосы вкладок.
|
|
11748
|
+
*
|
|
11749
|
+
* Пустое значение приводится к правой стороне: `<ng-template rtTabsControl>` без значения
|
|
11750
|
+
* задаёт входу пустую строку, а не умолчание, и такой контрол не попадал ни в левый слот,
|
|
11751
|
+
* ни в правый — он пропадал из шапки вовсе.
|
|
11752
|
+
*/
|
|
11753
|
+
this.side = input('right', { ...(ngDevMode ? { debugName: "side" } : /* istanbul ignore next */ {}), alias: 'rtTabsControl',
|
|
11754
|
+
transform: (value) => (value === '' ? 'right' : value) });
|
|
11548
11755
|
}
|
|
11549
11756
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: RtTabsControlDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
11550
11757
|
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.5", type: RtTabsControlDirective, isStandalone: true, selector: "[rtTabsControl]", inputs: { side: { classPropertyName: "side", publicName: "rtTabsControl", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); }
|