@rt-tools/ui-kit-v2 0.29.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +23 -29
  2. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +2 -2
  4. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +223 -3
  6. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +2 -2
  8. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +1 -2
  10. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +2 -2
  12. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +277 -904
  14. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +2 -2
  16. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +1 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +35 -48
  20. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +2 -2
  22. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs +2 -2
  23. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs.map +1 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +4 -0
  25. package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +4 -4
  27. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +37 -181
  29. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  30. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +5 -36
  31. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  32. package/fesm2022/rt-tools-ui-kit-v2-table.mjs +4 -1
  33. package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +2 -3
  35. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  36. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +2 -2
  37. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
  38. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +8 -13
  39. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  40. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +12 -7
  41. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  42. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +2 -2
  43. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
  44. package/package.json +1 -1
  45. package/src/lib/components/dynamic-selector/stories/component/test-dynamic-selector-popup-frame.component.scss +6 -0
  46. package/src/lib/components/page-header/rt-page-header.component.scss +4 -2
  47. package/src/lib/components/popover/CONTEXT.md +2 -1
  48. package/src/lib/components/tree/CONTEXT.md +1 -1
  49. package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +1 -1
  50. package/src/styles/_breakpoints.scss +37 -4
  51. package/src/styles/_index.scss +0 -4
  52. package/styles/tokens.css +0 -8
  53. package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +4 -3
  54. package/types/rt-tools-ui-kit-v2-core.d.ts +155 -1
  55. package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +51 -269
  56. package/types/rt-tools-ui-kit-v2-multiselect.d.ts +4 -3
  57. package/types/rt-tools-ui-kit-v2-select.d.ts +12 -74
  58. package/types/rt-tools-ui-kit-v2-side-menu.d.ts +5 -20
  59. package/types/rt-tools-ui-kit-v2-table.d.ts +4 -0
  60. package/types/rt-tools-ui-kit-v2-tag.d.ts +2 -3
  61. package/types/rt-tools-ui-kit-v2-tree-selector.d.ts +0 -2
  62. package/types/rt-tools-ui-kit-v2-tree.d.ts +7 -9
  63. package/src/styles/_place.scss +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-pdf-viewer.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.logic.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.output.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.tokens.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.component.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.component.html"],"sourcesContent":["import { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Чистая логика просмотрщика документа: шаги масштаба, начальный масштаб, номер видимой страницы и\n * то, какие страницы стоит держать нарисованными.\n */\n\n/** Ступени масштаба в процентах. Кнопки «−» и «+» ходят только по ним. */\nexport const RT_PDF_ZOOM_STEPS: readonly number[] = [50, 75, 100, 125, 150, 200, 300];\n\nexport const RT_PDF_ZOOM_MIN: number = RT_PDF_ZOOM_STEPS[0];\n\nexport const RT_PDF_ZOOM_MAX: number = RT_PDF_ZOOM_STEPS[RT_PDF_ZOOM_STEPS.length - 1];\n\n/**\n * Сколько CSS-пикселей в пункте документа. Пункт — 1/72 дюйма, CSS-пиксель — 1/96: при 100%\n * страница A4 на экране той же ширины, что на бумаге.\n */\nexport const RT_PDF_CSS_UNITS: number = 96 / 72;\n\n/** Ширина окна, до которой документ открывается «по ширине» при любом размере страницы. */\nexport const RT_PDF_NARROW_WIDTH: number = 768;\n\n/** Запас на дробную часть: 99.999% от деления ширин — это всё ещё 100%. */\nconst EPSILON: number = 0.01;\n\n/**\n * Следующая ступень масштаба. Шаг идёт от того, что на экране сейчас: из «по ширине» в 83% кнопка\n * «+» ведёт на 100%, а «−» — на 75%, а не на ступень, соседнюю с последней выбранной руками.\n */\nexport function nextZoomStep(current: number, direction: 1 | -1): number {\n if (direction > 0) {\n return RT_PDF_ZOOM_STEPS.find((step: number): boolean => step > current + EPSILON) ?? RT_PDF_ZOOM_MAX;\n }\n\n return [...RT_PDF_ZOOM_STEPS].reverse().find((step: number): boolean => step < current - EPSILON) ?? RT_PDF_ZOOM_MIN;\n}\n\n/** Есть ли куда увеличивать: на верхней границе кнопка «+» выключена. */\nexport function canZoomIn(current: number): boolean {\n return current < RT_PDF_ZOOM_MAX - EPSILON;\n}\n\n/** Есть ли куда уменьшать: на нижней границе кнопка «−» выключена. */\nexport function canZoomOut(current: number): boolean {\n return current > RT_PDF_ZOOM_MIN + EPSILON;\n}\n\n/**\n * Масштаб «по ширине» в процентах: самая широкая страница занимает всё место столбца.\n *\n * Границами ступеней он не зажат. На телефоне страница A4 «по ширине» — около 45%, и зажать её до\n * 50% значило бы вернуть горизонтальную прокрутку, ради которой режим и существует.\n */\nexport function fitWidthPercent(availableWidth: number, pages: readonly IRtPdfViewer.PageSize[]): number {\n const widest: number = Math.max(0, ...pages.map((page: IRtPdfViewer.PageSize): number => page.width));\n if (widest <= 0 || availableWidth <= 0) {\n return 100;\n }\n\n return (availableWidth / (widest * RT_PDF_CSS_UNITS)) * 100;\n}\n\n/**\n * С какого масштаба открыть документ. Узкий экран и страница, которая в 100% не влезает, —\n * «по ширине»: иначе человек сначала видит обрезанный край листа. Остальные — в 100%.\n */\nexport function initialZoom(viewportWidth: number, availableWidth: number, pages: readonly IRtPdfViewer.PageSize[]): IRtPdfViewer.Zoom {\n const widest: number = Math.max(0, ...pages.map((page: IRtPdfViewer.PageSize): number => page.width));\n if (viewportWidth <= RT_PDF_NARROW_WIDTH || widest * RT_PDF_CSS_UNITS > availableWidth) {\n return { kind: 'fit' };\n }\n\n return { kind: 'percent', percent: 100 };\n}\n\n/** Места под страницы на экране: от размера в пунктах и масштаба, в целых пикселях. */\nexport function pageBoxes(pages: readonly IRtPdfViewer.PageSize[], percent: number): IRtPdfViewer.PageBox[] {\n const scale: number = (percent / 100) * RT_PDF_CSS_UNITS;\n\n return pages.map((page: IRtPdfViewer.PageSize, index: number): IRtPdfViewer.PageBox => ({\n index,\n width: Math.floor(page.width * scale),\n height: Math.floor(page.height * scale),\n }));\n}\n\n/**\n * Номер страницы, которая видна сейчас, — с нуля. Это та, что занимает больше всего высоты окна;\n * при прокрутке до конца — последняя, даже если она короче соседки: иначе счётчик никогда не\n * показал бы «M / M» у документа с маленькой последней страницей. Документ, который виден целиком\n * без прокрутки, стоит на первой: до конца его никто не прокручивал.\n */\nexport function visiblePageIndex(tops: readonly number[], heights: readonly number[], scrollTop: number, viewportHeight: number): number {\n const total: number = Math.min(tops.length, heights.length);\n if (total === 0) {\n return 0;\n }\n\n const contentBottom: number = tops[total - 1] + heights[total - 1];\n if (scrollTop > 0 && scrollTop + viewportHeight >= contentBottom - 1) {\n return total - 1;\n }\n\n let best: number = 0;\n let bestArea: number = -1;\n for (let index: number = 0; index < total; index++) {\n const visible: number = Math.min(tops[index] + heights[index], scrollTop + viewportHeight) - Math.max(tops[index], scrollTop);\n if (visible > bestArea) {\n best = index;\n bestArea = visible;\n }\n }\n\n return best;\n}\n\n/**\n * Страницы, которые стоит нарисовать: видимые и по `overscan` соседних с каждой стороны. Документ\n * в сотню страниц разом не рисуется — каждая страница это холст в мегабайты памяти.\n */\nexport function pagesToRender(\n tops: readonly number[],\n heights: readonly number[],\n scrollTop: number,\n viewportHeight: number,\n overscan: number = 1\n): number[] {\n const total: number = Math.min(tops.length, heights.length);\n const visible: number[] = [];\n for (let index: number = 0; index < total; index++) {\n if (tops[index] < scrollTop + viewportHeight && tops[index] + heights[index] > scrollTop) {\n visible.push(index);\n }\n }\n if (visible.length === 0) {\n return [];\n }\n\n const first: number = Math.max(0, visible[0] - overscan);\n const last: number = Math.min(total - 1, visible[visible.length - 1] + overscan);\n\n return Array.from({ length: last - first + 1 }, (_: unknown, offset: number): number => first + offset);\n}\n\n/**\n * Куда прокрутить после смены масштаба, чтобы на экране осталось то же место той же страницы.\n * Без этого увеличение уводило бы человека на несколько страниц вниз от того, что он читал.\n */\nexport function anchoredScrollTop(oldTop: number, oldHeight: number, oldScrollTop: number, newTop: number, newHeight: number): number {\n if (oldHeight <= 0) {\n return newTop;\n }\n const ratio: number = (oldScrollTop - oldTop) / oldHeight;\n\n return Math.max(0, newTop + ratio * newHeight);\n}\n\n/** Подпись масштаба для плашки в процентах: «83%», а не «83.47%». */\nexport function zoomPercentLabel(percent: number): string {\n return `${Math.round(percent)}%`;\n}\n","import { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Печать и скачивание документа — то, что уходит за пределы окна просмотра.\n */\n\n/** Блок печатного листа. Лист лежит в конце `<body>`, вне просмотрщика, и класс у него свой. */\nexport const RT_PDF_PRINT_BLOCK: string = 'rt-pdf-viewer-print';\n\n/** Признак на корне страницы, пока идёт печать: под ним прячется всё, кроме листа. */\nexport const RT_PDF_PRINTING_FLAG: string = 'rt-pdf-viewer-printing';\n\n/** Плотность печати: 150 точек на дюйм — текст чёткий, а лист в сотню страниц не выедает память. */\nconst PRINT_DPI: number = 150;\n\n/** Байты документа в том виде, в каком их принимает `Blob`. */\nfunction blobOf(bytes: Uint8Array | ArrayBuffer): Blob {\n const copy: Uint8Array<ArrayBuffer> = bytes instanceof ArrayBuffer ? new Uint8Array(bytes.slice(0)) : new Uint8Array(bytes);\n\n return new Blob([copy], { type: 'application/pdf' });\n}\n\n/**\n * Сохраняет файл под его именем. Байты уходят ссылкой на `Blob`: так браузер берёт имя из\n * `download`. Адрес без байтов отдаётся ссылкой как есть — чужой домен имя может и не принять, но\n * файл человек получит.\n */\nexport function downloadPdf(\n doc: Document,\n win: Window & typeof globalThis,\n file: Uint8Array | ArrayBuffer | string,\n fileName: string\n): void {\n const anchor: HTMLAnchorElement = doc.createElement('a');\n const ownUrl: string | null = typeof file === 'string' ? null : win.URL.createObjectURL(blobOf(file));\n\n anchor.href = ownUrl ?? (typeof file === 'string' ? file : '');\n anchor.download = fileName;\n anchor.rel = 'noopener';\n anchor.style.display = 'none';\n doc.body.append(anchor);\n anchor.click();\n anchor.remove();\n\n if (ownUrl) {\n // Отзыв сразу после щелчка обрывает скачивание в части браузеров: ссылка живёт ещё ход.\n win.setTimeout((): void => win.URL.revokeObjectURL(ownUrl), 0);\n }\n}\n\nfunction canvasBlob(canvas: HTMLCanvasElement): Promise<Blob> {\n return new Promise<Blob>((resolve: (blob: Blob) => void, reject: (reason: Error) => void): void => {\n canvas.toBlob((blob: Blob | null): void => (blob ? resolve(blob) : reject(new Error('canvas is empty'))), 'image/png');\n });\n}\n\n/**\n * Готовит печатный лист: каждая страница рисуется в картинку и встаёт в разметку в конце `<body>`.\n *\n * Лист собирается из картинок, а не из `<iframe>` с самим файлом: встроенного показа PDF нет у\n * мобильных браузеров, а `print()` у кадра с PDF в части настольных печатает пустую страницу или\n * только первую. Картинки печатает любой браузер.\n */\nexport async function preparePrintSheet(\n pdf: IRtPdfViewer.PdfDocument,\n doc: Document,\n win: Window & typeof globalThis\n): Promise<IRtPdfViewer.PrintSheet> {\n const sheet: HTMLDivElement = doc.createElement('div');\n const urls: string[] = [];\n const images: HTMLImageElement[] = [];\n sheet.classList.add(RT_PDF_PRINT_BLOCK);\n\n const dispose: () => void = (): void => {\n doc.documentElement.classList.remove(RT_PDF_PRINTING_FLAG);\n sheet.remove();\n for (const url of urls) {\n win.URL.revokeObjectURL(url);\n }\n };\n\n try {\n for (let pageNumber: number = 1; pageNumber <= pdf.numPages; pageNumber++) {\n const page: IRtPdfViewer.PdfPage = await pdf.getPage(pageNumber);\n const viewport: IRtPdfViewer.Viewport = page.getViewport({ scale: PRINT_DPI / 72 });\n const canvas: HTMLCanvasElement = doc.createElement('canvas');\n canvas.width = Math.floor(viewport.width);\n canvas.height = Math.floor(viewport.height);\n await page.render({ canvas, viewport, intent: 'print' }).promise;\n\n const url: string = win.URL.createObjectURL(await canvasBlob(canvas));\n urls.push(url);\n // Холст печатной плотности весит десятки мегабайт: память отдаётся сразу, а не по уборке\n canvas.width = 0;\n canvas.height = 0;\n\n const image: HTMLImageElement = doc.createElement('img');\n image.classList.add(`${RT_PDF_PRINT_BLOCK}__page`);\n image.alt = '';\n image.src = url;\n images.push(image);\n sheet.append(image);\n }\n\n doc.body.append(sheet);\n // Печать до раскодирования картинок даёт пустые листы\n await Promise.all(images.map((image: HTMLImageElement): Promise<void> => image.decode().catch((): void => undefined)));\n doc.documentElement.classList.add(RT_PDF_PRINTING_FLAG);\n } catch (error: unknown) {\n dispose();\n throw error;\n }\n\n return { dispose };\n}\n","import { InjectionToken, Provider } from '@angular/core';\n\nimport { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Адрес воркера по умолчанию. Относительный: считается от `<base href>`, поэтому приложение под\n * своим префиксом не правит его руками. Файл туда кладёт сборка приложения — см. обзор компонента.\n */\nexport const RT_PDF_VIEWER_WORKER_SRC: string = 'assets/pdfjs/pdf.worker.min.mjs';\n\n/**\n * Рисовальщик грузится только при открытии окна. Статический импорт положил бы `pdfjs-dist` в\n * бандл каждому, кто импортировал вход, даже если окно так и не открылось.\n */\nfunction loadPdfJs(): Promise<IRtPdfViewer.PdfJs> {\n return import('pdfjs-dist');\n}\n\n/** Настройки рисовальщика просмотрщика. Без провайдера — умолчания выше. */\nexport const RT_PDF_VIEWER_CONFIG: InjectionToken<IRtPdfViewer.Config> = new InjectionToken<IRtPdfViewer.Config>('RT_PDF_VIEWER_CONFIG', {\n providedIn: 'root',\n factory: (): IRtPdfViewer.Config => ({ workerSrc: RT_PDF_VIEWER_WORKER_SRC, load: loadPdfJs }),\n});\n\n/**\n * Настраивает просмотрщик: адрес воркера и каталоги данных `pdfjs-dist`. Незаданное берётся из\n * умолчаний.\n *\n * ```typescript\n * export const appConfig: ApplicationConfig = {\n * providers: [provideRtPdfViewer({ workerSrc: 'static/pdf.worker.min.mjs' })],\n * };\n * ```\n */\nexport function provideRtPdfViewer(config: Partial<IRtPdfViewer.Config>): Provider {\n return {\n provide: RT_PDF_VIEWER_CONFIG,\n useValue: { workerSrc: RT_PDF_VIEWER_WORKER_SRC, load: loadPdfJs, ...config } satisfies IRtPdfViewer.Config,\n };\n}\n","import { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { DOCUMENT } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n inject,\n signal,\n viewChild,\n viewChildren,\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective, WINDOW } from '@rt-tools/core';\n\nimport { rtKitLabel, RT_KIT_LABELS, TRtKitLabelMap, TRtKitLabelParams } from '@rt-tools/ui-kit-v2/core';\nimport { RtButtonDirective } from '@rt-tools/ui-kit-v2/button';\nimport { RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';\nimport { RT_DIALOG_DATA } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtEmptyStateComponent } from '@rt-tools/ui-kit-v2/empty-state';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtSpinnerComponent } from '@rt-tools/ui-kit-v2/spinner';\nimport {\n anchoredScrollTop,\n canZoomIn,\n canZoomOut,\n fitWidthPercent,\n initialZoom,\n nextZoomStep,\n pageBoxes,\n pagesToRender,\n visiblePageIndex,\n zoomPercentLabel,\n} from './rt-pdf-viewer.logic';\nimport { IRtPdfViewer } from './rt-pdf-viewer.model';\nimport { downloadPdf, preparePrintSheet } from './rt-pdf-viewer.output';\nimport { RT_PDF_VIEWER_CONFIG } from './rt-pdf-viewer.tokens';\n\nconst BEM_BLOCK: string = 'rt-pdf-viewer';\n\n/** Сколько страниц сверх нарисованных держать холстами: дальше холст очищается ради памяти. */\nconst KEEP_OVERSCAN: number = 3;\n\n/** Нарисованная страница: холст и масштаб, в котором он нарисован. */\ninterface IRenderedPage {\n readonly percent: number;\n readonly task: IRtPdfViewer.RenderTask;\n}\n\n/**\n * Полноэкранный просмотр документа PDF поверх `RtDialogService`.\n *\n * Страницы рисует `pdfjs-dist` — необязательная зависимость кита. Он грузится динамическим импортом\n * только при открытии окна, поэтому вход `@rt-tools/ui-kit-v2/pdf-viewer` не утяжеляет бандл, пока\n * окно не открыто. Рисуются только видимые страницы и их соседи, с плотностью точек экрана.\n *\n * `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог; фокус после закрытия\n * просмотрщик возвращает туда, откуда его открыли.\n */\n@Component({\n selector: 'rt-pdf-viewer',\n templateUrl: './rt-pdf-viewer.component.html',\n styleUrl: './rt-pdf-viewer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n CdkTrapFocus,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtButtonDirective,\n RtEmptyStateComponent,\n RtIconButtonComponent,\n RtIconComponent,\n RtSpinnerComponent,\n\n // transloco\n ],\n host: {\n class: BEM_BLOCK,\n role: 'dialog',\n 'aria-modal': 'true',\n '[attr.aria-label]': 'fileName',\n // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него сразу после открытия\n tabindex: '-1',\n '(keydown.escape)': 'onEscape($event)',\n '(window:resize)': 'onResize()',\n },\n})\nexport class RtPdfViewerComponent {\n readonly #data: IRtPdfViewer.Data = inject(RT_DIALOG_DATA) as IRtPdfViewer.Data;\n readonly #dialogRef: RtDialogRef<void> = inject(RtDialogRef<void>);\n readonly #config: IRtPdfViewer.Config = inject(RT_PDF_VIEWER_CONFIG);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef<HTMLElement>);\n readonly #document: Document = inject(DOCUMENT);\n readonly #window: Window & typeof globalThis = inject(WINDOW) as Window & typeof globalThis;\n readonly #injector: Injector = inject(Injector);\n\n /** Откуда открыли: туда возвращается фокус после закрытия */\n readonly #opener: HTMLElement | null = this.#focusedElement();\n\n readonly #availableWidth: WritableSignal<number> = signal<number>(0);\n readonly #pageSizes: WritableSignal<readonly IRtPdfViewer.PageSize[]> = signal<readonly IRtPdfViewer.PageSize[]>([]);\n readonly #pageOfParams: Signal<TRtKitLabelParams> = computed((): TRtKitLabelParams => ({\n page: this.currentPage() + 1,\n total: this.#pageSizes().length,\n }));\n\n #loadingTask: IRtPdfViewer.LoadingTask | null = null;\n #pdf: IRtPdfViewer.PdfDocument | null = null;\n #pages: Map<number, Promise<IRtPdfViewer.PdfPage>> = new Map<number, Promise<IRtPdfViewer.PdfPage>>();\n #rendered: Map<number, IRenderedPage> = new Map<number, IRenderedPage>();\n #printSheet: IRtPdfViewer.PrintSheet | null = null;\n #destroyed: boolean = false;\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly scroller: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('scroller');\n protected readonly column: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('column');\n protected readonly pageNodes: Signal<readonly ElementRef<HTMLElement>[]> = viewChildren<ElementRef<HTMLElement>>('page');\n protected readonly canvases: Signal<readonly ElementRef<HTMLCanvasElement>[]> = viewChildren<ElementRef<HTMLCanvasElement>>('canvas');\n\n protected readonly fileName: string = this.#data.fileName;\n\n protected readonly status: WritableSignal<IRtPdfViewer.Status> = signal<IRtPdfViewer.Status>('loading');\n\n protected readonly printing: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly zoom: WritableSignal<IRtPdfViewer.Zoom> = signal<IRtPdfViewer.Zoom>({ kind: 'percent', percent: 100 });\n\n /** Масштаб на экране в процентах — и для «по ширине», который хранится режимом */\n protected readonly percent: Signal<number> = computed((): number => {\n const zoom: IRtPdfViewer.Zoom = this.zoom();\n\n return zoom.kind === 'fit' ? fitWidthPercent(this.#availableWidth(), this.#pageSizes()) : zoom.percent;\n });\n\n protected readonly isFit: Signal<boolean> = computed((): boolean => this.zoom().kind === 'fit');\n\n protected readonly zoomLabel: Signal<string> = computed((): string =>\n this.isFit() ? this.t().pdfViewerFitWidth : zoomPercentLabel(this.percent())\n );\n\n protected readonly canZoomIn: Signal<boolean> = computed((): boolean => canZoomIn(this.percent()));\n\n protected readonly canZoomOut: Signal<boolean> = computed((): boolean => canZoomOut(this.percent()));\n\n protected readonly boxes: Signal<IRtPdfViewer.PageBox[]> = computed((): IRtPdfViewer.PageBox[] =>\n pageBoxes(this.#pageSizes(), this.percent())\n );\n\n /** Видимая сейчас страница, с нуля */\n protected readonly currentPage: WritableSignal<number> = signal<number>(0);\n\n protected readonly counter: Signal<string> = computed((): string => `${this.currentPage() + 1} / ${this.#pageSizes().length}`);\n\n protected readonly pageOfLabel: Signal<string> = rtKitLabel('pdfViewerPageOf', this.#pageOfParams);\n\n constructor() {\n inject(DestroyRef).onDestroy((): void => this.#teardown());\n\n afterNextRender((): void => {\n this.#host.nativeElement.focus();\n void this.#open();\n });\n }\n\n public close(): void {\n this.#dialogRef.close();\n }\n\n /** Esc закрывает только окно: окно под ним, например боковая панель, остаётся открытым */\n protected onEscape(event: Event): void {\n event.stopPropagation();\n this.close();\n }\n\n protected zoomIn(): void {\n this.#setZoom({ kind: 'percent', percent: nextZoomStep(this.percent(), 1) });\n }\n\n protected zoomOut(): void {\n this.#setZoom({ kind: 'percent', percent: nextZoomStep(this.percent(), -1) });\n }\n\n protected fitWidth(): void {\n this.#measure();\n this.#setZoom({ kind: 'fit' });\n }\n\n protected onScroll(): void {\n this.#renderVisible();\n }\n\n protected onResize(): void {\n if (this.status() !== 'ready') {\n return;\n }\n this.#measure();\n if (this.isFit()) {\n this.#afterLayout((): void => this.#renderVisible());\n }\n }\n\n /** Байты уходят как пришли, а адрес — байтами документа, если он уже загружен */\n protected async download(): Promise<void> {\n const source: IRtPdfViewer.Source = this.#data.source;\n if (typeof source !== 'string') {\n downloadPdf(this.#document, this.#window, source, this.fileName);\n return;\n }\n const bytes: Uint8Array | null = this.#pdf ? await this.#pdf.getData().catch((): null => null) : null;\n downloadPdf(this.#document, this.#window, bytes ?? source, this.fileName);\n }\n\n /**\n * Печать документа целиком, без полосы и плашки: страницы рисуются картинками в отдельный лист,\n * и на время печати страница прячет всё, кроме него. Лист убирается после печати.\n */\n protected async print(): Promise<void> {\n const pdf: IRtPdfViewer.PdfDocument | null = this.#pdf;\n if (!pdf || this.printing()) {\n return;\n }\n this.printing.set(true);\n try {\n this.#printSheet?.dispose();\n const sheet: IRtPdfViewer.PrintSheet = await preparePrintSheet(pdf, this.#document, this.#window);\n if (this.#destroyed) {\n sheet.dispose();\n return;\n }\n this.#printSheet = sheet;\n this.#window.addEventListener(\n 'afterprint',\n (): void => {\n sheet.dispose();\n if (this.#printSheet === sheet) {\n this.#printSheet = null;\n }\n },\n { once: true }\n );\n this.#window.print();\n } catch {\n this.#printSheet?.dispose();\n this.#printSheet = null;\n } finally {\n this.printing.set(false);\n }\n }\n\n async #open(): Promise<void> {\n try {\n const pdfjs: IRtPdfViewer.PdfJs = await this.#config.load();\n if (this.#destroyed) {\n return;\n }\n pdfjs.GlobalWorkerOptions.workerSrc = this.#config.workerSrc;\n this.#loadingTask = pdfjs.getDocument(this.#documentParams());\n const pdf: IRtPdfViewer.PdfDocument = await this.#loadingTask.promise;\n if (this.#destroyed) {\n return;\n }\n this.#pdf = pdf;\n\n // Размеры всех страниц нужны сразу: без них нет высоты столбца, и прокрутка прыгала бы\n // по мере того, как страницы рисуются\n const sizes: IRtPdfViewer.PageSize[] = await Promise.all(\n Array.from({ length: pdf.numPages }, async (_: unknown, index: number): Promise<IRtPdfViewer.PageSize> => {\n const viewport: IRtPdfViewer.Viewport = (await this.#page(index)).getViewport({ scale: 1 });\n return { width: viewport.width, height: viewport.height };\n })\n );\n if (this.#destroyed) {\n return;\n }\n // Масштаб выбирается до показа страниц: иначе на телефоне на кадр мелькал бы обрезанный\n // лист в 100%, и только потом — «по ширине»\n this.#measure();\n this.zoom.set(initialZoom(this.#host.nativeElement.clientWidth, this.#availableWidth(), sizes));\n this.#pageSizes.set(sizes);\n this.status.set('ready');\n this.#afterLayout((): void => this.#renderVisible());\n } catch {\n if (!this.#destroyed) {\n this.status.set('error');\n }\n }\n }\n\n #documentParams(): IRtPdfViewer.DocumentParams {\n const source: IRtPdfViewer.Source = this.#data.source;\n const data: Pick<IRtPdfViewer.DocumentParams, 'url' | 'data'> =\n typeof source === 'string'\n ? { url: source }\n : // Рисовальщик забирает буфер себе в поток — отдаётся копия, оригинал нужен для скачивания\n { data: source instanceof ArrayBuffer ? new Uint8Array(source.slice(0)) : source.slice() };\n\n return {\n ...data,\n ...(this.#config.cMapUrl ? { cMapUrl: this.#config.cMapUrl, cMapPacked: true } : {}),\n ...(this.#config.standardFontDataUrl ? { standardFontDataUrl: this.#config.standardFontDataUrl } : {}),\n ...(this.#config.wasmUrl ? { wasmUrl: this.#config.wasmUrl } : {}),\n };\n }\n\n #page(index: number): Promise<IRtPdfViewer.PdfPage> {\n const pdf: IRtPdfViewer.PdfDocument | null = this.#pdf;\n if (!pdf) {\n return Promise.reject(new Error('document is not loaded'));\n }\n let page: Promise<IRtPdfViewer.PdfPage> | undefined = this.#pages.get(index);\n if (!page) {\n page = pdf.getPage(index + 1);\n this.#pages.set(index, page);\n }\n\n return page;\n }\n\n /**\n * Место, в которое страница встаёт «по ширине»: ширина окна прокрутки без полей столбца. Сам\n * столбец не меряется — увеличенная страница растягивает его шире окна.\n */\n #measure(): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const column: HTMLElement | undefined = this.column()?.nativeElement;\n if (!scroller || !column) {\n return;\n }\n const style: CSSStyleDeclaration = this.#window.getComputedStyle(column);\n const padding: number = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);\n this.#availableWidth.set(Math.max(0, scroller.clientWidth - padding));\n }\n\n /**\n * Смена масштаба держит на экране то же место той же страницы: после перестройки столбца\n * прокрутка ставится от новой высоты страницы, а не остаётся на прежнем числе пикселей.\n */\n #setZoom(zoom: IRtPdfViewer.Zoom): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const current: number = this.currentPage();\n const node: HTMLElement | undefined = this.pageNodes()[current]?.nativeElement;\n const before: { top: number; height: number; scrollTop: number } | null =\n scroller && node ? { top: node.offsetTop, height: node.offsetHeight, scrollTop: scroller.scrollTop } : null;\n\n this.zoom.set(zoom);\n this.#afterLayout((): void => {\n const after: HTMLElement | undefined = this.pageNodes()[current]?.nativeElement;\n if (scroller && after && before) {\n scroller.scrollTop = anchoredScrollTop(before.top, before.height, before.scrollTop, after.offsetTop, after.offsetHeight);\n }\n this.#renderVisible();\n });\n }\n\n #afterLayout(callback: () => void): void {\n afterNextRender(callback, { injector: this.#injector });\n }\n\n /** Счётчик догоняет прокрутку, а видимые страницы и их соседи дорисовываются */\n #renderVisible(): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const nodes: readonly ElementRef<HTMLElement>[] = this.pageNodes();\n if (!scroller || nodes.length === 0 || this.#destroyed) {\n return;\n }\n const tops: number[] = nodes.map((node: ElementRef<HTMLElement>): number => node.nativeElement.offsetTop);\n const heights: number[] = nodes.map((node: ElementRef<HTMLElement>): number => node.nativeElement.offsetHeight);\n const { scrollTop, clientHeight } = scroller;\n\n this.currentPage.set(visiblePageIndex(tops, heights, scrollTop, clientHeight));\n\n const keep: Set<number> = new Set<number>(pagesToRender(tops, heights, scrollTop, clientHeight, KEEP_OVERSCAN));\n for (const index of [...this.#rendered.keys()]) {\n if (!keep.has(index)) {\n this.#clearPage(index);\n }\n }\n for (const index of pagesToRender(tops, heights, scrollTop, clientHeight)) {\n void this.#renderPage(index);\n }\n }\n\n async #renderPage(index: number): Promise<void> {\n const percent: number = this.percent();\n const existing: IRenderedPage | undefined = this.#rendered.get(index);\n if (existing?.percent === percent) {\n return;\n }\n existing?.task.cancel();\n this.#rendered.delete(index);\n\n const canvas: HTMLCanvasElement | undefined = this.canvases()[index]?.nativeElement;\n const box: IRtPdfViewer.PageBox | undefined = this.boxes()[index];\n if (!canvas || !box) {\n return;\n }\n try {\n const page: IRtPdfViewer.PdfPage = await this.#page(index);\n if (this.#destroyed || this.percent() !== percent || this.#rendered.has(index)) {\n return;\n }\n // Холст в точках экрана, а не в CSS-пикселях: иначе на плотном экране текст мылится\n const ratio: number = this.#window.devicePixelRatio || 1;\n const viewport: IRtPdfViewer.Viewport = page.getViewport({ scale: (box.width / page.getViewport({ scale: 1 }).width) * ratio });\n canvas.width = Math.floor(viewport.width);\n canvas.height = Math.floor(viewport.height);\n const task: IRtPdfViewer.RenderTask = page.render({ canvas, viewport });\n this.#rendered.set(index, { percent, task });\n await task.promise;\n } catch {\n // Отменённая отрисовка — обычное дело при прокрутке; страница дорисуется при подходе\n if (this.#rendered.get(index)?.percent === percent) {\n this.#rendered.delete(index);\n }\n }\n }\n\n #clearPage(index: number): void {\n this.#rendered.get(index)?.task.cancel();\n this.#rendered.delete(index);\n const canvas: HTMLCanvasElement | undefined = this.canvases()[index]?.nativeElement;\n if (canvas) {\n canvas.width = 0;\n canvas.height = 0;\n }\n }\n\n #teardown(): void {\n this.#destroyed = true;\n for (const rendered of this.#rendered.values()) {\n rendered.task.cancel();\n }\n this.#rendered.clear();\n this.#printSheet?.dispose();\n this.#printSheet = null;\n void this.#loadingTask?.destroy().catch((): void => undefined);\n this.#pdf = null;\n if (this.#opener?.isConnected) {\n this.#opener.focus();\n }\n }\n\n #focusedElement(): HTMLElement | null {\n const active: Element | null = this.#document.activeElement;\n\n return active instanceof HTMLElement && active !== this.#document.body ? active : null;\n }\n}\n","<ng-container rtBlock=\"rt-pdf-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому авто-захвата здесь нет -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <!-- Полоса видна сразу, ещё до загрузки документа: человек видит, что открыл, и может закрыть -->\n <header rtElem=\"bar\">\n <div rtElem=\"title\">\n <rt-icon rtElem=\"file-icon\" name=\"attach-pdf\" size=\"md\" />\n <span rtElem=\"file-name\" qa-dataid=\"pdf-viewer-file-name\" [attr.title]=\"fileName\">{{ fileName }}</span>\n </div>\n\n <div rtElem=\"actions\">\n <rt-icon-button\n qa-dataid=\"pdf-viewer-print\"\n icon=\"print\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerPrint\"\n [tooltip]=\"t().pdfViewerPrint\"\n [disabled]=\"status() !== 'ready'\"\n [loading]=\"printing()\"\n (clicked)=\"print()\" />\n <rt-icon-button\n qa-dataid=\"pdf-viewer-download\"\n icon=\"ico-download\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerDownload\"\n [tooltip]=\"t().pdfViewerDownload\"\n (clicked)=\"download()\" />\n <rt-icon-button\n qa-dataid=\"pdf-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerClose\"\n [tooltip]=\"t().pdfViewerClose\"\n (clicked)=\"close()\" />\n </div>\n </header>\n\n <div rtElem=\"scroller\" qa-dataid=\"pdf-viewer-pages\" #scroller (scroll)=\"onScroll()\">\n <!-- Столбец стоит и до загрузки: по его ширине выбирается начальный масштаб -->\n <div rtElem=\"column\" #column>\n @if (status() === 'ready') {\n @for (box of boxes(); track box.index) {\n <div\n rtElem=\"page\"\n qa-dataid=\"pdf-viewer-page\"\n #page\n [attr.data-page-index]=\"box.index\"\n [style.width.px]=\"box.width\"\n [style.height.px]=\"box.height\">\n <canvas rtElem=\"canvas\" #canvas></canvas>\n </div>\n }\n }\n </div>\n </div>\n\n @switch (status()) {\n @case ('loading') {\n <div rtElem=\"center\">\n <rt-spinner qa-dataid=\"pdf-viewer-spinner\" color=\"on-primary\" [diameter]=\"40\" />\n </div>\n }\n\n @case ('error') {\n <div rtElem=\"center\">\n <rt-empty-state\n rtElem=\"error\"\n qa-dataid=\"pdf-viewer-error\"\n [title]=\"t().pdfViewerErrorTitle\"\n [description]=\"t().pdfViewerErrorText\">\n <button\n rtButton\n rtElem=\"error-download\"\n qa-dataid=\"pdf-viewer-error-download\"\n type=\"button\"\n icon=\"ico-download\"\n [label]=\"t().pdfViewerDownload\"\n [attr.aria-label]=\"t().pdfViewerDownload\"\n (click)=\"download()\"></button>\n </rt-empty-state>\n </div>\n }\n\n @case ('ready') {\n <div rtElem=\"toolbar\" qa-dataid=\"pdf-viewer-toolbar\">\n <span rtElem=\"counter\" qa-dataid=\"pdf-viewer-counter\" role=\"status\" aria-live=\"off\" [attr.aria-label]=\"pageOfLabel()\">\n {{ counter() }}\n </span>\n <span rtElem=\"divider\" aria-hidden=\"true\"></span>\n <rt-icon-button\n qa-dataid=\"pdf-viewer-zoom-out\"\n icon=\"minus\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().pdfViewerZoomOut\"\n [disabled]=\"!canZoomOut()\"\n (clicked)=\"zoomOut()\" />\n <span rtElem=\"zoom\" qa-dataid=\"pdf-viewer-zoom\">{{ zoomLabel() }}</span>\n <rt-icon-button\n qa-dataid=\"pdf-viewer-zoom-in\"\n icon=\"ico-plus\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().pdfViewerZoomIn\"\n [disabled]=\"!canZoomIn()\"\n (clicked)=\"zoomIn()\" />\n <span rtElem=\"fit-divider\" aria-hidden=\"true\"></span>\n <button\n rtButton\n rtElem=\"fit\"\n qa-dataid=\"pdf-viewer-fit\"\n type=\"button\"\n appearance=\"text\"\n size=\"sm\"\n [label]=\"t().pdfViewerFitWidth\"\n [attr.aria-label]=\"t().pdfViewerFitWidth\"\n [pressed]=\"isFit()\"\n (click)=\"fitWidth()\"></button>\n </div>\n }\n }\n </div>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-pdf-viewer.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.logic.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.output.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.tokens.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.component.ts","../../../projects/ui-kit-v2/src/lib/components/pdf-viewer/rt-pdf-viewer.component.html"],"sourcesContent":["import { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Чистая логика просмотрщика документа: шаги масштаба, начальный масштаб, номер видимой страницы и\n * то, какие страницы стоит держать нарисованными.\n */\n\n/** Ступени масштаба в процентах. Кнопки «−» и «+» ходят только по ним. */\nexport const RT_PDF_ZOOM_STEPS: readonly number[] = [50, 75, 100, 125, 150, 200, 300];\n\nexport const RT_PDF_ZOOM_MIN: number = RT_PDF_ZOOM_STEPS[0];\n\nexport const RT_PDF_ZOOM_MAX: number = RT_PDF_ZOOM_STEPS[RT_PDF_ZOOM_STEPS.length - 1];\n\n/**\n * Сколько CSS-пикселей в пункте документа. Пункт — 1/72 дюйма, CSS-пиксель — 1/96: при 100%\n * страница A4 на экране той же ширины, что на бумаге.\n */\nexport const RT_PDF_CSS_UNITS: number = 96 / 72;\n\n/** Ширина окна, до которой документ открывается «по ширине» при любом размере страницы. */\nexport const RT_PDF_NARROW_WIDTH: number = 768;\n\n/** Запас на дробную часть: 99.999% от деления ширин — это всё ещё 100%. */\nconst EPSILON: number = 0.01;\n\n/**\n * Следующая ступень масштаба. Шаг идёт от того, что на экране сейчас: из «по ширине» в 83% кнопка\n * «+» ведёт на 100%, а «−» — на 75%, а не на ступень, соседнюю с последней выбранной руками.\n */\nexport function nextZoomStep(current: number, direction: 1 | -1): number {\n if (direction > 0) {\n return RT_PDF_ZOOM_STEPS.find((step: number): boolean => step > current + EPSILON) ?? RT_PDF_ZOOM_MAX;\n }\n\n return [...RT_PDF_ZOOM_STEPS].reverse().find((step: number): boolean => step < current - EPSILON) ?? RT_PDF_ZOOM_MIN;\n}\n\n/** Есть ли куда увеличивать: на верхней границе кнопка «+» выключена. */\nexport function canZoomIn(current: number): boolean {\n return current < RT_PDF_ZOOM_MAX - EPSILON;\n}\n\n/** Есть ли куда уменьшать: на нижней границе кнопка «−» выключена. */\nexport function canZoomOut(current: number): boolean {\n return current > RT_PDF_ZOOM_MIN + EPSILON;\n}\n\n/**\n * Масштаб «по ширине» в процентах: самая широкая страница занимает всё место столбца.\n *\n * Границами ступеней он не зажат. На телефоне страница A4 «по ширине» — около 45%, и зажать её до\n * 50% значило бы вернуть горизонтальную прокрутку, ради которой режим и существует.\n */\nexport function fitWidthPercent(availableWidth: number, pages: readonly IRtPdfViewer.PageSize[]): number {\n const widest: number = Math.max(0, ...pages.map((page: IRtPdfViewer.PageSize): number => page.width));\n if (widest <= 0 || availableWidth <= 0) {\n return 100;\n }\n\n return (availableWidth / (widest * RT_PDF_CSS_UNITS)) * 100;\n}\n\n/**\n * С какого масштаба открыть документ. Узкий экран и страница, которая в 100% не влезает, —\n * «по ширине»: иначе человек сначала видит обрезанный край листа. Остальные — в 100%.\n */\nexport function initialZoom(viewportWidth: number, availableWidth: number, pages: readonly IRtPdfViewer.PageSize[]): IRtPdfViewer.Zoom {\n const widest: number = Math.max(0, ...pages.map((page: IRtPdfViewer.PageSize): number => page.width));\n if (viewportWidth <= RT_PDF_NARROW_WIDTH || widest * RT_PDF_CSS_UNITS > availableWidth) {\n return { kind: 'fit' };\n }\n\n return { kind: 'percent', percent: 100 };\n}\n\n/** Места под страницы на экране: от размера в пунктах и масштаба, в целых пикселях. */\nexport function pageBoxes(pages: readonly IRtPdfViewer.PageSize[], percent: number): IRtPdfViewer.PageBox[] {\n const scale: number = (percent / 100) * RT_PDF_CSS_UNITS;\n\n return pages.map((page: IRtPdfViewer.PageSize, index: number): IRtPdfViewer.PageBox => ({\n index,\n width: Math.floor(page.width * scale),\n height: Math.floor(page.height * scale),\n }));\n}\n\n/**\n * Номер страницы, которая видна сейчас, — с нуля. Это та, что занимает больше всего высоты окна;\n * при прокрутке до конца — последняя, даже если она короче соседки: иначе счётчик никогда не\n * показал бы «M / M» у документа с маленькой последней страницей. Документ, который виден целиком\n * без прокрутки, стоит на первой: до конца его никто не прокручивал.\n */\nexport function visiblePageIndex(tops: readonly number[], heights: readonly number[], scrollTop: number, viewportHeight: number): number {\n const total: number = Math.min(tops.length, heights.length);\n if (total === 0) {\n return 0;\n }\n\n const contentBottom: number = tops[total - 1] + heights[total - 1];\n if (scrollTop > 0 && scrollTop + viewportHeight >= contentBottom - 1) {\n return total - 1;\n }\n\n let best: number = 0;\n let bestArea: number = -1;\n for (let index: number = 0; index < total; index++) {\n const visible: number = Math.min(tops[index] + heights[index], scrollTop + viewportHeight) - Math.max(tops[index], scrollTop);\n if (visible > bestArea) {\n best = index;\n bestArea = visible;\n }\n }\n\n return best;\n}\n\n/**\n * Страницы, которые стоит нарисовать: видимые и по `overscan` соседних с каждой стороны. Документ\n * в сотню страниц разом не рисуется — каждая страница это холст в мегабайты памяти.\n */\nexport function pagesToRender(\n tops: readonly number[],\n heights: readonly number[],\n scrollTop: number,\n viewportHeight: number,\n overscan: number = 1\n): number[] {\n const total: number = Math.min(tops.length, heights.length);\n const visible: number[] = [];\n for (let index: number = 0; index < total; index++) {\n if (tops[index] < scrollTop + viewportHeight && tops[index] + heights[index] > scrollTop) {\n visible.push(index);\n }\n }\n if (visible.length === 0) {\n return [];\n }\n\n const first: number = Math.max(0, visible[0] - overscan);\n const last: number = Math.min(total - 1, visible[visible.length - 1] + overscan);\n\n return Array.from({ length: last - first + 1 }, (_: unknown, offset: number): number => first + offset);\n}\n\n/**\n * Куда прокрутить после смены масштаба, чтобы на экране осталось то же место той же страницы.\n * Без этого увеличение уводило бы человека на несколько страниц вниз от того, что он читал.\n */\nexport function anchoredScrollTop(oldTop: number, oldHeight: number, oldScrollTop: number, newTop: number, newHeight: number): number {\n if (oldHeight <= 0) {\n return newTop;\n }\n const ratio: number = (oldScrollTop - oldTop) / oldHeight;\n\n return Math.max(0, newTop + ratio * newHeight);\n}\n\n/** Подпись масштаба для плашки в процентах: «83%», а не «83.47%». */\nexport function zoomPercentLabel(percent: number): string {\n return `${Math.round(percent)}%`;\n}\n","import { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Печать и скачивание документа — то, что уходит за пределы окна просмотра.\n */\n\n/** Блок печатного листа. Лист лежит в конце `<body>`, вне просмотрщика, и класс у него свой. */\nexport const RT_PDF_PRINT_BLOCK: string = 'rt-pdf-viewer-print';\n\n/** Признак на корне страницы, пока идёт печать: под ним прячется всё, кроме листа. */\nexport const RT_PDF_PRINTING_FLAG: string = 'rt-pdf-viewer-printing';\n\n/** Плотность печати: 150 точек на дюйм — текст чёткий, а лист в сотню страниц не выедает память. */\nconst PRINT_DPI: number = 150;\n\n/** Байты документа в том виде, в каком их принимает `Blob`. */\nfunction blobOf(bytes: Uint8Array | ArrayBuffer): Blob {\n const copy: Uint8Array<ArrayBuffer> = bytes instanceof ArrayBuffer ? new Uint8Array(bytes.slice(0)) : new Uint8Array(bytes);\n\n return new Blob([copy], { type: 'application/pdf' });\n}\n\n/**\n * Сохраняет файл под его именем. Байты уходят ссылкой на `Blob`: так браузер берёт имя из\n * `download`. Адрес без байтов отдаётся ссылкой как есть — чужой домен имя может и не принять, но\n * файл человек получит.\n */\nexport function downloadPdf(\n doc: Document,\n win: Window & typeof globalThis,\n file: Uint8Array | ArrayBuffer | string,\n fileName: string\n): void {\n const anchor: HTMLAnchorElement = doc.createElement('a');\n const ownUrl: string | null = typeof file === 'string' ? null : win.URL.createObjectURL(blobOf(file));\n\n anchor.href = ownUrl ?? (typeof file === 'string' ? file : '');\n anchor.download = fileName;\n anchor.rel = 'noopener';\n anchor.style.display = 'none';\n doc.body.append(anchor);\n anchor.click();\n anchor.remove();\n\n if (ownUrl) {\n // Отзыв сразу после щелчка обрывает скачивание в части браузеров: ссылка живёт ещё ход.\n win.setTimeout((): void => win.URL.revokeObjectURL(ownUrl), 0);\n }\n}\n\nfunction canvasBlob(canvas: HTMLCanvasElement): Promise<Blob> {\n return new Promise<Blob>((resolve: (blob: Blob) => void, reject: (reason: Error) => void): void => {\n canvas.toBlob((blob: Blob | null): void => (blob ? resolve(blob) : reject(new Error('canvas is empty'))), 'image/png');\n });\n}\n\n/**\n * Готовит печатный лист: каждая страница рисуется в картинку и встаёт в разметку в конце `<body>`.\n *\n * Лист собирается из картинок, а не из `<iframe>` с самим файлом: встроенного показа PDF нет у\n * мобильных браузеров, а `print()` у кадра с PDF в части настольных печатает пустую страницу или\n * только первую. Картинки печатает любой браузер.\n */\nexport async function preparePrintSheet(\n pdf: IRtPdfViewer.PdfDocument,\n doc: Document,\n win: Window & typeof globalThis\n): Promise<IRtPdfViewer.PrintSheet> {\n const sheet: HTMLDivElement = doc.createElement('div');\n const urls: string[] = [];\n const images: HTMLImageElement[] = [];\n sheet.classList.add(RT_PDF_PRINT_BLOCK);\n\n const dispose: () => void = (): void => {\n doc.documentElement.classList.remove(RT_PDF_PRINTING_FLAG);\n sheet.remove();\n for (const url of urls) {\n win.URL.revokeObjectURL(url);\n }\n };\n\n try {\n for (let pageNumber: number = 1; pageNumber <= pdf.numPages; pageNumber++) {\n const page: IRtPdfViewer.PdfPage = await pdf.getPage(pageNumber);\n const viewport: IRtPdfViewer.Viewport = page.getViewport({ scale: PRINT_DPI / 72 });\n const canvas: HTMLCanvasElement = doc.createElement('canvas');\n canvas.width = Math.floor(viewport.width);\n canvas.height = Math.floor(viewport.height);\n await page.render({ canvas, viewport, intent: 'print' }).promise;\n\n const url: string = win.URL.createObjectURL(await canvasBlob(canvas));\n urls.push(url);\n // Холст печатной плотности весит десятки мегабайт: память отдаётся сразу, а не по уборке\n canvas.width = 0;\n canvas.height = 0;\n\n const image: HTMLImageElement = doc.createElement('img');\n image.classList.add(`${RT_PDF_PRINT_BLOCK}__page`);\n image.alt = '';\n image.src = url;\n images.push(image);\n sheet.append(image);\n }\n\n doc.body.append(sheet);\n // Печать до раскодирования картинок даёт пустые листы\n await Promise.all(images.map((image: HTMLImageElement): Promise<void> => image.decode().catch((): void => undefined)));\n doc.documentElement.classList.add(RT_PDF_PRINTING_FLAG);\n } catch (error: unknown) {\n dispose();\n throw error;\n }\n\n return { dispose };\n}\n","import { InjectionToken, Provider } from '@angular/core';\n\nimport { IRtPdfViewer } from './rt-pdf-viewer.model';\n\n/**\n * Адрес воркера по умолчанию. Относительный: считается от `<base href>`, поэтому приложение под\n * своим префиксом не правит его руками. Файл туда кладёт сборка приложения — см. обзор компонента.\n */\nexport const RT_PDF_VIEWER_WORKER_SRC: string = 'assets/pdfjs/pdf.worker.min.mjs';\n\n/**\n * Рисовальщик грузится только при открытии окна. Статический импорт положил бы `pdfjs-dist` в\n * бандл каждому, кто импортировал вход, даже если окно так и не открылось.\n */\nfunction loadPdfJs(): Promise<IRtPdfViewer.PdfJs> {\n return import('pdfjs-dist');\n}\n\n/** Настройки рисовальщика просмотрщика. Без провайдера — умолчания выше. */\nexport const RT_PDF_VIEWER_CONFIG: InjectionToken<IRtPdfViewer.Config> = new InjectionToken<IRtPdfViewer.Config>('RT_PDF_VIEWER_CONFIG', {\n providedIn: 'root',\n factory: (): IRtPdfViewer.Config => ({ workerSrc: RT_PDF_VIEWER_WORKER_SRC, load: loadPdfJs }),\n});\n\n/**\n * Настраивает просмотрщик: адрес воркера и каталоги данных `pdfjs-dist`. Незаданное берётся из\n * умолчаний.\n *\n * ```typescript\n * export const appConfig: ApplicationConfig = {\n * providers: [provideRtPdfViewer({ workerSrc: 'static/pdf.worker.min.mjs' })],\n * };\n * ```\n */\nexport function provideRtPdfViewer(config: Partial<IRtPdfViewer.Config>): Provider {\n return {\n provide: RT_PDF_VIEWER_CONFIG,\n useValue: { workerSrc: RT_PDF_VIEWER_WORKER_SRC, load: loadPdfJs, ...config } satisfies IRtPdfViewer.Config,\n };\n}\n","import { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { DOCUMENT } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n inject,\n signal,\n viewChild,\n viewChildren,\n ChangeDetectionStrategy,\n Component,\n DestroyRef,\n ElementRef,\n Injector,\n Signal,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective, WINDOW } from '@rt-tools/core';\n\nimport { rtKitLabel, RT_KIT_LABELS, TRtKitLabelMap, TRtKitLabelParams } from '@rt-tools/ui-kit-v2/core';\nimport { RtButtonDirective } from '@rt-tools/ui-kit-v2/button';\nimport { RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';\nimport { RT_DIALOG_DATA } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtEmptyStateComponent } from '@rt-tools/ui-kit-v2/empty-state';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtSpinnerComponent } from '@rt-tools/ui-kit-v2/spinner';\nimport {\n anchoredScrollTop,\n canZoomIn,\n canZoomOut,\n fitWidthPercent,\n initialZoom,\n nextZoomStep,\n pageBoxes,\n pagesToRender,\n visiblePageIndex,\n zoomPercentLabel,\n} from './rt-pdf-viewer.logic';\nimport { IRtPdfViewer } from './rt-pdf-viewer.model';\nimport { downloadPdf, preparePrintSheet } from './rt-pdf-viewer.output';\nimport { RT_PDF_VIEWER_CONFIG } from './rt-pdf-viewer.tokens';\n\nconst BEM_BLOCK: string = 'rt-pdf-viewer';\n\n/** Сколько страниц сверх нарисованных держать холстами: дальше холст очищается ради памяти. */\nconst KEEP_OVERSCAN: number = 3;\n\n/** Нарисованная страница: холст и масштаб, в котором он нарисован. */\ninterface IRenderedPage {\n readonly percent: number;\n readonly task: IRtPdfViewer.RenderTask;\n}\n\n/**\n * Полноэкранный просмотр документа PDF поверх `RtDialogService`.\n *\n * Страницы рисует `pdfjs-dist` — необязательная зависимость кита. Он грузится динамическим импортом\n * только при открытии окна, поэтому вход `@rt-tools/ui-kit-v2/pdf-viewer` не утяжеляет бандл, пока\n * окно не открыто. Рисуются только видимые страницы и их соседи, с плотностью точек экрана.\n *\n * `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог; фокус после закрытия\n * просмотрщик возвращает туда, откуда его открыли.\n */\n@Component({\n selector: 'rt-pdf-viewer',\n templateUrl: './rt-pdf-viewer.component.html',\n styleUrl: './rt-pdf-viewer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n CdkTrapFocus,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtButtonDirective,\n RtEmptyStateComponent,\n RtIconButtonComponent,\n RtIconComponent,\n RtSpinnerComponent,\n\n // transloco\n ],\n host: {\n class: BEM_BLOCK,\n role: 'dialog',\n 'aria-modal': 'true',\n '[attr.aria-label]': 'fileName',\n // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него сразу после открытия\n tabindex: '-1',\n '(keydown.escape)': 'onEscape($event)',\n '(window:resize)': 'onResize()',\n },\n})\nexport class RtPdfViewerComponent {\n readonly #data: IRtPdfViewer.Data = inject(RT_DIALOG_DATA) as IRtPdfViewer.Data;\n readonly #dialogRef: RtDialogRef<void> = inject(RtDialogRef<void>);\n readonly #config: IRtPdfViewer.Config = inject(RT_PDF_VIEWER_CONFIG);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef<HTMLElement>);\n readonly #document: Document = inject(DOCUMENT);\n readonly #window: Window & typeof globalThis = inject(WINDOW) as Window & typeof globalThis;\n readonly #injector: Injector = inject(Injector);\n\n /** Откуда открыли: туда возвращается фокус после закрытия */\n readonly #opener: HTMLElement | null = this.#focusedElement();\n\n readonly #availableWidth: WritableSignal<number> = signal<number>(0);\n readonly #pageSizes: WritableSignal<readonly IRtPdfViewer.PageSize[]> = signal<readonly IRtPdfViewer.PageSize[]>([]);\n readonly #pageOfParams: Signal<TRtKitLabelParams> = computed((): TRtKitLabelParams => ({\n page: this.currentPage() + 1,\n total: this.#pageSizes().length,\n }));\n\n #loadingTask: IRtPdfViewer.LoadingTask | null = null;\n #pdf: IRtPdfViewer.PdfDocument | null = null;\n #pages: Map<number, Promise<IRtPdfViewer.PdfPage>> = new Map<number, Promise<IRtPdfViewer.PdfPage>>();\n #rendered: Map<number, IRenderedPage> = new Map<number, IRenderedPage>();\n #printSheet: IRtPdfViewer.PrintSheet | null = null;\n #destroyed: boolean = false;\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly scroller: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('scroller');\n protected readonly column: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('column');\n protected readonly pageNodes: Signal<readonly ElementRef<HTMLElement>[]> = viewChildren<ElementRef<HTMLElement>>('page');\n protected readonly canvases: Signal<readonly ElementRef<HTMLCanvasElement>[]> = viewChildren<ElementRef<HTMLCanvasElement>>('canvas');\n\n protected readonly fileName: string = this.#data.fileName;\n\n protected readonly status: WritableSignal<IRtPdfViewer.Status> = signal<IRtPdfViewer.Status>('loading');\n\n protected readonly printing: WritableSignal<boolean> = signal<boolean>(false);\n\n protected readonly zoom: WritableSignal<IRtPdfViewer.Zoom> = signal<IRtPdfViewer.Zoom>({ kind: 'percent', percent: 100 });\n\n /** Масштаб на экране в процентах — и для «по ширине», который хранится режимом */\n protected readonly percent: Signal<number> = computed((): number => {\n const zoom: IRtPdfViewer.Zoom = this.zoom();\n\n return zoom.kind === 'fit' ? fitWidthPercent(this.#availableWidth(), this.#pageSizes()) : zoom.percent;\n });\n\n protected readonly isFit: Signal<boolean> = computed((): boolean => this.zoom().kind === 'fit');\n\n protected readonly zoomLabel: Signal<string> = computed((): string =>\n this.isFit() ? this.t().pdfViewerFitWidth : zoomPercentLabel(this.percent())\n );\n\n protected readonly canZoomIn: Signal<boolean> = computed((): boolean => canZoomIn(this.percent()));\n\n protected readonly canZoomOut: Signal<boolean> = computed((): boolean => canZoomOut(this.percent()));\n\n protected readonly boxes: Signal<IRtPdfViewer.PageBox[]> = computed((): IRtPdfViewer.PageBox[] =>\n pageBoxes(this.#pageSizes(), this.percent())\n );\n\n /** Видимая сейчас страница, с нуля */\n protected readonly currentPage: WritableSignal<number> = signal<number>(0);\n\n protected readonly counter: Signal<string> = computed((): string => `${this.currentPage() + 1} / ${this.#pageSizes().length}`);\n\n protected readonly pageOfLabel: Signal<string> = rtKitLabel('pdfViewerPageOf', this.#pageOfParams);\n\n constructor() {\n inject(DestroyRef).onDestroy((): void => this.#teardown());\n\n afterNextRender((): void => {\n this.#host.nativeElement.focus();\n void this.#open();\n });\n }\n\n public close(): void {\n this.#dialogRef.close();\n }\n\n /** Esc закрывает только окно: окно под ним, например боковая панель, остаётся открытым */\n protected onEscape(event: Event): void {\n event.stopPropagation();\n this.close();\n }\n\n protected zoomIn(): void {\n this.#setZoom({ kind: 'percent', percent: nextZoomStep(this.percent(), 1) });\n }\n\n protected zoomOut(): void {\n this.#setZoom({ kind: 'percent', percent: nextZoomStep(this.percent(), -1) });\n }\n\n protected fitWidth(): void {\n this.#measure();\n this.#setZoom({ kind: 'fit' });\n }\n\n protected onScroll(): void {\n this.#renderVisible();\n }\n\n protected onResize(): void {\n if (this.status() !== 'ready') {\n return;\n }\n this.#measure();\n if (this.isFit()) {\n this.#afterLayout((): void => this.#renderVisible());\n }\n }\n\n /** Байты уходят как пришли, а адрес — байтами документа, если он уже загружен */\n protected async download(): Promise<void> {\n const source: IRtPdfViewer.Source = this.#data.source;\n if (typeof source !== 'string') {\n downloadPdf(this.#document, this.#window, source, this.fileName);\n return;\n }\n const bytes: Uint8Array | null = this.#pdf ? await this.#pdf.getData().catch((): null => null) : null;\n downloadPdf(this.#document, this.#window, bytes ?? source, this.fileName);\n }\n\n /**\n * Печать документа целиком, без полосы и плашки: страницы рисуются картинками в отдельный лист,\n * и на время печати страница прячет всё, кроме него. Лист убирается после печати.\n */\n protected async print(): Promise<void> {\n const pdf: IRtPdfViewer.PdfDocument | null = this.#pdf;\n if (!pdf || this.printing()) {\n return;\n }\n this.printing.set(true);\n try {\n this.#printSheet?.dispose();\n const sheet: IRtPdfViewer.PrintSheet = await preparePrintSheet(pdf, this.#document, this.#window);\n if (this.#destroyed) {\n sheet.dispose();\n return;\n }\n this.#printSheet = sheet;\n this.#window.addEventListener(\n 'afterprint',\n (): void => {\n sheet.dispose();\n if (this.#printSheet === sheet) {\n this.#printSheet = null;\n }\n },\n { once: true }\n );\n this.#window.print();\n } catch {\n this.#printSheet?.dispose();\n this.#printSheet = null;\n } finally {\n this.printing.set(false);\n }\n }\n\n async #open(): Promise<void> {\n try {\n const pdfjs: IRtPdfViewer.PdfJs = await this.#config.load();\n if (this.#destroyed) {\n return;\n }\n pdfjs.GlobalWorkerOptions.workerSrc = this.#config.workerSrc;\n this.#loadingTask = pdfjs.getDocument(this.#documentParams());\n const pdf: IRtPdfViewer.PdfDocument = await this.#loadingTask.promise;\n if (this.#destroyed) {\n return;\n }\n this.#pdf = pdf;\n\n // Размеры всех страниц нужны сразу: без них нет высоты столбца, и прокрутка прыгала бы\n // по мере того, как страницы рисуются\n const sizes: IRtPdfViewer.PageSize[] = await Promise.all(\n Array.from({ length: pdf.numPages }, async (_: unknown, index: number): Promise<IRtPdfViewer.PageSize> => {\n const viewport: IRtPdfViewer.Viewport = (await this.#page(index)).getViewport({ scale: 1 });\n return { width: viewport.width, height: viewport.height };\n })\n );\n if (this.#destroyed) {\n return;\n }\n // Масштаб выбирается до показа страниц: иначе на телефоне на кадр мелькал бы обрезанный\n // лист в 100%, и только потом — «по ширине»\n this.#measure();\n this.zoom.set(initialZoom(this.#host.nativeElement.clientWidth, this.#availableWidth(), sizes));\n this.#pageSizes.set(sizes);\n this.status.set('ready');\n this.#afterLayout((): void => this.#renderVisible());\n } catch {\n if (!this.#destroyed) {\n this.status.set('error');\n }\n }\n }\n\n #documentParams(): IRtPdfViewer.DocumentParams {\n const source: IRtPdfViewer.Source = this.#data.source;\n const data: Pick<IRtPdfViewer.DocumentParams, 'url' | 'data'> =\n typeof source === 'string'\n ? { url: source }\n : // Рисовальщик забирает буфер себе в поток — отдаётся копия, оригинал нужен для скачивания\n { data: source instanceof ArrayBuffer ? new Uint8Array(source.slice(0)) : source.slice() };\n\n return {\n ...data,\n ...(this.#config.cMapUrl ? { cMapUrl: this.#config.cMapUrl, cMapPacked: true } : {}),\n ...(this.#config.standardFontDataUrl ? { standardFontDataUrl: this.#config.standardFontDataUrl } : {}),\n ...(this.#config.wasmUrl ? { wasmUrl: this.#config.wasmUrl } : {}),\n };\n }\n\n #page(index: number): Promise<IRtPdfViewer.PdfPage> {\n const pdf: IRtPdfViewer.PdfDocument | null = this.#pdf;\n if (!pdf) {\n return Promise.reject(new Error('document is not loaded'));\n }\n let page: Promise<IRtPdfViewer.PdfPage> | undefined = this.#pages.get(index);\n if (!page) {\n page = pdf.getPage(index + 1);\n this.#pages.set(index, page);\n }\n\n return page;\n }\n\n /**\n * Место, в которое страница встаёт «по ширине»: ширина окна прокрутки без полей столбца. Сам\n * столбец не меряется — увеличенная страница растягивает его шире окна.\n */\n #measure(): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const column: HTMLElement | undefined = this.column()?.nativeElement;\n if (!scroller || !column) {\n return;\n }\n const style: CSSStyleDeclaration = this.#window.getComputedStyle(column);\n const padding: number = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);\n this.#availableWidth.set(Math.max(0, scroller.clientWidth - padding));\n }\n\n /**\n * Смена масштаба держит на экране то же место той же страницы: после перестройки столбца\n * прокрутка ставится от новой высоты страницы, а не остаётся на прежнем числе пикселей.\n */\n #setZoom(zoom: IRtPdfViewer.Zoom): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const current: number = this.currentPage();\n const node: HTMLElement | undefined = this.pageNodes()[current]?.nativeElement;\n const before: { top: number; height: number; scrollTop: number } | null =\n scroller && node ? { top: node.offsetTop, height: node.offsetHeight, scrollTop: scroller.scrollTop } : null;\n\n this.zoom.set(zoom);\n this.#afterLayout((): void => {\n const after: HTMLElement | undefined = this.pageNodes()[current]?.nativeElement;\n if (scroller && after && before) {\n scroller.scrollTop = anchoredScrollTop(before.top, before.height, before.scrollTop, after.offsetTop, after.offsetHeight);\n }\n this.#renderVisible();\n });\n }\n\n #afterLayout(callback: () => void): void {\n afterNextRender(callback, { injector: this.#injector });\n }\n\n /** Счётчик догоняет прокрутку, а видимые страницы и их соседи дорисовываются */\n #renderVisible(): void {\n const scroller: HTMLElement | undefined = this.scroller()?.nativeElement;\n const nodes: readonly ElementRef<HTMLElement>[] = this.pageNodes();\n if (!scroller || nodes.length === 0 || this.#destroyed) {\n return;\n }\n const tops: number[] = nodes.map((node: ElementRef<HTMLElement>): number => node.nativeElement.offsetTop);\n const heights: number[] = nodes.map((node: ElementRef<HTMLElement>): number => node.nativeElement.offsetHeight);\n const { scrollTop, clientHeight } = scroller;\n\n this.currentPage.set(visiblePageIndex(tops, heights, scrollTop, clientHeight));\n\n const keep: Set<number> = new Set<number>(pagesToRender(tops, heights, scrollTop, clientHeight, KEEP_OVERSCAN));\n for (const index of [...this.#rendered.keys()]) {\n if (!keep.has(index)) {\n this.#clearPage(index);\n }\n }\n for (const index of pagesToRender(tops, heights, scrollTop, clientHeight)) {\n void this.#renderPage(index);\n }\n }\n\n async #renderPage(index: number): Promise<void> {\n const percent: number = this.percent();\n const existing: IRenderedPage | undefined = this.#rendered.get(index);\n if (existing?.percent === percent) {\n return;\n }\n existing?.task.cancel();\n this.#rendered.delete(index);\n\n const canvas: HTMLCanvasElement | undefined = this.canvases()[index]?.nativeElement;\n const box: IRtPdfViewer.PageBox | undefined = this.boxes()[index];\n if (!canvas || !box) {\n return;\n }\n try {\n const page: IRtPdfViewer.PdfPage = await this.#page(index);\n if (this.#destroyed || this.percent() !== percent || this.#rendered.has(index)) {\n return;\n }\n // Холст в точках экрана, а не в CSS-пикселях: иначе на плотном экране текст мылится\n const ratio: number = this.#window.devicePixelRatio || 1;\n const viewport: IRtPdfViewer.Viewport = page.getViewport({ scale: (box.width / page.getViewport({ scale: 1 }).width) * ratio });\n canvas.width = Math.floor(viewport.width);\n canvas.height = Math.floor(viewport.height);\n const task: IRtPdfViewer.RenderTask = page.render({ canvas, viewport });\n this.#rendered.set(index, { percent, task });\n await task.promise;\n } catch {\n // Отменённая отрисовка — обычное дело при прокрутке; страница дорисуется при подходе\n if (this.#rendered.get(index)?.percent === percent) {\n this.#rendered.delete(index);\n }\n }\n }\n\n #clearPage(index: number): void {\n this.#rendered.get(index)?.task.cancel();\n this.#rendered.delete(index);\n const canvas: HTMLCanvasElement | undefined = this.canvases()[index]?.nativeElement;\n if (canvas) {\n canvas.width = 0;\n canvas.height = 0;\n }\n }\n\n #teardown(): void {\n this.#destroyed = true;\n for (const rendered of this.#rendered.values()) {\n rendered.task.cancel();\n }\n this.#rendered.clear();\n this.#printSheet?.dispose();\n this.#printSheet = null;\n void this.#loadingTask?.destroy().catch((): void => undefined);\n this.#pdf = null;\n if (this.#opener?.isConnected) {\n this.#opener.focus();\n }\n }\n\n #focusedElement(): HTMLElement | null {\n const active: Element | null = this.#document.activeElement;\n\n return active instanceof HTMLElement && active !== this.#document.body ? active : null;\n }\n}\n","<ng-container rtBlock=\"rt-pdf-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому авто-захвата здесь нет -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <!-- Полоса видна сразу, ещё до загрузки документа: человек видит, что открыл, и может закрыть -->\n <header rtElem=\"bar\">\n <div rtElem=\"title\">\n <rt-icon rtElem=\"file-icon\" name=\"attach-pdf\" size=\"md\" />\n <span rtElem=\"file-name\" qa-dataid=\"pdf-viewer-file-name\" [attr.title]=\"fileName\">{{ fileName }}</span>\n </div>\n\n <div rtElem=\"actions\">\n <rt-icon-button\n qa-dataid=\"pdf-viewer-print\"\n icon=\"print\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerPrint\"\n [tooltip]=\"t().pdfViewerPrint\"\n [disabled]=\"status() !== 'ready'\"\n [loading]=\"printing()\"\n (clicked)=\"print()\" />\n <rt-icon-button\n qa-dataid=\"pdf-viewer-download\"\n icon=\"ico-download\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerDownload\"\n [tooltip]=\"t().pdfViewerDownload\"\n (clicked)=\"download()\" />\n <rt-icon-button\n qa-dataid=\"pdf-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().pdfViewerClose\"\n [tooltip]=\"t().pdfViewerClose\"\n (clicked)=\"close()\" />\n </div>\n </header>\n\n <div rtElem=\"scroller\" qa-dataid=\"pdf-viewer-pages\" #scroller (scroll)=\"onScroll()\">\n <!-- Столбец стоит и до загрузки: по его ширине выбирается начальный масштаб -->\n <div rtElem=\"column\" #column>\n @if (status() === 'ready') {\n @for (box of boxes(); track box.index) {\n <div\n rtElem=\"page\"\n qa-dataid=\"pdf-viewer-page\"\n #page\n [attr.data-page-index]=\"box.index\"\n [style.width.px]=\"box.width\"\n [style.height.px]=\"box.height\">\n <canvas rtElem=\"canvas\" #canvas></canvas>\n </div>\n }\n }\n </div>\n </div>\n\n @switch (status()) {\n @case ('loading') {\n <div rtElem=\"center\">\n <rt-spinner qa-dataid=\"pdf-viewer-spinner\" color=\"on-primary\" [diameter]=\"40\" />\n </div>\n }\n\n @case ('error') {\n <div rtElem=\"center\">\n <rt-empty-state\n rtElem=\"error\"\n qa-dataid=\"pdf-viewer-error\"\n [title]=\"t().pdfViewerErrorTitle\"\n [description]=\"t().pdfViewerErrorText\">\n <button\n rtButton\n rtElem=\"error-download\"\n qa-dataid=\"pdf-viewer-error-download\"\n type=\"button\"\n icon=\"ico-download\"\n [label]=\"t().pdfViewerDownload\"\n [attr.aria-label]=\"t().pdfViewerDownload\"\n (click)=\"download()\"></button>\n </rt-empty-state>\n </div>\n }\n\n @case ('ready') {\n <div rtElem=\"toolbar\" qa-dataid=\"pdf-viewer-toolbar\">\n <span rtElem=\"counter\" qa-dataid=\"pdf-viewer-counter\" role=\"status\" aria-live=\"off\" [attr.aria-label]=\"pageOfLabel()\">\n {{ counter() }}\n </span>\n <span rtElem=\"divider\" aria-hidden=\"true\"></span>\n <rt-icon-button\n qa-dataid=\"pdf-viewer-zoom-out\"\n icon=\"minus\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().pdfViewerZoomOut\"\n [disabled]=\"!canZoomOut()\"\n (clicked)=\"zoomOut()\" />\n <span rtElem=\"zoom\" qa-dataid=\"pdf-viewer-zoom\">{{ zoomLabel() }}</span>\n <rt-icon-button\n qa-dataid=\"pdf-viewer-zoom-in\"\n icon=\"ico-plus\"\n variant=\"ghost\"\n size=\"sm\"\n [ariaLabel]=\"t().pdfViewerZoomIn\"\n [disabled]=\"!canZoomIn()\"\n (clicked)=\"zoomIn()\" />\n <span rtElem=\"fit-divider\" aria-hidden=\"true\"></span>\n <button\n rtButton\n rtElem=\"fit\"\n qa-dataid=\"pdf-viewer-fit\"\n type=\"button\"\n appearance=\"text\"\n size=\"sm\"\n [label]=\"t().pdfViewerFitWidth\"\n [attr.aria-label]=\"t().pdfViewerFitWidth\"\n [pressed]=\"isFit()\"\n (click)=\"fitWidth()\"></button>\n </div>\n }\n }\n </div>\n</ng-container>\n"],"mappings":"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"}
@@ -109,6 +109,7 @@ var RtPopoverDirective = class RtPopoverDirective {
109
109
  carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);
110
110
  const portal = new TemplatePortal(this.template(), this.#viewContainerRef, { $implicit: this.context() });
111
111
  overlayRef.attach(portal);
112
+ this.#releasePaneMinHeight(overlayRef);
112
113
  this.isOpenState.set(true);
113
114
  this.opened.emit();
114
115
  }
@@ -184,6 +185,9 @@ var RtPopoverDirective = class RtPopoverDirective {
184
185
  const panes = Array.from(container.querySelectorAll(".cdk-overlay-pane"));
185
186
  return panes.indexOf(nested) > panes.indexOf(panel);
186
187
  }
188
+ #releasePaneMinHeight(overlayRef) {
189
+ overlayRef.overlayElement.style.minHeight = "";
190
+ }
187
191
  #resolvePanelClasses() {
188
192
  const base = "rt-popover-panel";
189
193
  const extra = this.panelClass().trim();
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-popover.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/popover/rt-popover.directive.ts"],"sourcesContent":["import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n ConnectedPosition,\n FlexibleConnectedPositionStrategy,\n Overlay,\n OverlayConfig,\n OverlayRef,\n OverlaySizeConfig,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport {\n booleanAttribute,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n numberAttribute,\n OnDestroy,\n output,\n OutputEmitterRef,\n Signal,\n signal,\n TemplateRef,\n ViewContainerRef,\n WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { map, merge, Observable, Subject, switchMap } from 'rxjs';\n\nimport { carryThemeScope, materialPresetClassesOf } from '@rt-tools/ui-kit-v2/core';\nimport { IRtPopover } from './rt-popover.model';\n\nconst HOVER_CLOSE_DELAY_MS: number = 100;\n\n/**\n * Высота, ниже которой ужимать панель под вьюпорт бессмысленно: в такой полосе не\n * видно и одного пункта. Нужна только режиму `fitViewport` — по ней CDK решает,\n * что свободного места под триггером хватает.\n */\nconst FIT_VIEWPORT_MIN_HEIGHT_PX: number = 120;\n\n/**\n * Переиспользуемый popover-примитив поверх CDK Overlay. Привязывает плавающую\n * панель (`<ng-template>`) к host-элементу. Движок — CDK Overlay\n * (`flexibleConnectedTo`): авто-flip у края viewport, reposition при скролле,\n * outside-click и Escape закрытие из коробки — без ручного `position: absolute`\n * в `:host` (тот резался `overflow: hidden` предками: таблицы, aside, карточки).\n *\n * Триггеры:\n * - `click` (default) — toggle по клику на host; outside-click закрывает.\n * - `hover` — открытие на `mouseenter`, закрытие с grace-периодом на `mouseleave`\n * (наведение на панель удерживает её открытой).\n * - `manual` — авто-listener'ы не вешаются; host сам зовёт `open()/close()`.\n * Используется form-control'ами с собственным жестом (autocomplete — открытие\n * по набору текста).\n *\n * `open()/close()/toggle()/isOpen()` публичны во ВСЕХ режимах — `trigger` лишь\n * определяет авто-listener'ы. `width: 'trigger'` синхронизирует ширину панели с\n * host'ом (dropdown'ы), `'trigger-min'` не даёт панели стать уже host'а и дальше пускает\n * её по содержимому, `'auto'` — content-sized (тултипы).\n */\n@Directive({\n selector: '[rtPopover]',\n exportAs: 'rtPopover',\n host: {\n '(click)': 'onHostClick()',\n '(mouseenter)': 'onHostMouseEnter()',\n '(mouseleave)': 'onHostMouseLeave($event)',\n },\n})\nexport class RtPopoverDirective implements OnDestroy {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #elementRef: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #viewContainerRef: ViewContainerRef = inject(ViewContainerRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #destroyRef: DestroyRef = inject(DestroyRef);\n\n readonly #overlayCreatedSource: Subject<OverlayRef> = new Subject<OverlayRef>();\n\n #overlayRef: OverlayRef | null = null;\n #positionStrategy: FlexibleConnectedPositionStrategy | null = null;\n #hoverCloseTimer: ReturnType<typeof setTimeout> | null = null;\n\n protected readonly isOpenState: WritableSignal<boolean> = signal<boolean>(false);\n\n public readonly template: InputSignal<TemplateRef<unknown> | null> = input<TemplateRef<unknown> | null>(null, { alias: 'rtPopover' });\n\n public readonly trigger: InputSignal<IRtPopover.Trigger> = input<IRtPopover.Trigger>('click', {\n alias: 'rtPopoverTrigger',\n });\n\n public readonly width: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('auto', {\n alias: 'rtPopoverWidth',\n });\n\n /** `end` — панель прижата к правому краю host'а (триггеры у правого края viewport). */\n public readonly align: InputSignal<IRtPopover.Align> = input<IRtPopover.Align>('start', {\n alias: 'rtPopoverAlign',\n });\n\n /**\n * Панель обязана уместиться во вьюпорт: не влезающая по ширине сдвигается от\n * края экрана внутрь, не влезающая по высоте ужимается до свободного места под\n * триггером, и содержимое в ней прокручивается.\n *\n * По умолчанию выключено: панель стоит ровно там, куда её ставит привязка к\n * триггеру, даже если часть её ушла за экран. Включать там, где панель широкая\n * или высокая настолько, что привязка сама по себе её не удерживает, — иначе\n * пункты навигации остаются за краем экрана и недостижимы.\n */\n public readonly fitViewport: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n alias: 'rtPopoverFitViewport',\n transform: booleanAttribute,\n });\n\n public readonly context: InputSignal<unknown> = input<unknown>(null, {\n alias: 'rtPopoverContext',\n });\n\n public readonly panelClass: InputSignal<string> = input<string>('', {\n alias: 'rtPopoverPanelClass',\n });\n\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n alias: 'rtPopoverDisabled',\n transform: booleanAttribute,\n });\n\n public readonly offsetY: InputSignalWithTransform<number, NumberInput> = input<number, NumberInput>(4, {\n alias: 'rtPopoverOffsetY',\n transform: numberAttribute,\n });\n\n public readonly offsetX: InputSignalWithTransform<number, NumberInput> = input<number, NumberInput>(0, {\n alias: 'rtPopoverOffsetX',\n transform: numberAttribute,\n });\n\n /**\n * Свои места панели вокруг host'а: первое главное, остальные — запасные у края экрана. `null` —\n * прежние два: под host'ом и над ним. Заданные читаются при каждом открытии.\n */\n public readonly positions: InputSignal<readonly ConnectedPosition[] | null> = input<readonly ConnectedPosition[] | null>(null, {\n alias: 'rtPopoverPositions',\n });\n\n public readonly opened: OutputEmitterRef<void> = output<void>({ alias: 'rtPopoverOpened' });\n\n public readonly closed: OutputEmitterRef<void> = output<void>({ alias: 'rtPopoverClosed' });\n\n public readonly isOpen: Signal<boolean> = this.isOpenState.asReadonly();\n\n constructor() {\n // Outside-click / ESC подписки на единожды создаваемый overlay живут в\n // одном конструкторном стриме: #ensureOverlay эмитит OverlayRef после\n // создания (ровно один раз за жизнь директивы), стрим живёт до destroy.\n this.#overlayCreatedSource\n .pipe(\n switchMap((overlayRef: OverlayRef): Observable<() => void> =>\n merge(\n overlayRef.outsidePointerEvents().pipe(\n map((event: MouseEvent): (() => void) => (): void => {\n // Клик по host в режиме click обрабатывает onHostClick (toggle) —\n // не закрываем здесь, иначе двойной toggle (close + open).\n if (this.#elementRef.nativeElement.contains(event.target as Node)) {\n return;\n }\n // Клик по вложенной панели — не клик снаружи: закрытие\n // разрушило бы её вместе с нашей до того, как выбор\n // дойдёт до обработчика\n if (this.#isNestedOverlay(event.target)) {\n return;\n }\n this.close();\n })\n ),\n overlayRef.keydownEvents().pipe(\n map((event: KeyboardEvent): (() => void) => (): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.close();\n }\n })\n )\n )\n ),\n takeUntilDestroyed(this.#destroyRef)\n )\n .subscribe((handleCloseIntent: () => void): void => handleCloseIntent());\n }\n\n public ngOnDestroy(): void {\n this.#clearHoverTimer();\n this.#disposeOverlay();\n }\n\n public open(): void {\n if (this.disabled() || this.isOpenState() || this.template() === null) {\n return;\n }\n const overlayRef: OverlayRef = this.#ensureOverlay();\n const positions: readonly ConnectedPosition[] | null = this.positions();\n if (positions !== null) {\n this.#positionStrategy?.withPositions([...positions]);\n }\n const hostWidth: number = this.#elementRef.nativeElement.offsetWidth;\n const sizeByWidth: Record<IRtPopover.Width, OverlaySizeConfig> = {\n trigger: { width: hostWidth },\n 'trigger-min': { minWidth: hostWidth },\n auto: {},\n };\n overlayRef.updateSize(sizeByWidth[this.width()]);\n // Тема куска `rtTheme` вокруг host'а едет на коробку панели: панель лежит в конце\n // страницы, и без этого список из тёмной карточки рисовался темой страницы.\n carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);\n const portal: TemplatePortal<unknown> = new TemplatePortal(this.template() as TemplateRef<unknown>, this.#viewContainerRef, {\n $implicit: this.context(),\n });\n overlayRef.attach(portal);\n this.isOpenState.set(true);\n this.opened.emit();\n }\n\n public close(): void {\n if (!this.isOpenState()) {\n return;\n }\n this.#overlayRef?.detach();\n this.isOpenState.set(false);\n this.closed.emit();\n }\n\n public toggle(): void {\n if (this.isOpenState()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n // --- авто-triggers (HostListener'ы навешиваются на host безусловно, но\n // проверяют активный режим — это дешевле, чем условная подписка) ---\n\n protected onHostClick(): void {\n if (this.trigger() !== 'click' || this.disabled()) {\n return;\n }\n this.toggle();\n }\n\n protected onHostMouseEnter(): void {\n if (this.trigger() !== 'hover') {\n return;\n }\n this.#clearHoverTimer();\n this.open();\n }\n\n protected onHostMouseLeave(event: MouseEvent): void {\n if (this.trigger() !== 'hover') {\n return;\n }\n if (this.#isNestedOverlay(event.relatedTarget)) {\n return;\n }\n this.#scheduleHoverClose();\n }\n\n #ensureOverlay(): OverlayRef {\n if (this.#overlayRef !== null) {\n return this.#overlayRef;\n }\n const alignX: 'start' | 'end' = this.align();\n const positions: ConnectedPosition[] = [\n {\n originX: alignX,\n originY: 'bottom',\n overlayX: alignX,\n overlayY: 'top',\n offsetX: this.offsetX(),\n offsetY: this.offsetY(),\n },\n {\n originX: alignX,\n originY: 'top',\n overlayX: alignX,\n overlayY: 'bottom',\n offsetX: this.offsetX(),\n offsetY: -this.offsetY(),\n },\n ];\n // Сдвиг к краю экрана и ужатие по высоте включаются только по просьбе\n // потребителя: панель, которая и так помещается, обязана стоять у своего\n // триггера — меню строки таблицы, подтверждение, меню профиля.\n const fitViewport: boolean = this.fitViewport();\n const positionStrategy: FlexibleConnectedPositionStrategy = this.#overlay\n .position()\n .flexibleConnectedTo(this.#elementRef)\n .withPush(fitViewport)\n .withFlexibleDimensions(fitViewport)\n // Отступ от края экрана равен отступу от триггера: свободное место\n // считается без него, и ужатая по высоте панель свисала бы за нижний\n // край ровно на этот отступ — вместе со своей рамкой.\n .withViewportMargin(fitViewport ? Math.abs(this.offsetY()) : 0)\n .withPositions(positions);\n this.#positionStrategy = positionStrategy;\n\n const config: OverlayConfig = new OverlayConfig({\n positionStrategy,\n scrollStrategy: this.#scrollStrategies.reposition(),\n hasBackdrop: false,\n panelClass: this.#resolvePanelClasses(),\n // Порог, ниже которого свободного места под триггером считается\n // недостаточно и панель не ужимается, а сдвигается целиком.\n minHeight: fitViewport ? FIT_VIEWPORT_MIN_HEIGHT_PX : undefined,\n });\n const overlayRef: OverlayRef = this.#overlay.create(config);\n\n // Hover-bridge: при переходе курсора с host'а на панель отложенный close\n // (grace-период) гасится; уход с панели планирует его заново. Без этого\n // hover-popover закрывался бы по таймеру, как только мышь покидает host.\n // Listeners живут на overlay-элементе и умирают вместе с ним в dispose().\n overlayRef.overlayElement.addEventListener('mouseenter', (): void => {\n if (this.trigger() === 'hover') {\n this.#clearHoverTimer();\n }\n });\n overlayRef.overlayElement.addEventListener('mouseleave', (event: MouseEvent): void => {\n if (this.trigger() === 'hover' && !this.#isNestedOverlay(event.relatedTarget)) {\n this.#scheduleHoverClose();\n }\n });\n\n // Outside-click / ESC подписки объявлены один раз в конструкторе —\n // здесь только эмит созданного OverlayRef (см. #overlayCreatedSource-стрим).\n this.#overlayCreatedSource.next(overlayRef);\n\n this.#overlayRef = overlayRef;\n return overlayRef;\n }\n\n /**\n * Панель, открытая из нашей — список `rt-select`, календарь, вложенное меню.\n *\n * В DOM она лежит не внутри нашей панели, а рядом, отдельным оверлеем CDK.\n * Поэтому и уход курсора на неё, и клик по ней выглядят как «ушёл наружу»:\n * меню профиля закрывалось прямо под рукой, стоило потянуться к списку\n * языков, а выбранное значение до обработчика не доходило — панель со\n * `rt-select` разрушалась раньше.\n *\n * Вложенность определяется порядком в контейнере оверлеев: CDK добавляет их\n * по мере открытия, поэтому панель, открытая из нашей, всегда следует за\n * нашей. Соседний оверлей, открытый до нас, этой проверкой не удерживается —\n * клик по нему закрывает попап, как и любой клик снаружи.\n */\n #isNestedOverlay(target: EventTarget | null): boolean {\n const panel: HTMLElement | undefined = this.#overlayRef?.overlayElement;\n if (!panel || !(target instanceof Element)) {\n return false;\n }\n\n const nested: Element | null = target.closest('.cdk-overlay-pane');\n const container: HTMLElement | null = panel.parentElement;\n if (nested === null || nested === panel || container === null) {\n return false;\n }\n\n // `querySelectorAll` отдаёт узлы в порядке документа — им и меряется,\n // какая панель открыта позже\n const panes: readonly Element[] = Array.from(container.querySelectorAll('.cdk-overlay-pane'));\n\n return panes.indexOf(nested) > panes.indexOf(panel);\n }\n\n #resolvePanelClasses(): string[] {\n const base: string = 'rt-popover-panel';\n const extra: string = this.panelClass().trim();\n const preset: string[] = materialPresetClassesOf(this.#elementRef.nativeElement);\n return extra ? [base, ...extra.split(/\\s+/), ...preset] : [base, ...preset];\n }\n\n #scheduleHoverClose(): void {\n this.#clearHoverTimer();\n this.#hoverCloseTimer = setTimeout((): void => this.close(), HOVER_CLOSE_DELAY_MS);\n }\n\n #clearHoverTimer(): void {\n if (this.#hoverCloseTimer !== null) {\n clearTimeout(this.#hoverCloseTimer);\n this.#hoverCloseTimer = null;\n }\n }\n\n #disposeOverlay(): void {\n this.#overlayRef?.dispose();\n this.#overlayRef = null;\n this.#positionStrategy = null;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-popover.mjs","names":[],"sources":["../../../projects/ui-kit-v2/src/lib/components/popover/rt-popover.directive.ts"],"sourcesContent":["import { BooleanInput, NumberInput } from '@angular/cdk/coercion';\nimport {\n ConnectedPosition,\n FlexibleConnectedPositionStrategy,\n Overlay,\n OverlayConfig,\n OverlayRef,\n OverlaySizeConfig,\n ScrollStrategyOptions,\n} from '@angular/cdk/overlay';\nimport { TemplatePortal } from '@angular/cdk/portal';\nimport {\n booleanAttribute,\n DestroyRef,\n Directive,\n ElementRef,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n numberAttribute,\n OnDestroy,\n output,\n OutputEmitterRef,\n Signal,\n signal,\n TemplateRef,\n ViewContainerRef,\n WritableSignal,\n} from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\n\nimport { map, merge, Observable, Subject, switchMap } from 'rxjs';\n\nimport { carryThemeScope, materialPresetClassesOf } from '@rt-tools/ui-kit-v2/core';\nimport { IRtPopover } from './rt-popover.model';\n\nconst HOVER_CLOSE_DELAY_MS: number = 100;\n\n/**\n * Высота, ниже которой ужимать панель под вьюпорт бессмысленно: в такой полосе не\n * видно и одного пункта. Нужна только режиму `fitViewport` — по ней CDK решает,\n * что свободного места под триггером хватает.\n */\nconst FIT_VIEWPORT_MIN_HEIGHT_PX: number = 120;\n\n/**\n * Переиспользуемый popover-примитив поверх CDK Overlay. Привязывает плавающую\n * панель (`<ng-template>`) к host-элементу. Движок — CDK Overlay\n * (`flexibleConnectedTo`): авто-flip у края viewport, reposition при скролле,\n * outside-click и Escape закрытие из коробки — без ручного `position: absolute`\n * в `:host` (тот резался `overflow: hidden` предками: таблицы, aside, карточки).\n *\n * Триггеры:\n * - `click` (default) — toggle по клику на host; outside-click закрывает.\n * - `hover` — открытие на `mouseenter`, закрытие с grace-периодом на `mouseleave`\n * (наведение на панель удерживает её открытой).\n * - `manual` — авто-listener'ы не вешаются; host сам зовёт `open()/close()`.\n * Используется form-control'ами с собственным жестом (autocomplete — открытие\n * по набору текста).\n *\n * `open()/close()/toggle()/isOpen()` публичны во ВСЕХ режимах — `trigger` лишь\n * определяет авто-listener'ы. `width: 'trigger'` синхронизирует ширину панели с\n * host'ом (dropdown'ы), `'trigger-min'` не даёт панели стать уже host'а и дальше пускает\n * её по содержимому, `'auto'` — content-sized (тултипы).\n */\n@Directive({\n selector: '[rtPopover]',\n exportAs: 'rtPopover',\n host: {\n '(click)': 'onHostClick()',\n '(mouseenter)': 'onHostMouseEnter()',\n '(mouseleave)': 'onHostMouseLeave($event)',\n },\n})\nexport class RtPopoverDirective implements OnDestroy {\n readonly #overlay: Overlay = inject(Overlay);\n readonly #elementRef: ElementRef<HTMLElement> = inject<ElementRef<HTMLElement>>(ElementRef);\n readonly #viewContainerRef: ViewContainerRef = inject(ViewContainerRef);\n readonly #scrollStrategies: ScrollStrategyOptions = inject(ScrollStrategyOptions);\n readonly #destroyRef: DestroyRef = inject(DestroyRef);\n\n readonly #overlayCreatedSource: Subject<OverlayRef> = new Subject<OverlayRef>();\n\n #overlayRef: OverlayRef | null = null;\n #positionStrategy: FlexibleConnectedPositionStrategy | null = null;\n #hoverCloseTimer: ReturnType<typeof setTimeout> | null = null;\n\n protected readonly isOpenState: WritableSignal<boolean> = signal<boolean>(false);\n\n public readonly template: InputSignal<TemplateRef<unknown> | null> = input<TemplateRef<unknown> | null>(null, { alias: 'rtPopover' });\n\n public readonly trigger: InputSignal<IRtPopover.Trigger> = input<IRtPopover.Trigger>('click', {\n alias: 'rtPopoverTrigger',\n });\n\n public readonly width: InputSignal<IRtPopover.Width> = input<IRtPopover.Width>('auto', {\n alias: 'rtPopoverWidth',\n });\n\n /** `end` — панель прижата к правому краю host'а (триггеры у правого края viewport). */\n public readonly align: InputSignal<IRtPopover.Align> = input<IRtPopover.Align>('start', {\n alias: 'rtPopoverAlign',\n });\n\n /**\n * Панель обязана уместиться во вьюпорт: не влезающая по ширине сдвигается от\n * края экрана внутрь, не влезающая по высоте ужимается до свободного места под\n * триггером, и содержимое в ней прокручивается.\n *\n * По умолчанию выключено: панель стоит ровно там, куда её ставит привязка к\n * триггеру, даже если часть её ушла за экран. Включать там, где панель широкая\n * или высокая настолько, что привязка сама по себе её не удерживает, — иначе\n * пункты навигации остаются за краем экрана и недостижимы.\n */\n public readonly fitViewport: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n alias: 'rtPopoverFitViewport',\n transform: booleanAttribute,\n });\n\n public readonly context: InputSignal<unknown> = input<unknown>(null, {\n alias: 'rtPopoverContext',\n });\n\n public readonly panelClass: InputSignal<string> = input<string>('', {\n alias: 'rtPopoverPanelClass',\n });\n\n public readonly disabled: InputSignalWithTransform<boolean, BooleanInput> = input<boolean, BooleanInput>(false, {\n alias: 'rtPopoverDisabled',\n transform: booleanAttribute,\n });\n\n public readonly offsetY: InputSignalWithTransform<number, NumberInput> = input<number, NumberInput>(4, {\n alias: 'rtPopoverOffsetY',\n transform: numberAttribute,\n });\n\n public readonly offsetX: InputSignalWithTransform<number, NumberInput> = input<number, NumberInput>(0, {\n alias: 'rtPopoverOffsetX',\n transform: numberAttribute,\n });\n\n /**\n * Свои места панели вокруг host'а: первое главное, остальные — запасные у края экрана. `null` —\n * прежние два: под host'ом и над ним. Заданные читаются при каждом открытии.\n */\n public readonly positions: InputSignal<readonly ConnectedPosition[] | null> = input<readonly ConnectedPosition[] | null>(null, {\n alias: 'rtPopoverPositions',\n });\n\n public readonly opened: OutputEmitterRef<void> = output<void>({ alias: 'rtPopoverOpened' });\n\n public readonly closed: OutputEmitterRef<void> = output<void>({ alias: 'rtPopoverClosed' });\n\n public readonly isOpen: Signal<boolean> = this.isOpenState.asReadonly();\n\n constructor() {\n // Outside-click / ESC подписки на единожды создаваемый overlay живут в\n // одном конструкторном стриме: #ensureOverlay эмитит OverlayRef после\n // создания (ровно один раз за жизнь директивы), стрим живёт до destroy.\n this.#overlayCreatedSource\n .pipe(\n switchMap((overlayRef: OverlayRef): Observable<() => void> =>\n merge(\n overlayRef.outsidePointerEvents().pipe(\n map((event: MouseEvent): (() => void) => (): void => {\n // Клик по host в режиме click обрабатывает onHostClick (toggle) —\n // не закрываем здесь, иначе двойной toggle (close + open).\n if (this.#elementRef.nativeElement.contains(event.target as Node)) {\n return;\n }\n // Клик по вложенной панели — не клик снаружи: закрытие\n // разрушило бы её вместе с нашей до того, как выбор\n // дойдёт до обработчика\n if (this.#isNestedOverlay(event.target)) {\n return;\n }\n this.close();\n })\n ),\n overlayRef.keydownEvents().pipe(\n map((event: KeyboardEvent): (() => void) => (): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n this.close();\n }\n })\n )\n )\n ),\n takeUntilDestroyed(this.#destroyRef)\n )\n .subscribe((handleCloseIntent: () => void): void => handleCloseIntent());\n }\n\n public ngOnDestroy(): void {\n this.#clearHoverTimer();\n this.#disposeOverlay();\n }\n\n public open(): void {\n if (this.disabled() || this.isOpenState() || this.template() === null) {\n return;\n }\n const overlayRef: OverlayRef = this.#ensureOverlay();\n const positions: readonly ConnectedPosition[] | null = this.positions();\n if (positions !== null) {\n this.#positionStrategy?.withPositions([...positions]);\n }\n const hostWidth: number = this.#elementRef.nativeElement.offsetWidth;\n const sizeByWidth: Record<IRtPopover.Width, OverlaySizeConfig> = {\n trigger: { width: hostWidth },\n 'trigger-min': { minWidth: hostWidth },\n auto: {},\n };\n overlayRef.updateSize(sizeByWidth[this.width()]);\n // Тема куска `rtTheme` вокруг host'а едет на коробку панели: панель лежит в конце\n // страницы, и без этого список из тёмной карточки рисовался темой страницы.\n carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);\n const portal: TemplatePortal<unknown> = new TemplatePortal(this.template() as TemplateRef<unknown>, this.#viewContainerRef, {\n $implicit: this.context(),\n });\n overlayRef.attach(portal);\n this.#releasePaneMinHeight(overlayRef);\n this.isOpenState.set(true);\n this.opened.emit();\n }\n\n public close(): void {\n if (!this.isOpenState()) {\n return;\n }\n this.#overlayRef?.detach();\n this.isOpenState.set(false);\n this.closed.emit();\n }\n\n public toggle(): void {\n if (this.isOpenState()) {\n this.close();\n } else {\n this.open();\n }\n }\n\n // --- авто-triggers (HostListener'ы навешиваются на host безусловно, но\n // проверяют активный режим — это дешевле, чем условная подписка) ---\n\n protected onHostClick(): void {\n if (this.trigger() !== 'click' || this.disabled()) {\n return;\n }\n this.toggle();\n }\n\n protected onHostMouseEnter(): void {\n if (this.trigger() !== 'hover') {\n return;\n }\n this.#clearHoverTimer();\n this.open();\n }\n\n protected onHostMouseLeave(event: MouseEvent): void {\n if (this.trigger() !== 'hover') {\n return;\n }\n if (this.#isNestedOverlay(event.relatedTarget)) {\n return;\n }\n this.#scheduleHoverClose();\n }\n\n #ensureOverlay(): OverlayRef {\n if (this.#overlayRef !== null) {\n return this.#overlayRef;\n }\n const alignX: 'start' | 'end' = this.align();\n const positions: ConnectedPosition[] = [\n {\n originX: alignX,\n originY: 'bottom',\n overlayX: alignX,\n overlayY: 'top',\n offsetX: this.offsetX(),\n offsetY: this.offsetY(),\n },\n {\n originX: alignX,\n originY: 'top',\n overlayX: alignX,\n overlayY: 'bottom',\n offsetX: this.offsetX(),\n offsetY: -this.offsetY(),\n },\n ];\n // Сдвиг к краю экрана и ужатие по высоте включаются только по просьбе\n // потребителя: панель, которая и так помещается, обязана стоять у своего\n // триггера — меню строки таблицы, подтверждение, меню профиля.\n const fitViewport: boolean = this.fitViewport();\n const positionStrategy: FlexibleConnectedPositionStrategy = this.#overlay\n .position()\n .flexibleConnectedTo(this.#elementRef)\n .withPush(fitViewport)\n .withFlexibleDimensions(fitViewport)\n // Отступ от края экрана равен отступу от триггера: свободное место\n // считается без него, и ужатая по высоте панель свисала бы за нижний\n // край ровно на этот отступ — вместе со своей рамкой.\n .withViewportMargin(fitViewport ? Math.abs(this.offsetY()) : 0)\n .withPositions(positions);\n this.#positionStrategy = positionStrategy;\n\n const config: OverlayConfig = new OverlayConfig({\n positionStrategy,\n scrollStrategy: this.#scrollStrategies.reposition(),\n hasBackdrop: false,\n panelClass: this.#resolvePanelClasses(),\n // Порог, ниже которого свободного места под триггером считается\n // недостаточно и панель не ужимается, а сдвигается целиком.\n minHeight: fitViewport ? FIT_VIEWPORT_MIN_HEIGHT_PX : undefined,\n });\n const overlayRef: OverlayRef = this.#overlay.create(config);\n\n // Hover-bridge: при переходе курсора с host'а на панель отложенный close\n // (grace-период) гасится; уход с панели планирует его заново. Без этого\n // hover-popover закрывался бы по таймеру, как только мышь покидает host.\n // Listeners живут на overlay-элементе и умирают вместе с ним в dispose().\n overlayRef.overlayElement.addEventListener('mouseenter', (): void => {\n if (this.trigger() === 'hover') {\n this.#clearHoverTimer();\n }\n });\n overlayRef.overlayElement.addEventListener('mouseleave', (event: MouseEvent): void => {\n if (this.trigger() === 'hover' && !this.#isNestedOverlay(event.relatedTarget)) {\n this.#scheduleHoverClose();\n }\n });\n\n // Outside-click / ESC подписки объявлены один раз в конструкторе —\n // здесь только эмит созданного OverlayRef (см. #overlayCreatedSource-стрим).\n this.#overlayCreatedSource.next(overlayRef);\n\n this.#overlayRef = overlayRef;\n return overlayRef;\n }\n\n /**\n * Панель, открытая из нашей — список `rt-select`, календарь, вложенное меню.\n *\n * В DOM она лежит не внутри нашей панели, а рядом, отдельным оверлеем CDK.\n * Поэтому и уход курсора на неё, и клик по ней выглядят как «ушёл наружу»:\n * меню профиля закрывалось прямо под рукой, стоило потянуться к списку\n * языков, а выбранное значение до обработчика не доходило — панель со\n * `rt-select` разрушалась раньше.\n *\n * Вложенность определяется порядком в контейнере оверлеев: CDK добавляет их\n * по мере открытия, поэтому панель, открытая из нашей, всегда следует за\n * нашей. Соседний оверлей, открытый до нас, этой проверкой не удерживается —\n * клик по нему закрывает попап, как и любой клик снаружи.\n */\n #isNestedOverlay(target: EventTarget | null): boolean {\n const panel: HTMLElement | undefined = this.#overlayRef?.overlayElement;\n if (!panel || !(target instanceof Element)) {\n return false;\n }\n\n const nested: Element | null = target.closest('.cdk-overlay-pane');\n const container: HTMLElement | null = panel.parentElement;\n if (nested === null || nested === panel || container === null) {\n return false;\n }\n\n // `querySelectorAll` отдаёт узлы в порядке документа — им и меряется,\n // какая панель открыта позже\n const panes: readonly Element[] = Array.from(container.querySelectorAll('.cdk-overlay-pane'));\n\n return panes.indexOf(nested) > panes.indexOf(panel);\n }\n\n /**\n * Порог подгонки остаётся в настройке оверлея и уходит из стиля коробки.\n *\n * CDK читает порог из настройки, когда решает, сдвинуть панель или ужать. Но при `attach` и\n * при каждом `updateSize` он пишет то же число в стиль коробки, и короткая панель\n * растягивалась до порога: под двумя пунктами «Ещё» шапки стояла пустая полоса.\n */\n #releasePaneMinHeight(overlayRef: OverlayRef): void {\n overlayRef.overlayElement.style.minHeight = '';\n }\n\n #resolvePanelClasses(): string[] {\n const base: string = 'rt-popover-panel';\n const extra: string = this.panelClass().trim();\n const preset: string[] = materialPresetClassesOf(this.#elementRef.nativeElement);\n return extra ? [base, ...extra.split(/\\s+/), ...preset] : [base, ...preset];\n }\n\n #scheduleHoverClose(): void {\n this.#clearHoverTimer();\n this.#hoverCloseTimer = setTimeout((): void => this.close(), HOVER_CLOSE_DELAY_MS);\n }\n\n #clearHoverTimer(): void {\n if (this.#hoverCloseTimer !== null) {\n clearTimeout(this.#hoverCloseTimer);\n this.#hoverCloseTimer = null;\n }\n }\n\n #disposeOverlay(): void {\n this.#overlayRef?.dispose();\n this.#overlayRef = null;\n this.#positionStrategy = null;\n }\n}\n"],"mappings":"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"}