@rt-tools/ui-kit-v2 0.28.0 → 0.29.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-calendar.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-container.mjs +9 -4
- package/fesm2022/rt-tools-ui-kit-v2-container.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +37 -4
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +367 -23
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +4 -0
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +4 -1
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +2 -2
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
- package/package.json +1 -1
- package/src/lib/components/calendar/rt-calendar.component.scss +3 -2
- package/src/lib/components/container/CONTEXT.md +6 -0
- package/src/lib/components/container/rt-container.component.scss +4 -0
- package/src/lib/components/data-list/toolbar/rt-data-list-toolbar.component.scss +1 -1
- package/src/lib/components/dynamic-list/rt-dynamic-list.component.scss +1 -1
- package/src/lib/components/filter-control/rt-filter-control.component.scss +4 -2
- package/src/lib/components/page-header/CONTEXT.md +12 -0
- package/src/lib/components/page-header/more/rt-page-header-more.component.scss +7 -0
- package/src/lib/components/page-header/rt-page-header-mobile.scss +24 -0
- package/src/lib/components/page-header/rt-page-header.component.scss +25 -3
- package/src/lib/components/pdf-viewer/rt-pdf-viewer.component.scss +1 -1
- package/src/lib/components/popover/CONTEXT.md +2 -1
- package/src/lib/components/toolbar/rt-toolbar.component.scss +3 -1
- package/src/lib/components/workspace/rt-workspace.component.scss +8 -6
- package/src/rich-editor/lib/components/chat/rt-chat.component.scss +5 -3
- package/src/styles/_breakpoints.scss +19 -0
- package/src/styles/_index.scss +4 -0
- package/src/styles/_place.scss +7 -0
- package/styles/tokens.css +8 -0
- package/types/rt-tools-ui-kit-v2-core.d.ts +24 -5
- package/types/rt-tools-ui-kit-v2-page-header.d.ts +60 -6
- package/types/rt-tools-ui-kit-v2-table.d.ts +4 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rt-tools-ui-kit-v2-container.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/container/rt-container-state.service.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.markers.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.component.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.component.html","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.directives.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.mutations.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.logic.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.navigation.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.unsaved.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.pristine.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.registry.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.base.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.guard.ts"],"sourcesContent":["import { Injectable, WritableSignal, signal } from '@angular/core';\n\n/**\n * Приватный signal-store для `rt-container`.\n *\n * Хранит open-state левой docked sidenav. Driver-директива\n * `[rtContainerLeftSidenavPanel]` пишет в `leftSidenavOpen`, шаблон\n * `RtContainerComponent` читает через signal-tracking.\n *\n * Provided component-level через `providers: [RtContainerStateService]` на\n * `RtContainerComponent` — per-instance state, без singleton'а. Pure signal IO\n * с автоматической reactivity.\n *\n * Правая sidenav (overlay) НЕ хранится здесь: её open-state живёт прямо в\n * `RtContainerComponent.rightOpen` сигнале и управляется через `openRight()` /\n * `closeRight()` методы + lifecycle outputs.\n */\n@Injectable()\nexport class RtContainerStateService {\n public readonly leftSidenavOpen: WritableSignal<boolean> = signal<boolean>(true);\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Маркеры слотов оболочки — своим файлом, а не рядом с панельными директивами.\n *\n * Панельные директивы берут сам компонент оболочки значением, а компонент объявляет маркеры в\n * своих импортах: вместе это круг «оболочка → директивы → оболочка». Маркеры пусты и не знают\n * об оболочке ничего, поэтому расходятся с ней без потерь.\n */\n\n/**\n * Маркер-директива для содержимого хедера `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerHeader>\n * <rt-header />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerHeader]',\n})\nexport class RtContainerHeaderDirective {}\n\n/**\n * Маркер-директива для контента левой sidenav в `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerLeftSidenav>\n * <router-outlet name=\"lo\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerLeftSidenav]',\n})\nexport class RtContainerLeftSidenavDirective {}\n\n/**\n * Маркер-директива для контента правой sidenav в `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerRightSidenav>\n * <router-outlet name=\"ro\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerRightSidenav]',\n})\nexport class RtContainerRightSidenavDirective {}\n\n/**\n * Маркер-директива для контента в левой секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarLeft>\n * <router-outlet name=\"lb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarLeft]',\n})\nexport class RtContainerToolbarLeftDirective {}\n\n/**\n * Маркер-директива для контента в центральной секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarCenter>\n * <router-outlet name=\"cb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarCenter]',\n})\nexport class RtContainerToolbarCenterDirective {}\n\n/**\n * Маркер-директива для контента в правой секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarRight>\n * <router-outlet name=\"rb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarRight]',\n})\nexport class RtContainerToolbarRightDirective {}\n\n/**\n * Маркер-директива для основного контента `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerContent>\n * <router-outlet />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerContent]',\n})\nexport class RtContainerContentDirective {}\n","import { BlockScrollStrategy, Overlay, OverlayConfig, OverlayRef, PositionStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { DOCUMENT, NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n InputSignal,\n OutputEmitterRef,\n Renderer2,\n RendererFactory2,\n Signal,\n TemplateRef,\n ViewContainerRef,\n ViewEncapsulation,\n WritableSignal,\n computed,\n contentChild,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { map, merge, Observable, Subject, switchMap, takeUntil } from 'rxjs';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { carryThemeScope, materialPresetClassesOf } from '@rt-tools/ui-kit-v2/core';\nimport { RtToasterComponent } from '@rt-tools/ui-kit-v2/toast';\nimport { RtContainerStateService } from './rt-container-state.service';\nimport {\n RtContainerContentDirective,\n RtContainerHeaderDirective,\n RtContainerLeftSidenavDirective,\n RtContainerRightSidenavDirective,\n RtContainerToolbarCenterDirective,\n RtContainerToolbarLeftDirective,\n RtContainerToolbarRightDirective,\n} from './rt-container.markers';\n\n/** Запас поверх длительности transition'а для страховочного таймера. */\nconst ASIDE_TRANSITION_BUFFER_MS: number = 80;\n\nconst BEM_BLOCK: string = 'rt-container';\n\n/**\n * Multi-slot container с CSS-grid layout. Slot consumers wrap content в\n * `*rtContainerXxx` structural directives, контейнер подхватывает шаблоны через\n * `contentChild(..., { read: TemplateRef })` + рендерит через `*ngTemplateOutlet`.\n *\n * Slots: `header`, `leftSidenav`, `toolbarLeft/Center/Right`, `content` — живут в\n * CSS-grid. `rightSidenav` рендерится через CDK Overlay поверх grid'а (slide-in\n * side-sheet), а не как grid-cell — overlay переиспользует глобальные\n * `rt-aside-overlay` / `rt-aside-backdrop` классы из rt-aside, чтобы анимация и\n * backdrop вели себя идентично программно открываемым asides.\n *\n * Driver-directives (`[rtContainerLeftSidenavPanel/RightSidenavPanel]`) общаются с\n * контейнером через `RtContainerStateService` (для левой docked) и через\n * `openRight()/closeRight()` + lifecycle outputs (для правой overlay).\n */\n@Component({\n selector: 'rt-container',\n templateUrl: './rt-container.component.html',\n styleUrls: ['./rt-container.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtToasterComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [RtContainerStateService],\n host: {\n class: BEM_BLOCK,\n '[class.rt-container--mobile-left-bottom]': \"mobileLeftNav() === 'bottom'\",\n '[class.rt-container--viewport]': \"height() === 'viewport'\",\n },\n})\nexport class RtContainerComponent {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #host: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #vcr: ViewContainerRef = inject(ViewContainerRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #blockScroll: BlockScrollStrategy = this.#scrollStrategies.block();\n readonly #renderer: Renderer2 = inject(RendererFactory2).createRenderer(null, null);\n readonly #window: Window | null = inject(DOCUMENT).defaultView;\n\n readonly #rightOverlaySource: Subject<OverlayRef> = new Subject<OverlayRef>();\n\n #overlayRef: OverlayRef | null = null;\n\n readonly #overlayRefSignal: WritableSignal<OverlayRef | null> = signal<OverlayRef | null>(null);\n\n protected readonly headerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerHeaderDirective, {\n read: TemplateRef,\n });\n\n protected readonly leftSidenavTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerLeftSidenavDirective, {\n read: TemplateRef,\n });\n\n protected readonly rightSidenavTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerRightSidenavDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarLeftTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarLeftDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarCenterTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarCenterDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarRightTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarRightDirective, {\n read: TemplateRef,\n });\n\n protected readonly contentTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerContentDirective, {\n read: TemplateRef,\n });\n\n /**\n * Поведение левого sidenav на мобилке (<=768px): `keep` — остаётся боковой\n * колонкой (по умолчанию, десктоп-стиль); `bottom` — сворачивается в нижний\n * ряд grid'а (таб-бар), контент занимает всю ширину и скроллится над ним.\n */\n public readonly mobileLeftNav: InputSignal<'keep' | 'bottom'> = input<'keep' | 'bottom'>('keep');\n\n /**\n * Высота контейнера: `auto` — растёт под контент, скроллится документ (по\n * умолчанию; на этом держится sticky-хедер); `viewport` — контейнер жёстко\n * равен вьюпорту, страница не скроллится вовсе, а скроллятся зоны внутри —\n * каждая сама по себе (app-shell раскладка, напр. чаты бот-панели).\n */\n public readonly height: InputSignal<'auto' | 'viewport'> = input<'auto' | 'viewport'>('auto');\n\n public readonly backdropClick: OutputEmitterRef<void> = output<void>();\n\n public readonly rightOpened: OutputEmitterRef<void> = output<void>();\n\n public readonly rightClosed: OutputEmitterRef<void> = output<void>();\n\n public readonly rightOpen: WritableSignal<boolean> = signal<boolean>(false);\n\n /**\n * `true` когда CDK Overlay для правой sidenav создан и attache'нут (см.\n * effect в constructor'е). Дёргать panel.open() из консьюмера можно только\n * когда это true — иначе openRight() видит null overlayRef и early-return'ит,\n * --entering modifier остаётся, панель остаётся за экраном. Re-exposed на\n * driver через `panel.ready` для удобства route-driven dialog'ов.\n */\n public readonly rightOverlayReady: Signal<boolean> = computed((): boolean => this.#overlayRefSignal() !== null);\n\n constructor() {\n // Backdrop-click / ESC подписки правого overlay живут в одном\n // конструкторном стриме: #createRightOverlay эмитит созданный OverlayRef,\n // switchMap переключается на его event-стримы (предыдущий overlay к этому\n // моменту уже dispose'нут effect-cleanup'ом; `detachments()` терминирует\n // внутренний поток при dispose).\n this.#rightOverlaySource\n .pipe(\n switchMap((overlayRef: OverlayRef): Observable<() => void> =>\n merge(\n overlayRef.backdropClick().pipe(map((): (() => void) => (): void => this.backdropClick.emit())),\n overlayRef.keydownEvents().pipe(\n map((event: KeyboardEvent): (() => void) => (): void => {\n if (event.key !== 'Escape' || !this.rightOpen()) {\n return;\n }\n event.preventDefault();\n this.backdropClick.emit();\n })\n )\n ).pipe(takeUntil(overlayRef.detachments()))\n ),\n takeUntilDestroyed()\n )\n .subscribe((handleCloseIntent: () => void): void => handleCloseIntent());\n\n // Persistent overlay: создаётся при появлении rightSidenavTpl и живёт\n // до уничтожения rt-container'а. Это нужно чтобы <router-outlet name=\"ro\">\n // внутри slot template'а материализовался и мог активировать route.\n // Без auto-attach landmine: route не активируется → driver не вызывается\n // → ничего не открывается (chicken-and-egg).\n effect((onCleanup: (cleanup: () => void) => void): void => {\n const tpl: TemplateRef<unknown> | undefined = this.rightSidenavTpl();\n\n if (tpl === undefined || this.#overlayRef !== null) {\n return;\n }\n\n const overlayRef: OverlayRef = this.#createRightOverlay(tpl);\n this.#overlayRef = overlayRef;\n this.#overlayRefSignal.set(overlayRef);\n\n onCleanup((): void => {\n overlayRef.dispose();\n this.#overlayRef = null;\n this.#overlayRefSignal.set(null);\n });\n });\n }\n\n public openRight(): void {\n const overlayRef: OverlayRef | null = this.#overlayRef;\n if (overlayRef === null) {\n return;\n }\n\n this.#blockScroll.enable();\n // Коробка постоянная и лежит в конце страницы, а кусок темы вокруг контейнера мог смениться.\n carryThemeScope(overlayRef.overlayElement, this.#host.nativeElement);\n\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--entering');\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--leaving');\n\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n if (backdropEl !== null) {\n this.#renderer.addClass(backdropEl, 'rt-aside-backdrop--visible');\n this.#renderer.removeStyle(backdropEl, 'opacity');\n this.#renderer.removeStyle(backdropEl, 'pointer-events');\n }\n\n // Slide-in: на следующий paint добавляем --open.\n requestAnimationFrame((): void => {\n this.#renderer.addClass(overlayRef.overlayElement, 'rt-aside-overlay--open');\n this.#listenTransitionEnd(overlayRef, (): void => this.rightOpened.emit());\n });\n\n this.rightOpen.set(true);\n }\n\n public closeRight(): void {\n const overlayRef: OverlayRef | null = this.#overlayRef;\n if (overlayRef === null || !this.rightOpen()) {\n return;\n }\n\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--open');\n this.#renderer.addClass(overlayRef.overlayElement, 'rt-aside-overlay--leaving');\n\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n if (backdropEl !== null) {\n this.#renderer.removeClass(backdropEl, 'rt-aside-backdrop--visible');\n }\n\n this.#listenTransitionEnd(overlayRef, (): void => {\n this.#blockScroll.disable();\n // Объявления на узле возвращаются только после ухода: поставленные сразу, они\n // обрывают затухание подложки на первом же кадре.\n this.#hideBackdrop(overlayRef);\n this.rightOpen.set(false);\n this.rightClosed.emit();\n });\n }\n\n /**\n * Подложка закрытой панели: невидима и нажатий не ловит.\n *\n * Стилями этого не сделать. Правила кита живут в слое каскада, а CDK кладёт свои прямо на\n * страницу, вне слоя, — и `.cdk-overlay-backdrop-showing { opacity: 1 }` выигрывает у нашего\n * `opacity: 0` независимо от специфичности: неслоевое правило сильнее слоевого всегда.\n * Подложка при этом висит поверх страницы во весь экран — прозрачная и перехватывающая\n * нажатия, а панель ещё не открывали. Объявление на самом узле сильнее обоих правил.\n *\n * Держится это ровно до открытия: там объявления снимаются, и дальше видом подложки\n * заведуют классы.\n */\n #hideBackdrop(overlayRef: OverlayRef): void {\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n\n if (backdropEl === null) {\n return;\n }\n\n this.#renderer.setStyle(backdropEl, 'opacity', '0');\n this.#renderer.setStyle(backdropEl, 'pointer-events', 'none');\n }\n\n #createRightOverlay(tpl: TemplateRef<unknown>): OverlayRef {\n const positionStrategy: PositionStrategy = this.#overlay.position().global().right('0').top('0');\n\n const overlayConfig: OverlayConfig = {\n positionStrategy,\n // noop здесь, scroll-block управляется вручную через #blockScroll\n // в openRight/closeRight — иначе scroll заблокирован всегда\n // (overlay persistent), пока aside ещё не открыт.\n scrollStrategy: this.#overlay.scrollStrategies.noop(),\n hasBackdrop: true,\n backdropClass: 'rt-aside-backdrop',\n panelClass: [\n 'rt-aside-overlay',\n 'rt-aside-overlay--position-right',\n 'rt-aside-overlay--entering',\n ...materialPresetClassesOf(this.#host.nativeElement),\n ],\n disposeOnNavigation: false,\n };\n\n const overlayRef: OverlayRef = this.#overlay.create(overlayConfig);\n const portal: TemplatePortal = new TemplatePortal(tpl, this.#vcr);\n overlayRef.attach(portal);\n this.#hideBackdrop(overlayRef);\n\n // Backdrop-click / ESC подписки объявлены один раз в конструкторе —\n // здесь только эмит созданного OverlayRef (см. #rightOverlaySource-стрим).\n this.#rightOverlaySource.next(overlayRef);\n\n return overlayRef;\n }\n\n // Слушаем именно transform-окончание: на overlay host'е могут анимироваться\n // и другие свойства (opacity backdrop'а, например) — это даст ложные срабатывания.\n #listenTransitionEnd(overlayRef: OverlayRef, callback: () => void): void {\n const element: HTMLElement = overlayRef.overlayElement;\n\n // Нет анимации (reduced-motion / `transition: none`) → `transitionend` не\n // придёт; teardown-callback (навигация `ro:null`) отрабатываем синхронно.\n const durationMs: number = this.#transitionDurationMs(element);\n if (durationMs === 0) {\n callback();\n return;\n }\n\n // settled-гард делает transitionend и fallback-таймаут взаимно\n // идемпотентными — сработает и снимет listener только то, что раньше.\n let settled: boolean = false;\n\n const handler: (event: TransitionEvent) => void = (event: TransitionEvent): void => {\n if (event.propertyName !== 'transform' || settled) {\n return;\n }\n settled = true;\n element.removeEventListener('transitionend', handler);\n callback();\n };\n element.addEventListener('transitionend', handler);\n\n // Страховка на случай пропущенного `transitionend` (лаг рендера).\n setTimeout((): void => {\n if (settled) {\n return;\n }\n settled = true;\n element.removeEventListener('transitionend', handler);\n callback();\n }, durationMs + ASIDE_TRANSITION_BUFFER_MS);\n }\n\n // Максимальная длительность transition'а элемента в мс; `0` если анимации нет.\n #transitionDurationMs(element: HTMLElement): number {\n const durationCss: string | undefined = this.#window?.getComputedStyle(element).transitionDuration;\n if (durationCss === undefined || durationCss === '') {\n return 0;\n }\n const maxSeconds: number = durationCss.split(',').reduce((max: number, part: string): number => {\n const trimmed: string = part.trim();\n const seconds: number = trimmed.endsWith('ms') ? Number.parseFloat(trimmed) / 1000 : Number.parseFloat(trimmed);\n return Number.isNaN(seconds) ? max : Math.max(max, seconds);\n }, 0);\n return maxSeconds * 1000;\n }\n}\n","<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","// Маркеры слотов переехали в соседний файл — иначе оболочка и панельные директивы замыкали\n// круг импортов. Здесь они остаются видимыми прежним потребителям.\nexport * from './rt-container.markers';\n\nimport { DestroyRef, Directive, OnDestroy, OutputEmitterRef, Signal, inject, output } from '@angular/core';\nimport { outputToObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { RtContainerStateService } from './rt-container-state.service';\nimport { RtContainerComponent } from './rt-container.component';\n\n/**\n * Driver-директива для управления левой sidenav через шаблонную ссылку.\n *\n * Пишет в component-scoped `RtContainerStateService.leftSidenavOpen` signal —\n * шаблон контейнера читает этот signal и применяет docked grid-cell состояние.\n *\n * @example\n * ```html\n * <ng-container rtContainerLeftSidenavPanel #left=\"containerLeftPanel\">\n * <button (click)=\"left.toggle(true)\">Open</button>\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerLeftSidenavPanel]',\n exportAs: 'containerLeftPanel',\n})\nexport class RtContainerLeftSidenavPanelDirective {\n readonly #state: RtContainerStateService = inject(RtContainerStateService);\n\n public open(): void {\n this.#state.leftSidenavOpen.set(true);\n }\n\n public close(): void {\n this.#state.leftSidenavOpen.set(false);\n }\n\n public toggle(isOpen: boolean): void {\n this.#state.leftSidenavOpen.set(isOpen);\n }\n}\n\n/**\n * Driver-директива правой sidenav: route-component вызывает `open()`/`close()`,\n * rt-container выполняет slide-in/slide-out через CSS transform. После завершения\n * анимации driver эмитит `(opened)` / `(closed)`; backdrop click и ESC контейнера\n * эмитятся через `(cancelled)` — консьюмер сам решает что делать (обычно `close()`).\n *\n * Sub'ы через `takeUntilDestroyed` снимаются при разрушении route-component'а.\n * При уничтожении с открытой панелью гарантирует cleanup overlay'я (см. `ngOnDestroy`),\n * иначе при hard-navigate без `panel.close()` overlay останется висеть.\n *\n * @example\n * ```html\n * <ng-container\n * rtContainerRightSidenavPanel\n * #right=\"rtContainerRightPanel\"\n * (cancelled)=\"onClose()\"\n * (closed)=\"onClosed()\">\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerRightSidenavPanel]',\n exportAs: 'rtContainerRightPanel',\n})\nexport class RtContainerRightSidenavPanelDirective implements OnDestroy {\n readonly #container: RtContainerComponent = inject(RtContainerComponent);\n\n public readonly opened: OutputEmitterRef<void> = output<void>();\n\n public readonly closed: OutputEmitterRef<void> = output<void>();\n\n public readonly cancelled: OutputEmitterRef<void> = output<void>();\n\n /**\n * Re-export rt-container.rightOverlayReady — `true` когда CDK Overlay создан\n * и attache'нут. Консьюмер должен ждать этого signal'а в effect'е, перед\n * тем как вызывать `panel.open()` (иначе race: rt-container.effect ещё не\n * создал overlay → openRight() рано вернётся → --entering остаётся).\n */\n public readonly ready: Signal<boolean> = this.#container.rightOverlayReady;\n\n constructor() {\n const destroyRef: DestroyRef = inject(DestroyRef);\n\n // `output()` возвращает OutputEmitterRef (нет .pipe) — оборачиваем через\n // outputToObservable, чтобы пристегнуть takeUntilDestroyed.\n outputToObservable(this.#container.backdropClick)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.cancelled.emit());\n\n outputToObservable(this.#container.rightOpened)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.opened.emit());\n\n outputToObservable(this.#container.rightClosed)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.closed.emit());\n }\n\n public ngOnDestroy(): void {\n // Если route-component разрушается при открытой панели (hard-navigate без\n // явного close через panel.close) — гарантируем cleanup, иначе overlay\n // останется висеть после смены роута.\n if (this.#container.rightOpen()) {\n this.#container.closeRight();\n }\n }\n\n public open(): void {\n this.#container.openRight();\n }\n\n public close(): void {\n this.#container.closeRight();\n }\n}\n\n/**\n * Driver-директива для основного content-региона `rt-container`.\n *\n * Объявляет 3 outputs (`scrolled/scrolledUp/scrolledDown`) — публичную поверхность\n * для будущих consumer'ов. Real-time wiring (scroll listeners → emit) отложено\n * до первого реального потребителя.\n *\n * @example\n * ```html\n * <ng-container\n * rtContainerContentPanel\n * (scrolled)=\"onScroll()\"\n * >\n * <router-outlet />\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerContentPanel]',\n})\nexport class RtContainerContentPanelDirective {\n public readonly scrolled: OutputEmitterRef<void> = output<void>();\n\n public readonly scrolledUp: OutputEmitterRef<void> = output<void>();\n\n public readonly scrolledDown: OutputEmitterRef<void> = output<void>();\n}\n","import { DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { catchError, map, mergeMap, Observable, of, Subject } from 'rxjs';\n\n/** Одна запущенная правка: сам поток работы и приёмы её удачи и отказа. */\nexport interface IRtRouteAsideMutation {\n op$: Observable<unknown>;\n handleSuccess: () => void;\n handleError: (error: unknown) => void;\n}\n\n/**\n * Правки, запущенные панелью.\n *\n * Подписка объявлена одна на панель, а не заводится на каждый вызов: каждая правка живёт\n * независимо от соседней, и отказ одной не уносит поток остальных. Приёмы удачи и отказа\n * приезжают вместе с работой — они замыкают доводы своего вызова.\n */\nexport class RtRouteAsideMutations {\n readonly #mutationSource: Subject<IRtRouteAsideMutation> = new Subject<IRtRouteAsideMutation>();\n\n constructor(destroyRef: DestroyRef) {\n this.#mutationSource\n .pipe(\n mergeMap((mutation: IRtRouteAsideMutation): Observable<() => void> =>\n mutation.op$.pipe(\n map((): (() => void) => mutation.handleSuccess),\n catchError((error: unknown): Observable<() => void> => of((): void => mutation.handleError(error)))\n )\n ),\n takeUntilDestroyed(destroyRef)\n )\n .subscribe((handleResult: () => void): void => handleResult());\n }\n\n /** Пускает правку в объявленный поток. */\n public run(mutation: IRtRouteAsideMutation): void {\n this.#mutationSource.next(mutation);\n }\n}\n","/**\n * Абсолютные команды приводятся к сегментам от корня: внутри объекта `outlets`\n * ведущий слэш отдельным сегментом не разбирается.\n *\n * Нужно это затем, что уход панели на связанную запись обязан снять её аутлет\n * той же навигацией. Абсолютные команды меняют только первичную ветку, поэтому\n * панель, открытая в корне приложения, переход переживает: адрес получается с\n * аутлетом, под который в корне маршрута нет, и роутер отклоняет навигацию\n * молча — адрес остаётся прежним, а приложение застревает на панели, которой на\n * экране уже нет.\n */\nexport function rootSegmentsOf(commands: readonly unknown[]): unknown[] {\n const [head, ...rest]: readonly unknown[] = commands;\n\n if (typeof head !== 'string' || !head.startsWith('/')) {\n return [...commands];\n }\n\n return [...head.split('/').filter(Boolean), ...rest];\n}\n","import { Params, Router, UrlTree } from '@angular/router';\n\nimport { rootSegmentsOf } from './rt-route-aside.logic';\n\n/** Аутлеты, которые уход панели снимает вместе со своим. */\nexport type TRtRouteAsideOutlets = Record<string, Array<string> | null>;\n\n/**\n * Уходы панели по маршруту и разрешение на них.\n *\n * Разрешение выдаётся вплотную к вызову маршрутизатора и снимается итогом навигации:\n * между закрытием панели и уходом наложение доигрывает своё, и разрешение, выданное\n * раньше, досталось бы чужому уходу — тронутая форма уехала бы без вопроса. Читается оно\n * тоже один раз: иначе первое же закрытие после записи сняло бы вопрос о правках со всех\n * следующих уходов.\n */\nexport class RtRouteAsideNavigation {\n readonly #router: Router;\n #leaveAllowed: boolean = false;\n\n constructor(router: Router) {\n this.#router = router;\n }\n\n /** Разрешение на этот уход. Читается один раз и снимается чтением. */\n public consumeLeaveAllowance(): boolean {\n const allowed: boolean = this.#leaveAllowed;\n\n this.#leaveAllowed = false;\n\n return allowed;\n }\n\n /** Уход, начатый самой панелью: разрешение живёт ровно эту навигацию. */\n public allowed(navigate: () => Promise<boolean>): void {\n this.#leaveAllowed = true;\n\n void navigate()\n .catch((): boolean => false)\n .then((): void => {\n this.#leaveAllowed = false;\n });\n }\n\n /**\n * Адрес, на который уходит панель. Свой аутлет она снимает тем же деревом: абсолютные\n * команды меняют только первичную ветку, и панель, открытая в корне приложения, переход\n * пережила бы — маршрута под неё в корне нет, а такую навигацию маршрутизатор отклоняет\n * молча, оставляя приложение на панели, которой на экране уже нет.\n */\n public relatedTree(outlets: TRtRouteAsideOutlets, commands: readonly unknown[], queryParams: Params | null = null): UrlTree {\n return this.#router.createUrlTree([{ outlets: { ...outlets, primary: rootSegmentsOf(commands) } }], { queryParams });\n }\n}\n","import { Injector, Signal } from '@angular/core';\nimport { toObservable } from '@angular/core/rxjs-interop';\n\nimport { filter, finalize, map, Observable, of, shareReplay, switchMap, take } from 'rxjs';\n\nimport { RtAsideUnsavedDialogComponent } from '@rt-tools/ui-kit-v2/aside';\nimport { ERtAsideUnsavedOutcome } from '@rt-tools/ui-kit-v2/aside';\nimport { RtDialogService } from '@rt-tools/ui-kit-v2/dialog';\n\n/** То, что вопрос о правках знает о панели: чем записать и чем узнать исход записи. */\nexport interface IRtRouteAsideUnsavedDeps {\n save: () => void;\n submitting: Signal<boolean>;\n submitError: Signal<string | null>;\n}\n\n/**\n * Вопрос о несохранённых правках, заданный уходу по маршруту.\n *\n * Окно открывается одно на все уходы, пришедшие пока оно висит: второе поставило бы поверх\n * первого наложение, убрать которое было бы некому. Ответ раздаётся всем спросившим, а после\n * закрытия окна следующий уход спрашивает заново.\n */\nexport class RtRouteAsideUnsavedPrompt {\n readonly #dialog: RtDialogService;\n readonly #injector: Injector;\n readonly #deps: IRtRouteAsideUnsavedDeps;\n\n /** Вопрос, который сейчас висит. `null` — окна нет, и следующий уход откроет своё. */\n #pendingAsk$: Observable<boolean> | null = null;\n\n constructor(dialog: RtDialogService, injector: Injector, deps: IRtRouteAsideUnsavedDeps) {\n this.#dialog = dialog;\n this.#injector = injector;\n this.#deps = deps;\n }\n\n /** Отпускать ли уход. Пока окно висит, все спросившие получают один и тот же ответ. */\n public ask(): Observable<boolean> {\n const pending: Observable<boolean> | null = this.#pendingAsk$;\n\n if (pending !== null) {\n return pending;\n }\n\n const ask$: Observable<boolean> = this.#dialog\n .open<RtAsideUnsavedDialogComponent, undefined, ERtAsideUnsavedOutcome>(RtAsideUnsavedDialogComponent)\n .afterClosed()\n .pipe(\n switchMap((outcome: ERtAsideUnsavedOutcome | undefined): Observable<boolean> => this.#deactivateAfter(outcome)),\n finalize((): void => {\n this.#pendingAsk$ = null;\n }),\n shareReplay({ bufferSize: 1, refCount: false })\n );\n\n this.#pendingAsk$ = ask$;\n\n return ask$;\n }\n\n /**\n * Что делать с уходом после ответа человека. «Закрыть с сохранением» ждёт конца записи:\n * панель уступает место только удачной — иначе правки пропали бы вместе с отказом, о\n * котором человек ещё не знает.\n */\n #deactivateAfter(outcome: ERtAsideUnsavedOutcome | undefined): Observable<boolean> {\n if (outcome === ERtAsideUnsavedOutcome.Discard) {\n return of(true);\n }\n\n if (outcome !== ERtAsideUnsavedOutcome.Save) {\n return of(false);\n }\n\n this.#deps.save();\n\n // Запись могла и не начаться — например, черновик не годится\n if (!this.#deps.submitting()) {\n return of(false);\n }\n\n return toObservable(this.#deps.submitting, { injector: this.#injector }).pipe(\n filter((submitting: boolean): boolean => !submitting),\n take(1),\n map((): boolean => this.#deps.submitError() === null)\n );\n }\n}\n","import { Signal, isSignal } from '@angular/core';\nimport { toObservable, toSignal } from '@angular/core/rxjs-interop';\nimport { AbstractControl, PristineChangeEvent } from '@angular/forms';\n\nimport { filter, map, Observable, of, startWith, switchMap } from 'rxjs';\n\n/**\n * Признак нетронутой формы.\n *\n * Контрол принимается и сигналом: форма панели собрана директивой формы и до отрисовки\n * шаблона её ещё нет, поэтому запрос вида отдаёт её только сигналом. Нынешнее состояние\n * подставляется первым значением — события прошлого подписка не получит, и без него кнопка\n * записи осталась бы недоступной у формы, испачканной до подписки.\n *\n * Зовётся в контексте внедрения: `toObservable` и `toSignal` требуют его сами.\n */\nexport function pristineOf(control: AbstractControl | Signal<AbstractControl | undefined>): Signal<boolean> {\n const control$: Observable<AbstractControl | undefined> = isSignal(control) ? toObservable(control) : of(control);\n\n return toSignal(\n control$.pipe(\n switchMap((current: AbstractControl | undefined): Observable<boolean> => {\n if (current === undefined) {\n return of(true);\n }\n\n return current.events.pipe(\n filter((event: unknown): event is PristineChangeEvent => event instanceof PristineChangeEvent),\n map((event: PristineChangeEvent): boolean => event.pristine),\n startWith(current.pristine)\n );\n })\n ),\n { initialValue: true }\n );\n}\n","import { Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, Route } from '@angular/router';\n\nimport { Observable } from 'rxjs';\n\n/**\n * Панель, которая умеет ответить на вопрос «можно ли уступить место».\n *\n * Объявлена рядом с учётом панелей, а не рядом с гардом: гард берёт учёт значением, и\n * объявление у него замыкало круг «учёт → гард → учёт».\n */\nexport interface IRtAsideDeactivate {\n canDeactivate(): Observable<boolean>;\n}\n\n/**\n * Учёт панелей, стоящих на экране: из него роутерный гард узнаёт, кого спрашивать\n * о несохранённых правках.\n *\n * Спрашивать компонент, который роутер подставляет в гард, нельзя: когда\n * ro-маршрут объявлен под уровнем без своего компонента, Angular отдаёт детям\n * контекст родителя, и в гард приходит компонент первичного аутлета — экран\n * раздела, а не панель.\n *\n * Ключ записи — объявление маршрута, а не порядок постановки. Гард стоит на\n * каждом ro-маршруте, и одна навигация зовёт его за каждый уходящий маршрут\n * отдельно: учёт стеком дал бы ответ не той панели и два вопроса о правках\n * подряд. Объявление маршрута — та же ссылка на объект и у панели, и у снимка,\n * который роутер отдаёт гарду, поэтому два каркаса на экране не путаются даже\n * при одинаково названных аутлетах: маршруты у них разные.\n *\n * Служба корневая: гард живёт в инжекторе маршрута и до службы, поставленной на\n * уровне компонента, не дотянулся бы вовсе. Одного учёта на приложение хватает —\n * записи различает маршрут. Переменной уровня модуля учёт быть не может: та\n * пережила бы запрос при отдаче страницы сервером и оказалась бы общей на два\n * приложения, поднятых на одной странице.\n */\n@Injectable({ providedIn: 'root' })\nexport class RtRouteAsideRegistry {\n readonly #panels: Map<Route, IRtAsideDeactivate> = new Map<Route, IRtAsideDeactivate>();\n\n /**\n * Панель встала на экран. Повторная постановка под тем же маршрутом заменяет\n * запись: аутлет держит одну панель, и отвечать должна нынешняя.\n */\n public register(route: ActivatedRouteSnapshot, panel: IRtAsideDeactivate): void {\n const key: Route | null = route.routeConfig;\n\n if (key === null) {\n return;\n }\n\n this.#panels.set(key, panel);\n }\n\n /**\n * Панель ушла с экрана. Запись снимается только своя: пока уходящая панель\n * доигрывает уход, её место мог занять сосед, и снятие вслепую стёрло бы\n * запись того, кто на экране остался.\n */\n public unregister(route: ActivatedRouteSnapshot, panel: IRtAsideDeactivate): void {\n const key: Route | null = route.routeConfig;\n\n if (key === null || this.#panels.get(key) !== panel) {\n return;\n }\n\n this.#panels.delete(key);\n }\n\n /** Снятие панели, чей ответ о правках отказал: спрашивать её больше нечем. */\n public forget(panel: IRtAsideDeactivate): void {\n for (const [key, registered] of this.#panels) {\n if (registered === panel) {\n this.#panels.delete(key);\n }\n }\n }\n\n /** Панель, отвечающая за уход с этого маршрута, или её отсутствие. */\n public find(route: ActivatedRouteSnapshot): IRtAsideDeactivate | null {\n const key: Route | null = route.routeConfig;\n\n if (key === null) {\n return null;\n }\n\n return this.#panels.get(key) ?? null;\n }\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n Injector,\n OnInit,\n Signal,\n signal,\n viewChild,\n WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { AbstractControl } from '@angular/forms';\nimport { ActivatedRoute, ParamMap, Params, Router, UrlTree } from '@angular/router';\n\nimport { exhaustMap, mergeMap, Observable, of, Subject, switchMap, tap } from 'rxjs';\n\nimport { NotificationBus } from '@rt-tools/ui-kit-v2/core';\n\nimport { RtAsideUnsavedDialogComponent } from '@rt-tools/ui-kit-v2/aside';\nimport { ERtAsideCloseIntent, ERtAsideUnsavedOutcome, IRtAsideUnsavedGuard, resolveCloseIntent } from '@rt-tools/ui-kit-v2/aside';\nimport { RtDialogService } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtContainerRightSidenavPanelDirective } from './rt-container.directives';\nimport { RtRouteAsideMutations } from './rt-route-aside.mutations';\nimport { RtRouteAsideNavigation } from './rt-route-aside.navigation';\nimport { RtRouteAsideUnsavedPrompt } from './rt-route-aside.unsaved';\nimport { pristineOf } from './rt-route-aside.pristine';\nimport { RtRouteAsideRegistry } from './rt-route-aside.registry';\n\n/**\n * Открытие ждёт `panel.ready()`, а не `ngAfterViewInit`: на F5 rt-container\n * создаёт overlay асинхронно, иначе `open()` гонится с ещё не готовым overlay.\n *\n * `@Directive()` без селектора — чтобы query/`inject`/lifecycle наследовались на\n * конкретный `@Component` (как `RtFormControlBase`).\n *\n * Закрытие всегда идёт через `onClose()`, куда сходятся все четыре пути: две\n * кнопки панели и — через `onCancel()` — нажатие мимо панели и Esc, которые ловит\n * `rt-container`. Панель, поставившая гард через `guardUnsavedChanges()`, на\n * тронутой форме получает вопрос о правках; панель без гарда закрывается сразу.\n */\n@Directive()\nexport abstract class RtRouteAsideComponent<T> implements OnInit {\n readonly #entity: WritableSignal<T | null> = signal<T | null>(null);\n readonly #entityId: WritableSignal<string | null> = signal<string | null>(null);\n readonly #isCreateMode: WritableSignal<boolean> = signal(false);\n readonly #resolving: WritableSignal<boolean> = signal(false);\n readonly #submitting: WritableSignal<boolean> = signal(false);\n\n readonly #mutations: RtRouteAsideMutations = new RtRouteAsideMutations(inject(DestroyRef));\n readonly #refreshSource: Subject<string> = new Subject<string>();\n readonly #closeIntentSource: Subject<void> = new Subject<void>();\n\n readonly #unsavedGuard: WritableSignal<IRtAsideUnsavedGuard | null> = signal<IRtAsideUnsavedGuard | null>(null);\n\n /** Связанная запись, на которую панель уйдёт, когда пользователь решит судьбу правок */\n #relatedCommands: readonly unknown[] | null = null;\n\n /** Параметры адреса связанной записи: у события журнала ими задан отбор списка */\n #relatedQueryParams: Params | null = null;\n\n readonly #notificationBus: NotificationBus = inject(NotificationBus);\n readonly #dialog: RtDialogService = inject(RtDialogService);\n readonly #injector: Injector = inject(Injector);\n readonly #registry: RtRouteAsideRegistry = inject(RtRouteAsideRegistry);\n\n /** Вопрос о правках, заданный уходу по маршруту: у кнопок панели свой путь. */\n readonly #unsavedPrompt: RtRouteAsideUnsavedPrompt = new RtRouteAsideUnsavedPrompt(this.#dialog, this.#injector, {\n save: (): void => this.#unsavedGuard()?.save(),\n submitting: this.#submitting,\n submitError: computed((): string | null => this.submitError()),\n });\n\n #opened: boolean = false;\n\n protected readonly router: Router = inject(Router);\n readonly #navigation: RtRouteAsideNavigation = new RtRouteAsideNavigation(this.router);\n protected readonly route: ActivatedRoute = inject(ActivatedRoute);\n protected readonly destroyRef: DestroyRef = inject(DestroyRef);\n\n protected readonly panel: Signal<RtContainerRightSidenavPanelDirective | undefined> = viewChild(RtContainerRightSidenavPanelDirective);\n\n protected readonly entity: Signal<T | null> = this.#entity.asReadonly();\n\n /** id из route-параметра как есть (uuid/строка); числовые id консьюмер парсит сам. */\n protected readonly entityId: Signal<string | null> = this.#entityId.asReadonly();\n protected readonly isCreateMode: Signal<boolean> = this.#isCreateMode.asReadonly();\n protected readonly submitting: Signal<boolean> = this.#submitting.asReadonly();\n\n /**\n * Скелетоны биндятся на это, а не на пустоту `entity()`: та залипает после\n * первой загрузки и не ловит рефетч.\n */\n protected readonly resolving: Signal<boolean> = this.#resolving.asReadonly();\n\n protected readonly busy: Signal<boolean> = computed((): boolean => this.#submitting() || this.#resolving());\n\n /**\n * Форма не тронута. Панель, которая не поставила гард закрытия, считается\n * нетронутой всегда — и закрывается без вопроса, как раньше.\n */\n protected readonly pristine: Signal<boolean> = computed((): boolean => this.#unsavedGuard()?.pristine() ?? true);\n\n protected readonly submitError: WritableSignal<string | null> = signal<string | null>(null);\n\n /**\n * Удача мутации — тем же способом, что и отказ. Человек, работающий в панели,\n * смотрит в панель, а не на экран за её краем: тост об успехе всплывает там,\n * куда он в эту минуту не смотрит. Оба сигнала гасятся в начале каждой\n * попытки и гасят друг друга — это ответы на один вопрос, и двух сразу не\n * бывает.\n */\n protected readonly submitSuccess: WritableSignal<string | null> = signal<string | null>(null);\n\n protected readonly idParamName: string = 'id';\n\n /** id-only режим: нужен только id (например, смена пароля по userId), сущность не грузится. */\n protected readonly idOnly: boolean = false;\n\n constructor() {\n effect((): void => {\n const panel: RtContainerRightSidenavPanelDirective | undefined = this.panel();\n if (panel === undefined || !panel.ready() || this.#opened) {\n return;\n }\n this.#opened = true;\n panel.open();\n // Панель встала в аутлет — с этого мгновения о правках спрашивают её,\n // а не тот компонент, который роутер подставит гарду.\n this.#registry.register(this.route.snapshot, this);\n });\n\n // Снятие с учёта идёт по уничтожению, а не по уходу с экрана: уход может и\n // не состояться — роутер отклоняет навигацию молча, — и панель, снятая с\n // учёта раньше времени, осталась бы на экране, не отвечая о своих правках.\n this.destroyRef.onDestroy((): void => this.#registry.unregister(this.route.snapshot, this));\n\n // Рефетч сущности из #refreshEntity(): mergeMap — каждый запуск resolve\n // живёт независимо (как раньше — отдельная подписка на вызов).\n this.#refreshSource\n .pipe(\n mergeMap((id: string): Observable<T | null> => this.resolve(id)),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe((entity: T | null): void => {\n this.#resolving.set(false);\n if (entity === null) {\n this.#navigateAway();\n return;\n }\n this.#entity.set(entity);\n this.onResolved();\n });\n\n // Вопрос о несохранённых правках: exhaustMap, а не switchMap — пока окно\n // открыто, повторное намерение закрыться игнорируется. switchMap отписался\n // бы от первого окна, не убрав его overlay, и на экране осталось бы два.\n this.#closeIntentSource\n .pipe(\n exhaustMap((): Observable<ERtAsideUnsavedOutcome | undefined> =>\n this.#dialog\n .open<RtAsideUnsavedDialogComponent, undefined, ERtAsideUnsavedOutcome>(RtAsideUnsavedDialogComponent)\n .afterClosed()\n ),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe((outcome: ERtAsideUnsavedOutcome | undefined): void => this.#applyUnsavedOutcome(outcome));\n }\n\n public ngOnInit(): void {\n this.route.paramMap\n .pipe(\n switchMap((params: ParamMap): Observable<T | null> => {\n const idParam: string | null = params.get(this.idParamName);\n if (idParam === null || idParam === '') {\n this.#isCreateMode.set(true);\n this.#entityId.set(null);\n return of(null);\n }\n this.#isCreateMode.set(false);\n this.#entityId.set(idParam);\n if (this.idOnly) {\n return of(null);\n }\n this.#resolving.set(true);\n return this.resolve(idParam);\n }),\n tap((entity: T | null): void => {\n this.#resolving.set(false);\n if (entity === null) {\n if (!this.#isCreateMode() && !this.idOnly) {\n this.#navigateAway();\n }\n return;\n }\n this.#entity.set(entity);\n this.onResolved();\n }),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe();\n }\n\n /**\n * Панель уходит с экрана не сама: её место в аутлете занимает другая — лента\n * событий из шапки или соседняя запись. Тронутая форма спрашивает о правках\n * так же, как при закрытии; окно кнопок этот путь не ловит, потому что панель\n * закрывает роутер.\n *\n * Гард ставится на ro-маршрут панели (`rtAsideUnsavedGuard`).\n */\n public canDeactivate(): Observable<boolean> {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n // Признак идущей записи старше разрешения на уход: панель, выдавшая себе\n // разрешение и не дождавшаяся ответа сервера, уходить не должна — исход\n // записи решит её судьбу сам.\n if (intent === ERtAsideCloseIntent.Ignore) {\n return of(false);\n }\n\n // Уход начала сама панель: правки либо записаны секунду назад, либо их\n // судьбу пользователь уже решил. Вопрос был бы задан про то, чего нет.\n if (this.#navigation.consumeLeaveAllowance()) {\n return of(true);\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n return of(true);\n }\n\n return this.#unsavedPrompt.ask();\n }\n\n protected setEntity(entity: T | null): void {\n this.#entity.set(entity);\n }\n\n /** Признак нетронутой формы — тот же, что гейтит кнопку записи. */\n protected pristineSignal(control: AbstractControl | Signal<AbstractControl | undefined>): Signal<boolean> {\n return pristineOf(control);\n }\n\n /**\n * Ставит гард закрытия: пока форма тронута, любой из четырёх путей закрытия\n * спрашивает, что сделать с правками, и исход «закрыть с сохранением» зовёт\n * `save`. Признак нетронутости даёт `pristineSignal(control)` — тот же, что\n * гейтит кнопку записи.\n */\n protected guardUnsavedChanges(guard: IRtAsideUnsavedGuard): void {\n this.#unsavedGuard.set(guard);\n }\n\n /**\n * Адрес связанной записи для атрибута `href`. Ссылка остаётся настоящей\n * ссылкой: её открывают в новой вкладке и копируют, а обычное нажатие панель\n * перехватывает и уводит сама.\n */\n protected relatedUrl(commands: readonly unknown[]): string {\n return this.router.serializeUrl(this.#relatedTree(commands));\n }\n\n /**\n * Уход на связанную запись. Он уводит с адреса панели так же, как её\n * закрытие, поэтому идёт тем же путём: тронутая форма спрашивает, что\n * сделать с правками, и на исходе «закрыть с сохранением» запись открывается\n * после удачной записи.\n */\n protected openRelated(commands: readonly unknown[], queryParams: Params | null = null): void {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n if (intent === ERtAsideCloseIntent.Ignore) {\n return;\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n this.#navigateRelated(commands, queryParams);\n\n return;\n }\n\n this.#relatedQueryParams = queryParams;\n\n this.#relatedCommands = commands;\n // Подписка объявлена один раз в конструкторе (см. #closeIntentSource-стрим).\n this.#closeIntentSource.next();\n }\n\n protected onResolved(): void {\n // Хук для наследника: базовый асайд после разрешения данных ничего не делает.\n }\n\n protected closeOutlets(): Record<string, Array<string> | null> {\n return { ro: null };\n }\n\n /**\n * `successText` — готовый текст тоста об успехе. Он принадлежит конкретной\n * мутации, а не панели: у снятия промокода и опроса подписки свой текст\n * успеха, и один на панель показал бы не тот.\n *\n * Именно текст, а не ключ словаря: ключ принадлежал бы словарю приложения, а\n * кит чужих словарей не знает — переводит приложение и передаёт готовое.\n *\n * `errorText` принимается и функцией: причина отказа известна только после\n * него — стор кладёт её в свой сигнал, а панель без стора выводит из ответа\n * сервера, — и готовой строкой на момент запуска мутации её не передать.\n *\n * `successMessage` — текст удачи, который панель показывает внутри себя,\n * рядом с формой. Довод отделён от `successText` намеренно: переезд текста\n * из тоста в панель сломал бы всякого потребителя, который тост и хотел, а\n * сказать «мне нужен тост» ему после этого было бы нечем. Назван он по месту\n * показа, а не по исходу: два довода с разницей в одном слоге читались бы\n * как опечатка.\n */\n protected runMutation(\n op$: Observable<unknown>,\n opts?: {\n onSuccess?: () => void;\n successText?: string;\n successMessage?: string;\n errorText?: string | ((error: unknown) => string);\n closeOnSuccess?: boolean;\n refreshOnSuccess?: boolean;\n }\n ): void {\n this.#submitting.set(true);\n this.submitError.set(null);\n this.submitSuccess.set(null);\n this.#mutations.run({\n op$,\n handleSuccess: (): void => {\n this.#submitting.set(false);\n if (opts?.successText) {\n this.#notificationBus.success(opts.successText);\n }\n if (opts?.successMessage) {\n this.submitSuccess.set(opts.successMessage);\n }\n // Пользователь шёл на связанную запись и по дороге согласился\n // сохранить: панель уходит туда, куда он нажал.\n const related: readonly unknown[] | null = this.#relatedCommands;\n if (related !== null) {\n this.#navigateRelated(related);\n return;\n }\n if (opts?.onSuccess) {\n opts.onSuccess();\n return;\n }\n // Закрытие после удачи идёт мимо обоих вопросов о правках: форма ещё\n // тронута, а спрашивать про только что записанное не о чем — уходу по\n // маршруту это закрывает разрешение на уход. Названный довод решает в обе\n // стороны, а не названный считается по режиму: создавать в закрытой панели\n // нечего.\n if (opts?.closeOnSuccess ?? this.#isCreateMode()) {\n this.#closePanel();\n return;\n }\n if (opts?.refreshOnSuccess === true) {\n this.#refreshEntity();\n }\n },\n handleError: (error: unknown): void => {\n this.#submitting.set(false);\n // Отказ снимает прежнюю удачу: иначе «сохранено» осталось бы на\n // панели рядом со свежим отказом.\n this.submitSuccess.set(null);\n // Отказ оставляет человека в панели с его правками, поэтому\n // намерение уйти на связанную запись снимается.\n this.#relatedCommands = null;\n const text: string | undefined = typeof opts?.errorText === 'function' ? opts.errorText(error) : opts?.errorText;\n if (text) {\n this.submitError.set(text);\n }\n },\n });\n }\n\n /**\n * Единственная точка закрытия панели. Через неё идут все четыре пути: кнопка\n * в шапке, кнопка в футере и — через `onCancel()` — нажатие мимо панели и\n * клавиша Esc, которые ловит `rt-container` и отдаёт панели одним выходом\n * `cancelled`.\n */\n protected onClose(): void {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n if (intent === ERtAsideCloseIntent.Ignore) {\n return;\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n this.#closePanel();\n return;\n }\n\n // Подписка объявлена один раз в конструкторе (см. #closeIntentSource-стрим).\n this.#closeIntentSource.next();\n }\n\n protected onCancel(): void {\n this.onClose();\n }\n\n protected onClosed(): void {\n this.#navigateAway();\n }\n\n protected abstract resolve(id: string): Observable<T | null>;\n\n #refreshEntity(): void {\n const id: string | null = this.#entityId();\n if (id === null || this.idOnly) {\n return;\n }\n this.#resolving.set(true);\n // Подписка объявлена один раз в конструкторе (см. #refreshSource-стрим).\n this.#refreshSource.next(id);\n }\n\n /** Решение по намерению уйти с панели — одно на закрытие и на переход по связанной записи */\n #closeIntent(): ERtAsideCloseIntent {\n return resolveCloseIntent({\n submitting: this.#submitting(),\n guarded: this.#unsavedGuard() !== null,\n pristine: this.pristine(),\n });\n }\n\n #closePanel(): void {\n this.panel()?.close();\n }\n\n #navigateRelated(commands: readonly unknown[], queryParams: Params | null = null): void {\n const params: Params | null = queryParams ?? this.#relatedQueryParams;\n\n this.#relatedCommands = null;\n this.#relatedQueryParams = null;\n this.#navigation.allowed((): Promise<boolean> => this.router.navigateByUrl(this.#relatedTree(commands, params)));\n }\n\n #relatedTree(commands: readonly unknown[], queryParams: Params | null = null): UrlTree {\n return this.#navigation.relatedTree(this.closeOutlets(), commands, queryParams);\n }\n\n #applyUnsavedOutcome(outcome: ERtAsideUnsavedOutcome | undefined): void {\n const related: readonly unknown[] | null = this.#relatedCommands;\n\n if (outcome === ERtAsideUnsavedOutcome.Discard) {\n if (related !== null) {\n this.#navigateRelated(related);\n return;\n }\n this.#closePanel();\n return;\n }\n\n // Запись закрывает панель сама на успехе — иначе панель ушла бы с экрана\n // раньше ответа, и отказ негде было бы показать. Уход на\n // связанную запись ждёт того же успеха.\n if (outcome === ERtAsideUnsavedOutcome.Save) {\n this.#unsavedGuard()?.save();\n // Запись могла и не начаться — например, черновик не годится. Тогда\n // намерение уйти снимается вместе с ней, иначе панель ушла бы на\n // связанную запись после следующей удачной записи.\n if (!this.#submitting()) {\n this.#relatedCommands = null;\n }\n return;\n }\n\n this.#relatedCommands = null;\n }\n\n /**\n * Уход с адреса при закрытии.\n *\n * Параметры адреса переносятся нетронутыми: панель открыта поверх экрана, чьё состояние в\n * них и живёт — страница списка, порядок, отбор. Без переноса закрытая панель возвращает\n * экран к его первой странице без отбора, и человек, открывший запись со второй страницы\n * отобранного списка, теряет её просмотром одной записи. Свои параметры панель называет\n * `openRelated` явно, и на них перенос не действует.\n */\n #navigateAway(): void {\n this.#navigation.allowed((): Promise<boolean> =>\n this.router.navigate([{ outlets: this.closeOutlets() }], {\n relativeTo: this.route.parent,\n queryParamsHandling: 'preserve',\n })\n );\n }\n}\n","import { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanDeactivateFn } from '@angular/router';\n\nimport { catchError, defer, Observable, of } from 'rxjs';\n\nimport { IRtAsideDeactivate, RtRouteAsideRegistry } from './rt-route-aside.registry';\n\n// Объявление переехало к учёту панелей; здесь оно остаётся видимым прежним потребителям.\n\n/**\n * Панель с несохранёнными правками спрашивает о них и при смене маршрута, а не\n * только при закрытии: аутлет `ro` занимает соседняя панель, и правки пропали бы\n * молча.\n *\n * Ставится на каждый ro-маршрут: гард висит на кнопках, нажатии мимо и Esc, а\n * смену маршрута ловит только роутер.\n *\n * Спрашивается панель, учтённая под маршрутом ухода, а не компонент, который\n * подставил роутер. Компонент — не панель, когда ro-маршрут объявлен под уровнем\n * без своего компонента: Angular отдаёт детям контекст родителя, и в гард\n * приходит компонент первичного аутлета. Ответ такого компонента к правкам\n * отношения не имеет, а вызов у него метода, которого нет, ронял навигацию — она\n * падала необработанным отказом, адрес не менялся, и приложение оставалось\n * запертым на панели.\n *\n * Компонент от роутера остаётся запасным путём: гард выведен наружу кита, и\n * панель приложения может отвечать о правках, не наследуя базовый класс, — на\n * учёт такая панель не встаёт никогда. Без запасного пути у неё вопрос о правках\n * исчез бы молча, а это тихая потеря правок.\n *\n * Спрашивать некого — уступить место можно: роутер зовёт гард и для маршрута,\n * чей компонент не создан. Без этого первая же такая навигация падает, а следом\n * отказывает любая другая.\n *\n * Отказом навигации гард не отвечает никогда, в том числе когда ответ панели\n * отказал: разрешить уход значило бы унести правки, о которых спросить не\n * удалось, а отменить и оставить всё как есть — запереть приложение на панели\n * навсегда. Поэтому уход отменяется один раз, а панель снимается с учёта, и\n * следующий уход идёт запасным путём.\n */\n/** Компонент от роутера годится в ответчики, только если умеет отвечать. */\nfunction fallbackPanel(component: IRtAsideDeactivate | null): IRtAsideDeactivate | null {\n return typeof component?.canDeactivate === 'function' ? component : null;\n}\n\nexport const rtAsideUnsavedGuard: CanDeactivateFn<IRtAsideDeactivate | null> = (\n component: IRtAsideDeactivate | null,\n currentRoute: ActivatedRouteSnapshot\n): Observable<boolean> => {\n const registry: RtRouteAsideRegistry = inject(RtRouteAsideRegistry);\n const registered: IRtAsideDeactivate | null = registry.find(currentRoute);\n const panel: IRtAsideDeactivate | null = registered ?? fallbackPanel(component);\n\n if (panel === null) {\n return of(true);\n }\n\n // defer: ответ панели может отказать и синхронным броском, а гард обязан\n // пережить и его.\n return defer((): Observable<boolean> => panel.canDeactivate()).pipe(\n catchError((): Observable<boolean> => {\n registry.forget(panel);\n\n return of(false);\n })\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-container.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/container/rt-container-state.service.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.markers.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.component.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.component.html","../../../projects/ui-kit-v2/src/lib/components/container/rt-container.directives.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.mutations.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.logic.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.navigation.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.unsaved.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.pristine.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.registry.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.base.ts","../../../projects/ui-kit-v2/src/lib/components/container/rt-route-aside.guard.ts"],"sourcesContent":["import { Injectable, WritableSignal, signal } from '@angular/core';\n\n/**\n * Приватный signal-store для `rt-container`.\n *\n * Хранит open-state левой docked sidenav. Driver-директива\n * `[rtContainerLeftSidenavPanel]` пишет в `leftSidenavOpen`, шаблон\n * `RtContainerComponent` читает через signal-tracking.\n *\n * Provided component-level через `providers: [RtContainerStateService]` на\n * `RtContainerComponent` — per-instance state, без singleton'а. Pure signal IO\n * с автоматической reactivity.\n *\n * Правая sidenav (overlay) НЕ хранится здесь: её open-state живёт прямо в\n * `RtContainerComponent.rightOpen` сигнале и управляется через `openRight()` /\n * `closeRight()` методы + lifecycle outputs.\n */\n@Injectable()\nexport class RtContainerStateService {\n public readonly leftSidenavOpen: WritableSignal<boolean> = signal<boolean>(true);\n}\n","import { Directive } from '@angular/core';\n\n/**\n * Маркеры слотов оболочки — своим файлом, а не рядом с панельными директивами.\n *\n * Панельные директивы берут сам компонент оболочки значением, а компонент объявляет маркеры в\n * своих импортах: вместе это круг «оболочка → директивы → оболочка». Маркеры пусты и не знают\n * об оболочке ничего, поэтому расходятся с ней без потерь.\n */\n\n/**\n * Маркер-директива для содержимого хедера `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerHeader>\n * <rt-header />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerHeader]',\n})\nexport class RtContainerHeaderDirective {}\n\n/**\n * Маркер-директива для контента левой sidenav в `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerLeftSidenav>\n * <router-outlet name=\"lo\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerLeftSidenav]',\n})\nexport class RtContainerLeftSidenavDirective {}\n\n/**\n * Маркер-директива для контента правой sidenav в `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerRightSidenav>\n * <router-outlet name=\"ro\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerRightSidenav]',\n})\nexport class RtContainerRightSidenavDirective {}\n\n/**\n * Маркер-директива для контента в левой секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarLeft>\n * <router-outlet name=\"lb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarLeft]',\n})\nexport class RtContainerToolbarLeftDirective {}\n\n/**\n * Маркер-директива для контента в центральной секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarCenter>\n * <router-outlet name=\"cb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarCenter]',\n})\nexport class RtContainerToolbarCenterDirective {}\n\n/**\n * Маркер-директива для контента в правой секции toolbar.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerToolbarRight>\n * <router-outlet name=\"rb\" />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerToolbarRight]',\n})\nexport class RtContainerToolbarRightDirective {}\n\n/**\n * Маркер-директива для основного контента `rt-container`.\n *\n * @example\n * ```html\n * <rt-container>\n * <ng-container *rtContainerContent>\n * <router-outlet />\n * </ng-container>\n * </rt-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerContent]',\n})\nexport class RtContainerContentDirective {}\n","import { BlockScrollStrategy, Overlay, OverlayConfig, OverlayRef, PositionStrategy, ScrollStrategyOptions } from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport { DOCUMENT, NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n InputSignal,\n OutputEmitterRef,\n Renderer2,\n RendererFactory2,\n Signal,\n TemplateRef,\n ViewContainerRef,\n ViewEncapsulation,\n WritableSignal,\n afterNextRender,\n computed,\n contentChild,\n effect,\n inject,\n input,\n output,\n signal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { map, merge, Observable, Subject, switchMap, takeUntil } from 'rxjs';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { BreakpointsService, carryThemeScope, materialPresetClassesOf } from '@rt-tools/ui-kit-v2/core';\nimport { RtToasterComponent } from '@rt-tools/ui-kit-v2/toast';\nimport { RtContainerStateService } from './rt-container-state.service';\nimport {\n RtContainerContentDirective,\n RtContainerHeaderDirective,\n RtContainerLeftSidenavDirective,\n RtContainerRightSidenavDirective,\n RtContainerToolbarCenterDirective,\n RtContainerToolbarLeftDirective,\n RtContainerToolbarRightDirective,\n} from './rt-container.markers';\n\n/** Запас поверх длительности transition'а для страховочного таймера. */\nconst ASIDE_TRANSITION_BUFFER_MS: number = 80;\n\nconst BEM_BLOCK: string = 'rt-container';\n\n/**\n * Multi-slot container с CSS-grid layout. Slot consumers wrap content в\n * `*rtContainerXxx` structural directives, контейнер подхватывает шаблоны через\n * `contentChild(..., { read: TemplateRef })` + рендерит через `*ngTemplateOutlet`.\n *\n * Slots: `header`, `leftSidenav`, `toolbarLeft/Center/Right`, `content` — живут в\n * CSS-grid. `rightSidenav` рендерится через CDK Overlay поверх grid'а (slide-in\n * side-sheet), а не как grid-cell — overlay переиспользует глобальные\n * `rt-aside-overlay` / `rt-aside-backdrop` классы из rt-aside, чтобы анимация и\n * backdrop вели себя идентично программно открываемым asides.\n *\n * Driver-directives (`[rtContainerLeftSidenavPanel/RightSidenavPanel]`) общаются с\n * контейнером через `RtContainerStateService` (для левой docked) и через\n * `openRight()/closeRight()` + lifecycle outputs (для правой overlay).\n */\n@Component({\n selector: 'rt-container',\n templateUrl: './rt-container.component.html',\n styleUrls: ['./rt-container.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // Angular\n NgTemplateOutlet,\n\n // standalone components / directives\n RtToasterComponent,\n BlockDirective,\n ElemDirective,\n ModDirective,\n ],\n providers: [RtContainerStateService],\n host: {\n class: BEM_BLOCK,\n '[class.rt-container--mobile-left-bottom]': \"mobileLeftNav() === 'bottom'\",\n '[class.rt-container--viewport]': \"height() === 'viewport'\",\n },\n})\nexport class RtContainerComponent {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #host: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #vcr: ViewContainerRef = inject(ViewContainerRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #blockScroll: BlockScrollStrategy = this.#scrollStrategies.block();\n readonly #renderer: Renderer2 = inject(RendererFactory2).createRenderer(null, null);\n readonly #window: Window | null = inject(DOCUMENT).defaultView;\n\n readonly #rightOverlaySource: Subject<OverlayRef> = new Subject<OverlayRef>();\n\n #overlayRef: OverlayRef | null = null;\n\n readonly #overlayRefSignal: WritableSignal<OverlayRef | null> = signal<OverlayRef | null>(null);\n\n protected readonly headerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerHeaderDirective, {\n read: TemplateRef,\n });\n\n protected readonly leftSidenavTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerLeftSidenavDirective, {\n read: TemplateRef,\n });\n\n protected readonly rightSidenavTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerRightSidenavDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarLeftTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarLeftDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarCenterTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarCenterDirective, {\n read: TemplateRef,\n });\n\n protected readonly toolbarRightTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerToolbarRightDirective, {\n read: TemplateRef,\n });\n\n protected readonly contentTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtContainerContentDirective, {\n read: TemplateRef,\n });\n\n /**\n * Поведение левого sidenav на мобилке (<=768px): `keep` — остаётся боковой\n * колонкой (по умолчанию, десктоп-стиль); `bottom` — сворачивается в нижний\n * ряд grid'а (таб-бар), контент занимает всю ширину и скроллится над ним.\n */\n public readonly mobileLeftNav: InputSignal<'keep' | 'bottom'> = input<'keep' | 'bottom'>('keep');\n\n /**\n * Высота контейнера: `auto` — растёт под контент, скроллится документ (по\n * умолчанию; на этом держится sticky-хедер); `viewport` — контейнер жёстко\n * равен вьюпорту, страница не скроллится вовсе, а скроллятся зоны внутри —\n * каждая сама по себе (app-shell раскладка, напр. чаты бот-панели).\n */\n public readonly height: InputSignal<'auto' | 'viewport'> = input<'auto' | 'viewport'>('auto');\n\n public readonly backdropClick: OutputEmitterRef<void> = output<void>();\n\n public readonly rightOpened: OutputEmitterRef<void> = output<void>();\n\n public readonly rightClosed: OutputEmitterRef<void> = output<void>();\n\n public readonly rightOpen: WritableSignal<boolean> = signal<boolean>(false);\n\n /**\n * `true` когда CDK Overlay для правой sidenav создан и attache'нут (см.\n * effect в constructor'е). Дёргать panel.open() из консьюмера можно только\n * когда это true — иначе openRight() видит null overlayRef и early-return'ит,\n * --entering modifier остаётся, панель остаётся за экраном. Re-exposed на\n * driver через `panel.ready` для удобства route-driven dialog'ов.\n */\n public readonly rightOverlayReady: Signal<boolean> = computed((): boolean => this.#overlayRefSignal() !== null);\n\n constructor() {\n // Контейнер — место страницы: узкий вид кита считается от его ширины. Боковая панель\n // приложения рядом сужает его, а окно остаётся прежним. Место объявляется в браузере\n // после первой отрисовки и снимается вместе с контейнером.\n const breakpoints: BreakpointsService = inject(BreakpointsService);\n const destroyRef: DestroyRef = inject(DestroyRef);\n afterNextRender((): void => {\n destroyRef.onDestroy(breakpoints.registerPlace(this.#host.nativeElement));\n });\n\n // Backdrop-click / ESC подписки правого overlay живут в одном\n // конструкторном стриме: #createRightOverlay эмитит созданный OverlayRef,\n // switchMap переключается на его event-стримы (предыдущий overlay к этому\n // моменту уже dispose'нут effect-cleanup'ом; `detachments()` терминирует\n // внутренний поток при dispose).\n this.#rightOverlaySource\n .pipe(\n switchMap((overlayRef: OverlayRef): Observable<() => void> =>\n merge(\n overlayRef.backdropClick().pipe(map((): (() => void) => (): void => this.backdropClick.emit())),\n overlayRef.keydownEvents().pipe(\n map((event: KeyboardEvent): (() => void) => (): void => {\n if (event.key !== 'Escape' || !this.rightOpen()) {\n return;\n }\n event.preventDefault();\n this.backdropClick.emit();\n })\n )\n ).pipe(takeUntil(overlayRef.detachments()))\n ),\n takeUntilDestroyed()\n )\n .subscribe((handleCloseIntent: () => void): void => handleCloseIntent());\n\n // Persistent overlay: создаётся при появлении rightSidenavTpl и живёт\n // до уничтожения rt-container'а. Это нужно чтобы <router-outlet name=\"ro\">\n // внутри slot template'а материализовался и мог активировать route.\n // Без auto-attach landmine: route не активируется → driver не вызывается\n // → ничего не открывается (chicken-and-egg).\n effect((onCleanup: (cleanup: () => void) => void): void => {\n const tpl: TemplateRef<unknown> | undefined = this.rightSidenavTpl();\n\n if (tpl === undefined || this.#overlayRef !== null) {\n return;\n }\n\n const overlayRef: OverlayRef = this.#createRightOverlay(tpl);\n this.#overlayRef = overlayRef;\n this.#overlayRefSignal.set(overlayRef);\n\n onCleanup((): void => {\n overlayRef.dispose();\n this.#overlayRef = null;\n this.#overlayRefSignal.set(null);\n });\n });\n }\n\n public openRight(): void {\n const overlayRef: OverlayRef | null = this.#overlayRef;\n if (overlayRef === null) {\n return;\n }\n\n this.#blockScroll.enable();\n // Коробка постоянная и лежит в конце страницы, а кусок темы вокруг контейнера мог смениться.\n carryThemeScope(overlayRef.overlayElement, this.#host.nativeElement);\n\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--entering');\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--leaving');\n\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n if (backdropEl !== null) {\n this.#renderer.addClass(backdropEl, 'rt-aside-backdrop--visible');\n this.#renderer.removeStyle(backdropEl, 'opacity');\n this.#renderer.removeStyle(backdropEl, 'pointer-events');\n }\n\n // Slide-in: на следующий paint добавляем --open.\n requestAnimationFrame((): void => {\n this.#renderer.addClass(overlayRef.overlayElement, 'rt-aside-overlay--open');\n this.#listenTransitionEnd(overlayRef, (): void => this.rightOpened.emit());\n });\n\n this.rightOpen.set(true);\n }\n\n public closeRight(): void {\n const overlayRef: OverlayRef | null = this.#overlayRef;\n if (overlayRef === null || !this.rightOpen()) {\n return;\n }\n\n this.#renderer.removeClass(overlayRef.overlayElement, 'rt-aside-overlay--open');\n this.#renderer.addClass(overlayRef.overlayElement, 'rt-aside-overlay--leaving');\n\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n if (backdropEl !== null) {\n this.#renderer.removeClass(backdropEl, 'rt-aside-backdrop--visible');\n }\n\n this.#listenTransitionEnd(overlayRef, (): void => {\n this.#blockScroll.disable();\n // Объявления на узле возвращаются только после ухода: поставленные сразу, они\n // обрывают затухание подложки на первом же кадре.\n this.#hideBackdrop(overlayRef);\n this.rightOpen.set(false);\n this.rightClosed.emit();\n });\n }\n\n /**\n * Подложка закрытой панели: невидима и нажатий не ловит.\n *\n * Стилями этого не сделать. Правила кита живут в слое каскада, а CDK кладёт свои прямо на\n * страницу, вне слоя, — и `.cdk-overlay-backdrop-showing { opacity: 1 }` выигрывает у нашего\n * `opacity: 0` независимо от специфичности: неслоевое правило сильнее слоевого всегда.\n * Подложка при этом висит поверх страницы во весь экран — прозрачная и перехватывающая\n * нажатия, а панель ещё не открывали. Объявление на самом узле сильнее обоих правил.\n *\n * Держится это ровно до открытия: там объявления снимаются, и дальше видом подложки\n * заведуют классы.\n */\n #hideBackdrop(overlayRef: OverlayRef): void {\n const backdropEl: HTMLElement | null = overlayRef.backdropElement;\n\n if (backdropEl === null) {\n return;\n }\n\n this.#renderer.setStyle(backdropEl, 'opacity', '0');\n this.#renderer.setStyle(backdropEl, 'pointer-events', 'none');\n }\n\n #createRightOverlay(tpl: TemplateRef<unknown>): OverlayRef {\n const positionStrategy: PositionStrategy = this.#overlay.position().global().right('0').top('0');\n\n const overlayConfig: OverlayConfig = {\n positionStrategy,\n // noop здесь, scroll-block управляется вручную через #blockScroll\n // в openRight/closeRight — иначе scroll заблокирован всегда\n // (overlay persistent), пока aside ещё не открыт.\n scrollStrategy: this.#overlay.scrollStrategies.noop(),\n hasBackdrop: true,\n backdropClass: 'rt-aside-backdrop',\n panelClass: [\n 'rt-aside-overlay',\n 'rt-aside-overlay--position-right',\n 'rt-aside-overlay--entering',\n ...materialPresetClassesOf(this.#host.nativeElement),\n ],\n disposeOnNavigation: false,\n };\n\n const overlayRef: OverlayRef = this.#overlay.create(overlayConfig);\n const portal: TemplatePortal = new TemplatePortal(tpl, this.#vcr);\n overlayRef.attach(portal);\n this.#hideBackdrop(overlayRef);\n\n // Backdrop-click / ESC подписки объявлены один раз в конструкторе —\n // здесь только эмит созданного OverlayRef (см. #rightOverlaySource-стрим).\n this.#rightOverlaySource.next(overlayRef);\n\n return overlayRef;\n }\n\n // Слушаем именно transform-окончание: на overlay host'е могут анимироваться\n // и другие свойства (opacity backdrop'а, например) — это даст ложные срабатывания.\n #listenTransitionEnd(overlayRef: OverlayRef, callback: () => void): void {\n const element: HTMLElement = overlayRef.overlayElement;\n\n // Нет анимации (reduced-motion / `transition: none`) → `transitionend` не\n // придёт; teardown-callback (навигация `ro:null`) отрабатываем синхронно.\n const durationMs: number = this.#transitionDurationMs(element);\n if (durationMs === 0) {\n callback();\n return;\n }\n\n // settled-гард делает transitionend и fallback-таймаут взаимно\n // идемпотентными — сработает и снимет listener только то, что раньше.\n let settled: boolean = false;\n\n const handler: (event: TransitionEvent) => void = (event: TransitionEvent): void => {\n if (event.propertyName !== 'transform' || settled) {\n return;\n }\n settled = true;\n element.removeEventListener('transitionend', handler);\n callback();\n };\n element.addEventListener('transitionend', handler);\n\n // Страховка на случай пропущенного `transitionend` (лаг рендера).\n setTimeout((): void => {\n if (settled) {\n return;\n }\n settled = true;\n element.removeEventListener('transitionend', handler);\n callback();\n }, durationMs + ASIDE_TRANSITION_BUFFER_MS);\n }\n\n // Максимальная длительность transition'а элемента в мс; `0` если анимации нет.\n #transitionDurationMs(element: HTMLElement): number {\n const durationCss: string | undefined = this.#window?.getComputedStyle(element).transitionDuration;\n if (durationCss === undefined || durationCss === '') {\n return 0;\n }\n const maxSeconds: number = durationCss.split(',').reduce((max: number, part: string): number => {\n const trimmed: string = part.trim();\n const seconds: number = trimmed.endsWith('ms') ? Number.parseFloat(trimmed) / 1000 : Number.parseFloat(trimmed);\n return Number.isNaN(seconds) ? max : Math.max(max, seconds);\n }, 0);\n return maxSeconds * 1000;\n }\n}\n","<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","// Маркеры слотов переехали в соседний файл — иначе оболочка и панельные директивы замыкали\n// круг импортов. Здесь они остаются видимыми прежним потребителям.\nexport * from './rt-container.markers';\n\nimport { DestroyRef, Directive, OnDestroy, OutputEmitterRef, Signal, inject, output } from '@angular/core';\nimport { outputToObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { RtContainerStateService } from './rt-container-state.service';\nimport { RtContainerComponent } from './rt-container.component';\n\n/**\n * Driver-директива для управления левой sidenav через шаблонную ссылку.\n *\n * Пишет в component-scoped `RtContainerStateService.leftSidenavOpen` signal —\n * шаблон контейнера читает этот signal и применяет docked grid-cell состояние.\n *\n * @example\n * ```html\n * <ng-container rtContainerLeftSidenavPanel #left=\"containerLeftPanel\">\n * <button (click)=\"left.toggle(true)\">Open</button>\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerLeftSidenavPanel]',\n exportAs: 'containerLeftPanel',\n})\nexport class RtContainerLeftSidenavPanelDirective {\n readonly #state: RtContainerStateService = inject(RtContainerStateService);\n\n public open(): void {\n this.#state.leftSidenavOpen.set(true);\n }\n\n public close(): void {\n this.#state.leftSidenavOpen.set(false);\n }\n\n public toggle(isOpen: boolean): void {\n this.#state.leftSidenavOpen.set(isOpen);\n }\n}\n\n/**\n * Driver-директива правой sidenav: route-component вызывает `open()`/`close()`,\n * rt-container выполняет slide-in/slide-out через CSS transform. После завершения\n * анимации driver эмитит `(opened)` / `(closed)`; backdrop click и ESC контейнера\n * эмитятся через `(cancelled)` — консьюмер сам решает что делать (обычно `close()`).\n *\n * Sub'ы через `takeUntilDestroyed` снимаются при разрушении route-component'а.\n * При уничтожении с открытой панелью гарантирует cleanup overlay'я (см. `ngOnDestroy`),\n * иначе при hard-navigate без `panel.close()` overlay останется висеть.\n *\n * @example\n * ```html\n * <ng-container\n * rtContainerRightSidenavPanel\n * #right=\"rtContainerRightPanel\"\n * (cancelled)=\"onClose()\"\n * (closed)=\"onClosed()\">\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerRightSidenavPanel]',\n exportAs: 'rtContainerRightPanel',\n})\nexport class RtContainerRightSidenavPanelDirective implements OnDestroy {\n readonly #container: RtContainerComponent = inject(RtContainerComponent);\n\n public readonly opened: OutputEmitterRef<void> = output<void>();\n\n public readonly closed: OutputEmitterRef<void> = output<void>();\n\n public readonly cancelled: OutputEmitterRef<void> = output<void>();\n\n /**\n * Re-export rt-container.rightOverlayReady — `true` когда CDK Overlay создан\n * и attache'нут. Консьюмер должен ждать этого signal'а в effect'е, перед\n * тем как вызывать `panel.open()` (иначе race: rt-container.effect ещё не\n * создал overlay → openRight() рано вернётся → --entering остаётся).\n */\n public readonly ready: Signal<boolean> = this.#container.rightOverlayReady;\n\n constructor() {\n const destroyRef: DestroyRef = inject(DestroyRef);\n\n // `output()` возвращает OutputEmitterRef (нет .pipe) — оборачиваем через\n // outputToObservable, чтобы пристегнуть takeUntilDestroyed.\n outputToObservable(this.#container.backdropClick)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.cancelled.emit());\n\n outputToObservable(this.#container.rightOpened)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.opened.emit());\n\n outputToObservable(this.#container.rightClosed)\n .pipe(takeUntilDestroyed(destroyRef))\n .subscribe((): void => this.closed.emit());\n }\n\n public ngOnDestroy(): void {\n // Если route-component разрушается при открытой панели (hard-navigate без\n // явного close через panel.close) — гарантируем cleanup, иначе overlay\n // останется висеть после смены роута.\n if (this.#container.rightOpen()) {\n this.#container.closeRight();\n }\n }\n\n public open(): void {\n this.#container.openRight();\n }\n\n public close(): void {\n this.#container.closeRight();\n }\n}\n\n/**\n * Driver-директива для основного content-региона `rt-container`.\n *\n * Объявляет 3 outputs (`scrolled/scrolledUp/scrolledDown`) — публичную поверхность\n * для будущих consumer'ов. Real-time wiring (scroll listeners → emit) отложено\n * до первого реального потребителя.\n *\n * @example\n * ```html\n * <ng-container\n * rtContainerContentPanel\n * (scrolled)=\"onScroll()\"\n * >\n * <router-outlet />\n * </ng-container>\n * ```\n */\n@Directive({\n selector: '[rtContainerContentPanel]',\n})\nexport class RtContainerContentPanelDirective {\n public readonly scrolled: OutputEmitterRef<void> = output<void>();\n\n public readonly scrolledUp: OutputEmitterRef<void> = output<void>();\n\n public readonly scrolledDown: OutputEmitterRef<void> = output<void>();\n}\n","import { DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { catchError, map, mergeMap, Observable, of, Subject } from 'rxjs';\n\n/** Одна запущенная правка: сам поток работы и приёмы её удачи и отказа. */\nexport interface IRtRouteAsideMutation {\n op$: Observable<unknown>;\n handleSuccess: () => void;\n handleError: (error: unknown) => void;\n}\n\n/**\n * Правки, запущенные панелью.\n *\n * Подписка объявлена одна на панель, а не заводится на каждый вызов: каждая правка живёт\n * независимо от соседней, и отказ одной не уносит поток остальных. Приёмы удачи и отказа\n * приезжают вместе с работой — они замыкают доводы своего вызова.\n */\nexport class RtRouteAsideMutations {\n readonly #mutationSource: Subject<IRtRouteAsideMutation> = new Subject<IRtRouteAsideMutation>();\n\n constructor(destroyRef: DestroyRef) {\n this.#mutationSource\n .pipe(\n mergeMap((mutation: IRtRouteAsideMutation): Observable<() => void> =>\n mutation.op$.pipe(\n map((): (() => void) => mutation.handleSuccess),\n catchError((error: unknown): Observable<() => void> => of((): void => mutation.handleError(error)))\n )\n ),\n takeUntilDestroyed(destroyRef)\n )\n .subscribe((handleResult: () => void): void => handleResult());\n }\n\n /** Пускает правку в объявленный поток. */\n public run(mutation: IRtRouteAsideMutation): void {\n this.#mutationSource.next(mutation);\n }\n}\n","/**\n * Абсолютные команды приводятся к сегментам от корня: внутри объекта `outlets`\n * ведущий слэш отдельным сегментом не разбирается.\n *\n * Нужно это затем, что уход панели на связанную запись обязан снять её аутлет\n * той же навигацией. Абсолютные команды меняют только первичную ветку, поэтому\n * панель, открытая в корне приложения, переход переживает: адрес получается с\n * аутлетом, под который в корне маршрута нет, и роутер отклоняет навигацию\n * молча — адрес остаётся прежним, а приложение застревает на панели, которой на\n * экране уже нет.\n */\nexport function rootSegmentsOf(commands: readonly unknown[]): unknown[] {\n const [head, ...rest]: readonly unknown[] = commands;\n\n if (typeof head !== 'string' || !head.startsWith('/')) {\n return [...commands];\n }\n\n return [...head.split('/').filter(Boolean), ...rest];\n}\n","import { Params, Router, UrlTree } from '@angular/router';\n\nimport { rootSegmentsOf } from './rt-route-aside.logic';\n\n/** Аутлеты, которые уход панели снимает вместе со своим. */\nexport type TRtRouteAsideOutlets = Record<string, Array<string> | null>;\n\n/**\n * Уходы панели по маршруту и разрешение на них.\n *\n * Разрешение выдаётся вплотную к вызову маршрутизатора и снимается итогом навигации:\n * между закрытием панели и уходом наложение доигрывает своё, и разрешение, выданное\n * раньше, досталось бы чужому уходу — тронутая форма уехала бы без вопроса. Читается оно\n * тоже один раз: иначе первое же закрытие после записи сняло бы вопрос о правках со всех\n * следующих уходов.\n */\nexport class RtRouteAsideNavigation {\n readonly #router: Router;\n #leaveAllowed: boolean = false;\n\n constructor(router: Router) {\n this.#router = router;\n }\n\n /** Разрешение на этот уход. Читается один раз и снимается чтением. */\n public consumeLeaveAllowance(): boolean {\n const allowed: boolean = this.#leaveAllowed;\n\n this.#leaveAllowed = false;\n\n return allowed;\n }\n\n /** Уход, начатый самой панелью: разрешение живёт ровно эту навигацию. */\n public allowed(navigate: () => Promise<boolean>): void {\n this.#leaveAllowed = true;\n\n void navigate()\n .catch((): boolean => false)\n .then((): void => {\n this.#leaveAllowed = false;\n });\n }\n\n /**\n * Адрес, на который уходит панель. Свой аутлет она снимает тем же деревом: абсолютные\n * команды меняют только первичную ветку, и панель, открытая в корне приложения, переход\n * пережила бы — маршрута под неё в корне нет, а такую навигацию маршрутизатор отклоняет\n * молча, оставляя приложение на панели, которой на экране уже нет.\n */\n public relatedTree(outlets: TRtRouteAsideOutlets, commands: readonly unknown[], queryParams: Params | null = null): UrlTree {\n return this.#router.createUrlTree([{ outlets: { ...outlets, primary: rootSegmentsOf(commands) } }], { queryParams });\n }\n}\n","import { Injector, Signal } from '@angular/core';\nimport { toObservable } from '@angular/core/rxjs-interop';\n\nimport { filter, finalize, map, Observable, of, shareReplay, switchMap, take } from 'rxjs';\n\nimport { RtAsideUnsavedDialogComponent } from '@rt-tools/ui-kit-v2/aside';\nimport { ERtAsideUnsavedOutcome } from '@rt-tools/ui-kit-v2/aside';\nimport { RtDialogService } from '@rt-tools/ui-kit-v2/dialog';\n\n/** То, что вопрос о правках знает о панели: чем записать и чем узнать исход записи. */\nexport interface IRtRouteAsideUnsavedDeps {\n save: () => void;\n submitting: Signal<boolean>;\n submitError: Signal<string | null>;\n}\n\n/**\n * Вопрос о несохранённых правках, заданный уходу по маршруту.\n *\n * Окно открывается одно на все уходы, пришедшие пока оно висит: второе поставило бы поверх\n * первого наложение, убрать которое было бы некому. Ответ раздаётся всем спросившим, а после\n * закрытия окна следующий уход спрашивает заново.\n */\nexport class RtRouteAsideUnsavedPrompt {\n readonly #dialog: RtDialogService;\n readonly #injector: Injector;\n readonly #deps: IRtRouteAsideUnsavedDeps;\n\n /** Вопрос, который сейчас висит. `null` — окна нет, и следующий уход откроет своё. */\n #pendingAsk$: Observable<boolean> | null = null;\n\n constructor(dialog: RtDialogService, injector: Injector, deps: IRtRouteAsideUnsavedDeps) {\n this.#dialog = dialog;\n this.#injector = injector;\n this.#deps = deps;\n }\n\n /** Отпускать ли уход. Пока окно висит, все спросившие получают один и тот же ответ. */\n public ask(): Observable<boolean> {\n const pending: Observable<boolean> | null = this.#pendingAsk$;\n\n if (pending !== null) {\n return pending;\n }\n\n const ask$: Observable<boolean> = this.#dialog\n .open<RtAsideUnsavedDialogComponent, undefined, ERtAsideUnsavedOutcome>(RtAsideUnsavedDialogComponent)\n .afterClosed()\n .pipe(\n switchMap((outcome: ERtAsideUnsavedOutcome | undefined): Observable<boolean> => this.#deactivateAfter(outcome)),\n finalize((): void => {\n this.#pendingAsk$ = null;\n }),\n shareReplay({ bufferSize: 1, refCount: false })\n );\n\n this.#pendingAsk$ = ask$;\n\n return ask$;\n }\n\n /**\n * Что делать с уходом после ответа человека. «Закрыть с сохранением» ждёт конца записи:\n * панель уступает место только удачной — иначе правки пропали бы вместе с отказом, о\n * котором человек ещё не знает.\n */\n #deactivateAfter(outcome: ERtAsideUnsavedOutcome | undefined): Observable<boolean> {\n if (outcome === ERtAsideUnsavedOutcome.Discard) {\n return of(true);\n }\n\n if (outcome !== ERtAsideUnsavedOutcome.Save) {\n return of(false);\n }\n\n this.#deps.save();\n\n // Запись могла и не начаться — например, черновик не годится\n if (!this.#deps.submitting()) {\n return of(false);\n }\n\n return toObservable(this.#deps.submitting, { injector: this.#injector }).pipe(\n filter((submitting: boolean): boolean => !submitting),\n take(1),\n map((): boolean => this.#deps.submitError() === null)\n );\n }\n}\n","import { Signal, isSignal } from '@angular/core';\nimport { toObservable, toSignal } from '@angular/core/rxjs-interop';\nimport { AbstractControl, PristineChangeEvent } from '@angular/forms';\n\nimport { filter, map, Observable, of, startWith, switchMap } from 'rxjs';\n\n/**\n * Признак нетронутой формы.\n *\n * Контрол принимается и сигналом: форма панели собрана директивой формы и до отрисовки\n * шаблона её ещё нет, поэтому запрос вида отдаёт её только сигналом. Нынешнее состояние\n * подставляется первым значением — события прошлого подписка не получит, и без него кнопка\n * записи осталась бы недоступной у формы, испачканной до подписки.\n *\n * Зовётся в контексте внедрения: `toObservable` и `toSignal` требуют его сами.\n */\nexport function pristineOf(control: AbstractControl | Signal<AbstractControl | undefined>): Signal<boolean> {\n const control$: Observable<AbstractControl | undefined> = isSignal(control) ? toObservable(control) : of(control);\n\n return toSignal(\n control$.pipe(\n switchMap((current: AbstractControl | undefined): Observable<boolean> => {\n if (current === undefined) {\n return of(true);\n }\n\n return current.events.pipe(\n filter((event: unknown): event is PristineChangeEvent => event instanceof PristineChangeEvent),\n map((event: PristineChangeEvent): boolean => event.pristine),\n startWith(current.pristine)\n );\n })\n ),\n { initialValue: true }\n );\n}\n","import { Injectable } from '@angular/core';\nimport { ActivatedRouteSnapshot, Route } from '@angular/router';\n\nimport { Observable } from 'rxjs';\n\n/**\n * Панель, которая умеет ответить на вопрос «можно ли уступить место».\n *\n * Объявлена рядом с учётом панелей, а не рядом с гардом: гард берёт учёт значением, и\n * объявление у него замыкало круг «учёт → гард → учёт».\n */\nexport interface IRtAsideDeactivate {\n canDeactivate(): Observable<boolean>;\n}\n\n/**\n * Учёт панелей, стоящих на экране: из него роутерный гард узнаёт, кого спрашивать\n * о несохранённых правках.\n *\n * Спрашивать компонент, который роутер подставляет в гард, нельзя: когда\n * ro-маршрут объявлен под уровнем без своего компонента, Angular отдаёт детям\n * контекст родителя, и в гард приходит компонент первичного аутлета — экран\n * раздела, а не панель.\n *\n * Ключ записи — объявление маршрута, а не порядок постановки. Гард стоит на\n * каждом ro-маршруте, и одна навигация зовёт его за каждый уходящий маршрут\n * отдельно: учёт стеком дал бы ответ не той панели и два вопроса о правках\n * подряд. Объявление маршрута — та же ссылка на объект и у панели, и у снимка,\n * который роутер отдаёт гарду, поэтому два каркаса на экране не путаются даже\n * при одинаково названных аутлетах: маршруты у них разные.\n *\n * Служба корневая: гард живёт в инжекторе маршрута и до службы, поставленной на\n * уровне компонента, не дотянулся бы вовсе. Одного учёта на приложение хватает —\n * записи различает маршрут. Переменной уровня модуля учёт быть не может: та\n * пережила бы запрос при отдаче страницы сервером и оказалась бы общей на два\n * приложения, поднятых на одной странице.\n */\n@Injectable({ providedIn: 'root' })\nexport class RtRouteAsideRegistry {\n readonly #panels: Map<Route, IRtAsideDeactivate> = new Map<Route, IRtAsideDeactivate>();\n\n /**\n * Панель встала на экран. Повторная постановка под тем же маршрутом заменяет\n * запись: аутлет держит одну панель, и отвечать должна нынешняя.\n */\n public register(route: ActivatedRouteSnapshot, panel: IRtAsideDeactivate): void {\n const key: Route | null = route.routeConfig;\n\n if (key === null) {\n return;\n }\n\n this.#panels.set(key, panel);\n }\n\n /**\n * Панель ушла с экрана. Запись снимается только своя: пока уходящая панель\n * доигрывает уход, её место мог занять сосед, и снятие вслепую стёрло бы\n * запись того, кто на экране остался.\n */\n public unregister(route: ActivatedRouteSnapshot, panel: IRtAsideDeactivate): void {\n const key: Route | null = route.routeConfig;\n\n if (key === null || this.#panels.get(key) !== panel) {\n return;\n }\n\n this.#panels.delete(key);\n }\n\n /** Снятие панели, чей ответ о правках отказал: спрашивать её больше нечем. */\n public forget(panel: IRtAsideDeactivate): void {\n for (const [key, registered] of this.#panels) {\n if (registered === panel) {\n this.#panels.delete(key);\n }\n }\n }\n\n /** Панель, отвечающая за уход с этого маршрута, или её отсутствие. */\n public find(route: ActivatedRouteSnapshot): IRtAsideDeactivate | null {\n const key: Route | null = route.routeConfig;\n\n if (key === null) {\n return null;\n }\n\n return this.#panels.get(key) ?? null;\n }\n}\n","import {\n computed,\n DestroyRef,\n Directive,\n effect,\n inject,\n Injector,\n OnInit,\n Signal,\n signal,\n viewChild,\n WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { AbstractControl } from '@angular/forms';\nimport { ActivatedRoute, ParamMap, Params, Router, UrlTree } from '@angular/router';\n\nimport { exhaustMap, mergeMap, Observable, of, Subject, switchMap, tap } from 'rxjs';\n\nimport { NotificationBus } from '@rt-tools/ui-kit-v2/core';\n\nimport { RtAsideUnsavedDialogComponent } from '@rt-tools/ui-kit-v2/aside';\nimport { ERtAsideCloseIntent, ERtAsideUnsavedOutcome, IRtAsideUnsavedGuard, resolveCloseIntent } from '@rt-tools/ui-kit-v2/aside';\nimport { RtDialogService } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtContainerRightSidenavPanelDirective } from './rt-container.directives';\nimport { RtRouteAsideMutations } from './rt-route-aside.mutations';\nimport { RtRouteAsideNavigation } from './rt-route-aside.navigation';\nimport { RtRouteAsideUnsavedPrompt } from './rt-route-aside.unsaved';\nimport { pristineOf } from './rt-route-aside.pristine';\nimport { RtRouteAsideRegistry } from './rt-route-aside.registry';\n\n/**\n * Открытие ждёт `panel.ready()`, а не `ngAfterViewInit`: на F5 rt-container\n * создаёт overlay асинхронно, иначе `open()` гонится с ещё не готовым overlay.\n *\n * `@Directive()` без селектора — чтобы query/`inject`/lifecycle наследовались на\n * конкретный `@Component` (как `RtFormControlBase`).\n *\n * Закрытие всегда идёт через `onClose()`, куда сходятся все четыре пути: две\n * кнопки панели и — через `onCancel()` — нажатие мимо панели и Esc, которые ловит\n * `rt-container`. Панель, поставившая гард через `guardUnsavedChanges()`, на\n * тронутой форме получает вопрос о правках; панель без гарда закрывается сразу.\n */\n@Directive()\nexport abstract class RtRouteAsideComponent<T> implements OnInit {\n readonly #entity: WritableSignal<T | null> = signal<T | null>(null);\n readonly #entityId: WritableSignal<string | null> = signal<string | null>(null);\n readonly #isCreateMode: WritableSignal<boolean> = signal(false);\n readonly #resolving: WritableSignal<boolean> = signal(false);\n readonly #submitting: WritableSignal<boolean> = signal(false);\n\n readonly #mutations: RtRouteAsideMutations = new RtRouteAsideMutations(inject(DestroyRef));\n readonly #refreshSource: Subject<string> = new Subject<string>();\n readonly #closeIntentSource: Subject<void> = new Subject<void>();\n\n readonly #unsavedGuard: WritableSignal<IRtAsideUnsavedGuard | null> = signal<IRtAsideUnsavedGuard | null>(null);\n\n /** Связанная запись, на которую панель уйдёт, когда пользователь решит судьбу правок */\n #relatedCommands: readonly unknown[] | null = null;\n\n /** Параметры адреса связанной записи: у события журнала ими задан отбор списка */\n #relatedQueryParams: Params | null = null;\n\n readonly #notificationBus: NotificationBus = inject(NotificationBus);\n readonly #dialog: RtDialogService = inject(RtDialogService);\n readonly #injector: Injector = inject(Injector);\n readonly #registry: RtRouteAsideRegistry = inject(RtRouteAsideRegistry);\n\n /** Вопрос о правках, заданный уходу по маршруту: у кнопок панели свой путь. */\n readonly #unsavedPrompt: RtRouteAsideUnsavedPrompt = new RtRouteAsideUnsavedPrompt(this.#dialog, this.#injector, {\n save: (): void => this.#unsavedGuard()?.save(),\n submitting: this.#submitting,\n submitError: computed((): string | null => this.submitError()),\n });\n\n #opened: boolean = false;\n\n protected readonly router: Router = inject(Router);\n readonly #navigation: RtRouteAsideNavigation = new RtRouteAsideNavigation(this.router);\n protected readonly route: ActivatedRoute = inject(ActivatedRoute);\n protected readonly destroyRef: DestroyRef = inject(DestroyRef);\n\n protected readonly panel: Signal<RtContainerRightSidenavPanelDirective | undefined> = viewChild(RtContainerRightSidenavPanelDirective);\n\n protected readonly entity: Signal<T | null> = this.#entity.asReadonly();\n\n /** id из route-параметра как есть (uuid/строка); числовые id консьюмер парсит сам. */\n protected readonly entityId: Signal<string | null> = this.#entityId.asReadonly();\n protected readonly isCreateMode: Signal<boolean> = this.#isCreateMode.asReadonly();\n protected readonly submitting: Signal<boolean> = this.#submitting.asReadonly();\n\n /**\n * Скелетоны биндятся на это, а не на пустоту `entity()`: та залипает после\n * первой загрузки и не ловит рефетч.\n */\n protected readonly resolving: Signal<boolean> = this.#resolving.asReadonly();\n\n protected readonly busy: Signal<boolean> = computed((): boolean => this.#submitting() || this.#resolving());\n\n /**\n * Форма не тронута. Панель, которая не поставила гард закрытия, считается\n * нетронутой всегда — и закрывается без вопроса, как раньше.\n */\n protected readonly pristine: Signal<boolean> = computed((): boolean => this.#unsavedGuard()?.pristine() ?? true);\n\n protected readonly submitError: WritableSignal<string | null> = signal<string | null>(null);\n\n /**\n * Удача мутации — тем же способом, что и отказ. Человек, работающий в панели,\n * смотрит в панель, а не на экран за её краем: тост об успехе всплывает там,\n * куда он в эту минуту не смотрит. Оба сигнала гасятся в начале каждой\n * попытки и гасят друг друга — это ответы на один вопрос, и двух сразу не\n * бывает.\n */\n protected readonly submitSuccess: WritableSignal<string | null> = signal<string | null>(null);\n\n protected readonly idParamName: string = 'id';\n\n /** id-only режим: нужен только id (например, смена пароля по userId), сущность не грузится. */\n protected readonly idOnly: boolean = false;\n\n constructor() {\n effect((): void => {\n const panel: RtContainerRightSidenavPanelDirective | undefined = this.panel();\n if (panel === undefined || !panel.ready() || this.#opened) {\n return;\n }\n this.#opened = true;\n panel.open();\n // Панель встала в аутлет — с этого мгновения о правках спрашивают её,\n // а не тот компонент, который роутер подставит гарду.\n this.#registry.register(this.route.snapshot, this);\n });\n\n // Снятие с учёта идёт по уничтожению, а не по уходу с экрана: уход может и\n // не состояться — роутер отклоняет навигацию молча, — и панель, снятая с\n // учёта раньше времени, осталась бы на экране, не отвечая о своих правках.\n this.destroyRef.onDestroy((): void => this.#registry.unregister(this.route.snapshot, this));\n\n // Рефетч сущности из #refreshEntity(): mergeMap — каждый запуск resolve\n // живёт независимо (как раньше — отдельная подписка на вызов).\n this.#refreshSource\n .pipe(\n mergeMap((id: string): Observable<T | null> => this.resolve(id)),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe((entity: T | null): void => {\n this.#resolving.set(false);\n if (entity === null) {\n this.#navigateAway();\n return;\n }\n this.#entity.set(entity);\n this.onResolved();\n });\n\n // Вопрос о несохранённых правках: exhaustMap, а не switchMap — пока окно\n // открыто, повторное намерение закрыться игнорируется. switchMap отписался\n // бы от первого окна, не убрав его overlay, и на экране осталось бы два.\n this.#closeIntentSource\n .pipe(\n exhaustMap((): Observable<ERtAsideUnsavedOutcome | undefined> =>\n this.#dialog\n .open<RtAsideUnsavedDialogComponent, undefined, ERtAsideUnsavedOutcome>(RtAsideUnsavedDialogComponent)\n .afterClosed()\n ),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe((outcome: ERtAsideUnsavedOutcome | undefined): void => this.#applyUnsavedOutcome(outcome));\n }\n\n public ngOnInit(): void {\n this.route.paramMap\n .pipe(\n switchMap((params: ParamMap): Observable<T | null> => {\n const idParam: string | null = params.get(this.idParamName);\n if (idParam === null || idParam === '') {\n this.#isCreateMode.set(true);\n this.#entityId.set(null);\n return of(null);\n }\n this.#isCreateMode.set(false);\n this.#entityId.set(idParam);\n if (this.idOnly) {\n return of(null);\n }\n this.#resolving.set(true);\n return this.resolve(idParam);\n }),\n tap((entity: T | null): void => {\n this.#resolving.set(false);\n if (entity === null) {\n if (!this.#isCreateMode() && !this.idOnly) {\n this.#navigateAway();\n }\n return;\n }\n this.#entity.set(entity);\n this.onResolved();\n }),\n takeUntilDestroyed(this.destroyRef)\n )\n .subscribe();\n }\n\n /**\n * Панель уходит с экрана не сама: её место в аутлете занимает другая — лента\n * событий из шапки или соседняя запись. Тронутая форма спрашивает о правках\n * так же, как при закрытии; окно кнопок этот путь не ловит, потому что панель\n * закрывает роутер.\n *\n * Гард ставится на ro-маршрут панели (`rtAsideUnsavedGuard`).\n */\n public canDeactivate(): Observable<boolean> {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n // Признак идущей записи старше разрешения на уход: панель, выдавшая себе\n // разрешение и не дождавшаяся ответа сервера, уходить не должна — исход\n // записи решит её судьбу сам.\n if (intent === ERtAsideCloseIntent.Ignore) {\n return of(false);\n }\n\n // Уход начала сама панель: правки либо записаны секунду назад, либо их\n // судьбу пользователь уже решил. Вопрос был бы задан про то, чего нет.\n if (this.#navigation.consumeLeaveAllowance()) {\n return of(true);\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n return of(true);\n }\n\n return this.#unsavedPrompt.ask();\n }\n\n protected setEntity(entity: T | null): void {\n this.#entity.set(entity);\n }\n\n /** Признак нетронутой формы — тот же, что гейтит кнопку записи. */\n protected pristineSignal(control: AbstractControl | Signal<AbstractControl | undefined>): Signal<boolean> {\n return pristineOf(control);\n }\n\n /**\n * Ставит гард закрытия: пока форма тронута, любой из четырёх путей закрытия\n * спрашивает, что сделать с правками, и исход «закрыть с сохранением» зовёт\n * `save`. Признак нетронутости даёт `pristineSignal(control)` — тот же, что\n * гейтит кнопку записи.\n */\n protected guardUnsavedChanges(guard: IRtAsideUnsavedGuard): void {\n this.#unsavedGuard.set(guard);\n }\n\n /**\n * Адрес связанной записи для атрибута `href`. Ссылка остаётся настоящей\n * ссылкой: её открывают в новой вкладке и копируют, а обычное нажатие панель\n * перехватывает и уводит сама.\n */\n protected relatedUrl(commands: readonly unknown[]): string {\n return this.router.serializeUrl(this.#relatedTree(commands));\n }\n\n /**\n * Уход на связанную запись. Он уводит с адреса панели так же, как её\n * закрытие, поэтому идёт тем же путём: тронутая форма спрашивает, что\n * сделать с правками, и на исходе «закрыть с сохранением» запись открывается\n * после удачной записи.\n */\n protected openRelated(commands: readonly unknown[], queryParams: Params | null = null): void {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n if (intent === ERtAsideCloseIntent.Ignore) {\n return;\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n this.#navigateRelated(commands, queryParams);\n\n return;\n }\n\n this.#relatedQueryParams = queryParams;\n\n this.#relatedCommands = commands;\n // Подписка объявлена один раз в конструкторе (см. #closeIntentSource-стрим).\n this.#closeIntentSource.next();\n }\n\n protected onResolved(): void {\n // Хук для наследника: базовый асайд после разрешения данных ничего не делает.\n }\n\n protected closeOutlets(): Record<string, Array<string> | null> {\n return { ro: null };\n }\n\n /**\n * `successText` — готовый текст тоста об успехе. Он принадлежит конкретной\n * мутации, а не панели: у снятия промокода и опроса подписки свой текст\n * успеха, и один на панель показал бы не тот.\n *\n * Именно текст, а не ключ словаря: ключ принадлежал бы словарю приложения, а\n * кит чужих словарей не знает — переводит приложение и передаёт готовое.\n *\n * `errorText` принимается и функцией: причина отказа известна только после\n * него — стор кладёт её в свой сигнал, а панель без стора выводит из ответа\n * сервера, — и готовой строкой на момент запуска мутации её не передать.\n *\n * `successMessage` — текст удачи, который панель показывает внутри себя,\n * рядом с формой. Довод отделён от `successText` намеренно: переезд текста\n * из тоста в панель сломал бы всякого потребителя, который тост и хотел, а\n * сказать «мне нужен тост» ему после этого было бы нечем. Назван он по месту\n * показа, а не по исходу: два довода с разницей в одном слоге читались бы\n * как опечатка.\n */\n protected runMutation(\n op$: Observable<unknown>,\n opts?: {\n onSuccess?: () => void;\n successText?: string;\n successMessage?: string;\n errorText?: string | ((error: unknown) => string);\n closeOnSuccess?: boolean;\n refreshOnSuccess?: boolean;\n }\n ): void {\n this.#submitting.set(true);\n this.submitError.set(null);\n this.submitSuccess.set(null);\n this.#mutations.run({\n op$,\n handleSuccess: (): void => {\n this.#submitting.set(false);\n if (opts?.successText) {\n this.#notificationBus.success(opts.successText);\n }\n if (opts?.successMessage) {\n this.submitSuccess.set(opts.successMessage);\n }\n // Пользователь шёл на связанную запись и по дороге согласился\n // сохранить: панель уходит туда, куда он нажал.\n const related: readonly unknown[] | null = this.#relatedCommands;\n if (related !== null) {\n this.#navigateRelated(related);\n return;\n }\n if (opts?.onSuccess) {\n opts.onSuccess();\n return;\n }\n // Закрытие после удачи идёт мимо обоих вопросов о правках: форма ещё\n // тронута, а спрашивать про только что записанное не о чем — уходу по\n // маршруту это закрывает разрешение на уход. Названный довод решает в обе\n // стороны, а не названный считается по режиму: создавать в закрытой панели\n // нечего.\n if (opts?.closeOnSuccess ?? this.#isCreateMode()) {\n this.#closePanel();\n return;\n }\n if (opts?.refreshOnSuccess === true) {\n this.#refreshEntity();\n }\n },\n handleError: (error: unknown): void => {\n this.#submitting.set(false);\n // Отказ снимает прежнюю удачу: иначе «сохранено» осталось бы на\n // панели рядом со свежим отказом.\n this.submitSuccess.set(null);\n // Отказ оставляет человека в панели с его правками, поэтому\n // намерение уйти на связанную запись снимается.\n this.#relatedCommands = null;\n const text: string | undefined = typeof opts?.errorText === 'function' ? opts.errorText(error) : opts?.errorText;\n if (text) {\n this.submitError.set(text);\n }\n },\n });\n }\n\n /**\n * Единственная точка закрытия панели. Через неё идут все четыре пути: кнопка\n * в шапке, кнопка в футере и — через `onCancel()` — нажатие мимо панели и\n * клавиша Esc, которые ловит `rt-container` и отдаёт панели одним выходом\n * `cancelled`.\n */\n protected onClose(): void {\n const intent: ERtAsideCloseIntent = this.#closeIntent();\n\n if (intent === ERtAsideCloseIntent.Ignore) {\n return;\n }\n\n if (intent === ERtAsideCloseIntent.Close) {\n this.#closePanel();\n return;\n }\n\n // Подписка объявлена один раз в конструкторе (см. #closeIntentSource-стрим).\n this.#closeIntentSource.next();\n }\n\n protected onCancel(): void {\n this.onClose();\n }\n\n protected onClosed(): void {\n this.#navigateAway();\n }\n\n protected abstract resolve(id: string): Observable<T | null>;\n\n #refreshEntity(): void {\n const id: string | null = this.#entityId();\n if (id === null || this.idOnly) {\n return;\n }\n this.#resolving.set(true);\n // Подписка объявлена один раз в конструкторе (см. #refreshSource-стрим).\n this.#refreshSource.next(id);\n }\n\n /** Решение по намерению уйти с панели — одно на закрытие и на переход по связанной записи */\n #closeIntent(): ERtAsideCloseIntent {\n return resolveCloseIntent({\n submitting: this.#submitting(),\n guarded: this.#unsavedGuard() !== null,\n pristine: this.pristine(),\n });\n }\n\n #closePanel(): void {\n this.panel()?.close();\n }\n\n #navigateRelated(commands: readonly unknown[], queryParams: Params | null = null): void {\n const params: Params | null = queryParams ?? this.#relatedQueryParams;\n\n this.#relatedCommands = null;\n this.#relatedQueryParams = null;\n this.#navigation.allowed((): Promise<boolean> => this.router.navigateByUrl(this.#relatedTree(commands, params)));\n }\n\n #relatedTree(commands: readonly unknown[], queryParams: Params | null = null): UrlTree {\n return this.#navigation.relatedTree(this.closeOutlets(), commands, queryParams);\n }\n\n #applyUnsavedOutcome(outcome: ERtAsideUnsavedOutcome | undefined): void {\n const related: readonly unknown[] | null = this.#relatedCommands;\n\n if (outcome === ERtAsideUnsavedOutcome.Discard) {\n if (related !== null) {\n this.#navigateRelated(related);\n return;\n }\n this.#closePanel();\n return;\n }\n\n // Запись закрывает панель сама на успехе — иначе панель ушла бы с экрана\n // раньше ответа, и отказ негде было бы показать. Уход на\n // связанную запись ждёт того же успеха.\n if (outcome === ERtAsideUnsavedOutcome.Save) {\n this.#unsavedGuard()?.save();\n // Запись могла и не начаться — например, черновик не годится. Тогда\n // намерение уйти снимается вместе с ней, иначе панель ушла бы на\n // связанную запись после следующей удачной записи.\n if (!this.#submitting()) {\n this.#relatedCommands = null;\n }\n return;\n }\n\n this.#relatedCommands = null;\n }\n\n /**\n * Уход с адреса при закрытии.\n *\n * Параметры адреса переносятся нетронутыми: панель открыта поверх экрана, чьё состояние в\n * них и живёт — страница списка, порядок, отбор. Без переноса закрытая панель возвращает\n * экран к его первой странице без отбора, и человек, открывший запись со второй страницы\n * отобранного списка, теряет её просмотром одной записи. Свои параметры панель называет\n * `openRelated` явно, и на них перенос не действует.\n */\n #navigateAway(): void {\n this.#navigation.allowed((): Promise<boolean> =>\n this.router.navigate([{ outlets: this.closeOutlets() }], {\n relativeTo: this.route.parent,\n queryParamsHandling: 'preserve',\n })\n );\n }\n}\n","import { inject } from '@angular/core';\nimport { ActivatedRouteSnapshot, CanDeactivateFn } from '@angular/router';\n\nimport { catchError, defer, Observable, of } from 'rxjs';\n\nimport { IRtAsideDeactivate, RtRouteAsideRegistry } from './rt-route-aside.registry';\n\n// Объявление переехало к учёту панелей; здесь оно остаётся видимым прежним потребителям.\n\n/**\n * Панель с несохранёнными правками спрашивает о них и при смене маршрута, а не\n * только при закрытии: аутлет `ro` занимает соседняя панель, и правки пропали бы\n * молча.\n *\n * Ставится на каждый ro-маршрут: гард висит на кнопках, нажатии мимо и Esc, а\n * смену маршрута ловит только роутер.\n *\n * Спрашивается панель, учтённая под маршрутом ухода, а не компонент, который\n * подставил роутер. Компонент — не панель, когда ro-маршрут объявлен под уровнем\n * без своего компонента: Angular отдаёт детям контекст родителя, и в гард\n * приходит компонент первичного аутлета. Ответ такого компонента к правкам\n * отношения не имеет, а вызов у него метода, которого нет, ронял навигацию — она\n * падала необработанным отказом, адрес не менялся, и приложение оставалось\n * запертым на панели.\n *\n * Компонент от роутера остаётся запасным путём: гард выведен наружу кита, и\n * панель приложения может отвечать о правках, не наследуя базовый класс, — на\n * учёт такая панель не встаёт никогда. Без запасного пути у неё вопрос о правках\n * исчез бы молча, а это тихая потеря правок.\n *\n * Спрашивать некого — уступить место можно: роутер зовёт гард и для маршрута,\n * чей компонент не создан. Без этого первая же такая навигация падает, а следом\n * отказывает любая другая.\n *\n * Отказом навигации гард не отвечает никогда, в том числе когда ответ панели\n * отказал: разрешить уход значило бы унести правки, о которых спросить не\n * удалось, а отменить и оставить всё как есть — запереть приложение на панели\n * навсегда. Поэтому уход отменяется один раз, а панель снимается с учёта, и\n * следующий уход идёт запасным путём.\n */\n/** Компонент от роутера годится в ответчики, только если умеет отвечать. */\nfunction fallbackPanel(component: IRtAsideDeactivate | null): IRtAsideDeactivate | null {\n return typeof component?.canDeactivate === 'function' ? component : null;\n}\n\nexport const rtAsideUnsavedGuard: CanDeactivateFn<IRtAsideDeactivate | null> = (\n component: IRtAsideDeactivate | null,\n currentRoute: ActivatedRouteSnapshot\n): Observable<boolean> => {\n const registry: RtRouteAsideRegistry = inject(RtRouteAsideRegistry);\n const registered: IRtAsideDeactivate | null = registry.find(currentRoute);\n const panel: IRtAsideDeactivate | null = registered ?? fallbackPanel(component);\n\n if (panel === null) {\n return of(true);\n }\n\n // defer: ответ панели может отказать и синхронным броском, а гард обязан\n // пережить и его.\n return defer((): Observable<boolean> => panel.canDeactivate()).pipe(\n catchError((): Observable<boolean> => {\n registry.forget(panel);\n\n return of(false);\n })\n );\n};\n"],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@ import { DestroyRef, Directive, ElementRef, Injectable, InjectionToken, Injector
|
|
|
3
3
|
import { BreakpointObserver, Breakpoints } from "@angular/cdk/layout";
|
|
4
4
|
import { toSignal } from "@angular/core/rxjs-interop";
|
|
5
5
|
import { Subject, debounce, filter, map, timer } from "rxjs";
|
|
6
|
-
import { DOCUMENT, formatDate } from "@angular/common";
|
|
7
6
|
import { PlatformService, StorageService, WINDOW } from "@rt-tools/core";
|
|
7
|
+
import { DOCUMENT, formatDate } from "@angular/common";
|
|
8
8
|
const RT_KIT_CONFIG = new InjectionToken("RT_KIT_CONFIG", {
|
|
9
9
|
providedIn: "root",
|
|
10
10
|
factory: () => ({})
|
|
@@ -256,6 +256,7 @@ const RT_KIT_LABELS_EN = {
|
|
|
256
256
|
uiMoveStage: "Move",
|
|
257
257
|
uiMoveStageAria: "Move stage",
|
|
258
258
|
uiNavMenu: "Navigation menu",
|
|
259
|
+
uiNavMore: "More",
|
|
259
260
|
uiNavUnread: "Unread",
|
|
260
261
|
uiNextMonth: "Next month",
|
|
261
262
|
uiNextPage: "Next page",
|
|
@@ -417,16 +418,48 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
417
418
|
}]
|
|
418
419
|
}]
|
|
419
420
|
});
|
|
420
|
-
const
|
|
421
|
+
const RT_NARROW_WIDTH = 1080;
|
|
422
|
+
const NARROW_VIEWPORT_QUERY = `(max-width: ${RT_NARROW_WIDTH}px)`;
|
|
423
|
+
function rtNarrowByWidth(placeWidth, viewportNarrow) {
|
|
424
|
+
return placeWidth === null ? viewportNarrow : placeWidth <= RT_NARROW_WIDTH;
|
|
425
|
+
}
|
|
421
426
|
var BreakpointsService = class BreakpointsService {
|
|
422
427
|
constructor() {
|
|
423
428
|
this.#observer = inject(BreakpointObserver);
|
|
429
|
+
this.#window = inject(WINDOW);
|
|
430
|
+
this.#places = [];
|
|
431
|
+
this.#placeWidth = signal(null, ...ngDevMode ? [{ debugName: "#placeWidth" }] : /* istanbul ignore next */ []);
|
|
432
|
+
this.#resizeObserver = null;
|
|
424
433
|
this.mobile = toSignal(this.#match([Breakpoints.HandsetPortrait, Breakpoints.HandsetLandscape]), { initialValue: false });
|
|
425
434
|
this.tablet = toSignal(this.#match([Breakpoints.TabletPortrait, Breakpoints.TabletLandscape]), { initialValue: false });
|
|
426
435
|
this.desktop = toSignal(this.#match([Breakpoints.WebPortrait, Breakpoints.WebLandscape]), { initialValue: true });
|
|
427
|
-
this.
|
|
436
|
+
this.viewportNarrow = toSignal(this.#match([NARROW_VIEWPORT_QUERY]), { initialValue: false });
|
|
437
|
+
this.narrow = computed(() => rtNarrowByWidth(this.#placeWidth(), this.viewportNarrow()), ...ngDevMode ? [{ debugName: "narrow" }] : /* istanbul ignore next */ []);
|
|
428
438
|
}
|
|
429
439
|
#observer;
|
|
440
|
+
#window;
|
|
441
|
+
#places;
|
|
442
|
+
#placeWidth;
|
|
443
|
+
#resizeObserver;
|
|
444
|
+
registerPlace(element) {
|
|
445
|
+
if (!("ResizeObserver" in this.#window)) return () => void 0;
|
|
446
|
+
const resizeObserver = this.#resizeObserver ?? new this.#window.ResizeObserver(() => this.#measure());
|
|
447
|
+
this.#resizeObserver = resizeObserver;
|
|
448
|
+
this.#places.push(element);
|
|
449
|
+
resizeObserver.observe(element);
|
|
450
|
+
this.#measure();
|
|
451
|
+
return () => {
|
|
452
|
+
const index = this.#places.indexOf(element);
|
|
453
|
+
if (index === -1) return;
|
|
454
|
+
this.#places.splice(index, 1);
|
|
455
|
+
this.#resizeObserver?.unobserve(element);
|
|
456
|
+
this.#measure();
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
#measure() {
|
|
460
|
+
const place = this.#places.at(-1);
|
|
461
|
+
this.#placeWidth.set(place ? place.getBoundingClientRect().width : null);
|
|
462
|
+
}
|
|
430
463
|
#match(queries) {
|
|
431
464
|
return this.#observer.observe(queries).pipe(map((state) => state.matches));
|
|
432
465
|
}
|
|
@@ -2462,6 +2495,6 @@ const iconsName = [
|
|
|
2462
2495
|
"window-maximize",
|
|
2463
2496
|
"window-minimize"
|
|
2464
2497
|
];
|
|
2465
|
-
export { BreakpointsService, ERtDataTableCellColor, ERtDataTableColumnType, ERtDataTableFilterType, ERtMarkdownBlock, ERtMarkdownInline, ERtStorageKeys, MessageBus, NotificationBus, RT_CONSUMER_HANDLE_NAMES, RT_DATA_TABLE_PRESET_FROM_HOST, RT_DESIGN_TOKEN_NAMES, RT_KIT_CONFIG, RT_KIT_LABELS, RT_KIT_LABELS_EN, RT_KIT_LOCALE, RT_KIT_TRANSLATOR, RT_MATERIAL_PRESET_CLASS, RT_MATERIAL_PRESET_SELECTOR, RT_PRESET_MATERIAL_CLASS, RT_RADIUS_DEFAULT, RT_RADIUS_STEPS, RT_THEME_SCOPE_ATTRIBUTE, RtDayStampPipe, RtInfiniteScrollDirective, RtKitLabelPipe, RtThemeScopeDirective, SEARCH_DEBOUNCE_MS, ThemeService, breakNode, carryThemeScope, carryThemeScopeOfFocus, coerceQuillDeltaOutput, fileNameFromUrl, formatFileSize, iconsName, markdownToPlainText, materialPresetClassesOf, materialPresetClassesOfFocus, normalizeQuillDelta, parseInline, parseMarkdown, provideRtKit, provideRtKitLabels, rtKitDefault, rtKitLabel, searchDebounce, themeScopeOf, themeScopeOfFocus };
|
|
2498
|
+
export { BreakpointsService, ERtDataTableCellColor, ERtDataTableColumnType, ERtDataTableFilterType, ERtMarkdownBlock, ERtMarkdownInline, ERtStorageKeys, MessageBus, NotificationBus, RT_CONSUMER_HANDLE_NAMES, RT_DATA_TABLE_PRESET_FROM_HOST, RT_DESIGN_TOKEN_NAMES, RT_KIT_CONFIG, RT_KIT_LABELS, RT_KIT_LABELS_EN, RT_KIT_LOCALE, RT_KIT_TRANSLATOR, RT_MATERIAL_PRESET_CLASS, RT_MATERIAL_PRESET_SELECTOR, RT_NARROW_WIDTH, RT_PRESET_MATERIAL_CLASS, RT_RADIUS_DEFAULT, RT_RADIUS_STEPS, RT_THEME_SCOPE_ATTRIBUTE, RtDayStampPipe, RtInfiniteScrollDirective, RtKitLabelPipe, RtThemeScopeDirective, SEARCH_DEBOUNCE_MS, ThemeService, breakNode, carryThemeScope, carryThemeScopeOfFocus, coerceQuillDeltaOutput, fileNameFromUrl, formatFileSize, iconsName, markdownToPlainText, materialPresetClassesOf, materialPresetClassesOfFocus, normalizeQuillDelta, parseInline, parseMarkdown, provideRtKit, provideRtKitLabels, rtKitDefault, rtKitLabel, rtNarrowByWidth, searchDebounce, themeScopeOf, themeScopeOfFocus };
|
|
2466
2499
|
|
|
2467
2500
|
//# sourceMappingURL=rt-tools-ui-kit-v2-core.mjs.map
|