@rt-tools/ui-kit-v2 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs +79 -0
- package/fesm2022/rt-tools-ui-kit-v2-accordion.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs +227 -0
- package/fesm2022/rt-tools-ui-kit-v2-action-bar.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-aside-section.mjs +47 -0
- package/fesm2022/rt-tools-ui-kit-v2-aside-section.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs +702 -0
- package/fesm2022/rt-tools-ui-kit-v2-aside.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +214 -0
- package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs +53 -0
- package/fesm2022/rt-tools-ui-kit-v2-bar-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-bottom-sheet.mjs +88 -0
- package/fesm2022/rt-tools-ui-kit-v2-bottom-sheet.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-button.mjs +365 -0
- package/fesm2022/rt-tools-ui-kit-v2-button.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +152 -0
- package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-card.mjs +72 -0
- package/fesm2022/rt-tools-ui-kit-v2-card.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs +111 -0
- package/fesm2022/rt-tools-ui-kit-v2-checkbox.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-collapsible-text.mjs +76 -0
- package/fesm2022/rt-tools-ui-kit-v2-collapsible-text.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-confirm-popover.mjs +255 -0
- package/fesm2022/rt-tools-ui-kit-v2-confirm-popover.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-container.mjs +1326 -0
- package/fesm2022/rt-tools-ui-kit-v2-container.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs +2590 -0
- package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-counter-row.mjs +45 -0
- package/fesm2022/rt-tools-ui-kit-v2-counter-row.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-counter.mjs +158 -0
- package/fesm2022/rt-tools-ui-kit-v2-counter.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +894 -0
- package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs +1217 -0
- package/fesm2022/rt-tools-ui-kit-v2-data-table.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs +779 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-picker.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs +573 -0
- package/fesm2022/rt-tools-ui-kit-v2-date-range.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-delta-view.mjs +141 -0
- package/fesm2022/rt-tools-ui-kit-v2-delta-view.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs +68 -0
- package/fesm2022/rt-tools-ui-kit-v2-detail-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-dialog.mjs +478 -0
- package/fesm2022/rt-tools-ui-kit-v2-dialog.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-dot-field.mjs +183 -0
- package/fesm2022/rt-tools-ui-kit-v2-dot-field.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs +44 -0
- package/fesm2022/rt-tools-ui-kit-v2-download-link.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +260 -0
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +937 -0
- package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs +56 -0
- package/fesm2022/rt-tools-ui-kit-v2-empty-state.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs +107 -0
- package/fesm2022/rt-tools-ui-kit-v2-expansion-panel.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs +183 -0
- package/fesm2022/rt-tools-ui-kit-v2-field.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs +219 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-card.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-drop.mjs +214 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-drop.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-input.mjs +117 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-input.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-list.mjs +32 -0
- package/fesm2022/rt-tools-ui-kit-v2-file-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +91 -0
- package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs +220 -0
- package/fesm2022/rt-tools-ui-kit-v2-form-control.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-header.mjs +72 -0
- package/fesm2022/rt-tools-ui-kit-v2-header.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs +163 -0
- package/fesm2022/rt-tools-ui-kit-v2-icon-button.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs +674 -0
- package/fesm2022/rt-tools-ui-kit-v2-icon.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-image-cropper.mjs +540 -0
- package/fesm2022/rt-tools-ui-kit-v2-image-cropper.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs +227 -0
- package/fesm2022/rt-tools-ui-kit-v2-image-upload.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-info-item.mjs +37 -0
- package/fesm2022/rt-tools-ui-kit-v2-info-item.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs +361 -0
- package/fesm2022/rt-tools-ui-kit-v2-input-number.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-input.mjs +143 -0
- package/fesm2022/rt-tools-ui-kit-v2-input.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-live-badge.mjs +49 -0
- package/fesm2022/rt-tools-ui-kit-v2-live-badge.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-logo.mjs +97 -0
- package/fesm2022/rt-tools-ui-kit-v2-logo.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs +57 -0
- package/fesm2022/rt-tools-ui-kit-v2-markdown-text.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-menu.mjs +537 -0
- package/fesm2022/rt-tools-ui-kit-v2-menu.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs +105 -0
- package/fesm2022/rt-tools-ui-kit-v2-message.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-money-list.mjs +71 -0
- package/fesm2022/rt-tools-ui-kit-v2-money-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +315 -0
- package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-night-grid.mjs +51 -0
- package/fesm2022/rt-tools-ui-kit-v2-night-grid.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-note.mjs +44 -0
- package/fesm2022/rt-tools-ui-kit-v2-note.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs +54 -0
- package/fesm2022/rt-tools-ui-kit-v2-notifications-bell.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +339 -0
- package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs +187 -0
- package/fesm2022/rt-tools-ui-kit-v2-pagination.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-photo-viewer.mjs +170 -0
- package/fesm2022/rt-tools-ui-kit-v2-photo-viewer.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs +312 -0
- package/fesm2022/rt-tools-ui-kit-v2-popover.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs +174 -0
- package/fesm2022/rt-tools-ui-kit-v2-radio-button.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-radius.mjs +56 -0
- package/fesm2022/rt-tools-ui-kit-v2-radius.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +96 -41
- package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
- package/fesm2022/rt-tools-ui-kit-v2-ripple.mjs +118 -0
- package/fesm2022/rt-tools-ui-kit-v2-ripple.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs +290 -0
- package/fesm2022/rt-tools-ui-kit-v2-scroll-area.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs +37 -0
- package/fesm2022/rt-tools-ui-kit-v2-section-nav.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs +495 -0
- package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +1493 -0
- package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-skeleton-wrapper.mjs +49 -0
- package/fesm2022/rt-tools-ui-kit-v2-skeleton-wrapper.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-skeleton.mjs +123 -0
- package/fesm2022/rt-tools-ui-kit-v2-skeleton.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-spinner.mjs +70 -0
- package/fesm2022/rt-tools-ui-kit-v2-spinner.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs +92 -0
- package/fesm2022/rt-tools-ui-kit-v2-split-button.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs +147 -0
- package/fesm2022/rt-tools-ui-kit-v2-stat-tile.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs +117 -0
- package/fesm2022/rt-tools-ui-kit-v2-stepper.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs +1636 -0
- package/fesm2022/rt-tools-ui-kit-v2-table.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs +350 -0
- package/fesm2022/rt-tools-ui-kit-v2-tabs.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +122 -0
- package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs +85 -0
- package/fesm2022/rt-tools-ui-kit-v2-textarea.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs +73 -0
- package/fesm2022/rt-tools-ui-kit-v2-theme-toggle.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs +196 -0
- package/fesm2022/rt-tools-ui-kit-v2-thread-list.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-timeline.mjs +39 -0
- package/fesm2022/rt-tools-ui-kit-v2-timeline.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-toast.mjs +338 -0
- package/fesm2022/rt-tools-ui-kit-v2-toast.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs +132 -0
- package/fesm2022/rt-tools-ui-kit-v2-toggle-button-group.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs +162 -0
- package/fesm2022/rt-tools-ui-kit-v2-toggle-switch.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +135 -0
- package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs +214 -0
- package/fesm2022/rt-tools-ui-kit-v2-tooltip.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-welcome-dialog.mjs +64 -0
- package/fesm2022/rt-tools-ui-kit-v2-welcome-dialog.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs +263 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace-details.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +296 -0
- package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -0
- package/fesm2022/rt-tools-ui-kit-v2.mjs +91 -23164
- package/fesm2022/rt-tools-ui-kit-v2.mjs.map +1 -1
- package/package.json +349 -1
- package/rt-tools-ui-kit-v2-0.18.0.tgz +0 -0
- package/src/lib/components/action-bar/CONTEXT.md +5 -0
- package/src/lib/components/action-bar/rt-action-bar.component.scss +30 -16
- package/src/lib/components/aside/CONTEXT.md +9 -1
- package/src/lib/components/aside/footer/rt-aside-footer.component.scss +7 -8
- package/src/lib/components/aside/header/rt-aside-header.component.scss +16 -1
- package/src/lib/components/aside/rt-aside.component.scss +17 -1
- package/src/lib/components/button/CONTEXT.md +12 -11
- package/src/lib/components/button/rt-button.directive.scss +36 -0
- package/src/lib/components/data-list/settings/rt-data-list-settings-aside.component.scss +2 -2
- package/src/lib/components/data-table/cell/rt-data-table-cell.component.scss +1 -1
- package/src/lib/components/data-table/filter-cell/rt-data-table-filter-cell.component.scss +1 -1
- package/src/lib/components/data-table/rt-data-table.component.scss +1 -1
- package/src/lib/components/dialog/CONTEXT.md +9 -0
- package/src/lib/components/dialog/content/rt-dialog-content.component.scss +20 -0
- package/src/lib/components/dialog/footer/rt-dialog-footer.component.scss +18 -2
- package/src/lib/components/dialog/header/rt-dialog-header.component.scss +21 -3
- package/src/lib/components/dialog/rt-dialog.component.scss +9 -3
- package/src/lib/components/dot-field/CONTEXT.md +31 -0
- package/src/lib/components/dot-field/rt-dot-field.component.scss +20 -0
- package/src/lib/components/dot-field/stories/component/CONTEXT.md +14 -0
- package/src/lib/components/dot-field/stories/component/test-dot-field.component.scss +31 -0
- package/src/lib/components/dynamic-selector/CONTEXT.md +6 -0
- package/src/lib/components/field/CONTEXT.md +18 -2
- package/src/lib/components/header/CONTEXT.md +2 -2
- package/src/lib/components/icon/CONTEXT.md +11 -7
- package/src/lib/components/icon/rt-icon.component.scss +47 -0
- package/src/lib/components/icon-button/CONTEXT.md +27 -20
- package/src/lib/components/icon-button/rt-icon-button.component.scss +51 -25
- package/src/lib/components/image-upload/CONTEXT.md +20 -15
- package/src/lib/components/image-upload/rt-image-upload.component.scss +14 -2
- package/src/lib/components/pagination/rt-pagination.component.scss +1 -1
- package/src/lib/components/scroll-area/CONTEXT.md +2 -0
- package/src/lib/components/scroll-area/rt-scroll-area.component.scss +20 -3
- package/src/lib/components/select/rt-select.component.scss +4 -0
- package/src/lib/components/side-menu/CONTEXT.md +7 -2
- package/src/lib/components/spinner/CONTEXT.md +15 -8
- package/src/lib/components/spinner/rt-spinner.component.scss +108 -9
- package/src/lib/components/tag/rt-tag.component.scss +43 -23
- package/src/lib/components/toast/CONTEXT.md +8 -0
- package/src/lib/components/toast/rt-toast.component.scss +74 -15
- package/src/lib/components/toast/rt-toaster.component.scss +5 -1
- package/src/lib/components/toggle-button-group/rt-toggle-button-group.component.scss +35 -37
- package/src/lib/components/toggle-switch/CONTEXT.md +11 -3
- package/src/lib/components/toggle-switch/rt-toggle-switch.component.scss +53 -14
- package/src/lib/components/toolbar/rt-toolbar.component.scss +15 -4
- package/src/lib/components/tooltip/CONTEXT.md +5 -5
- package/src/rich-editor/lib/components/chat/CONTEXT.md +7 -2
- package/src/rich-editor/lib/components/chat/rt-chat.component.scss +25 -0
- package/src/styles/_semantic.scss +1 -1
- package/styles/tokens.css +43 -8
- package/types/rt-tools-ui-kit-v2-accordion.d.ts +30 -0
- package/types/rt-tools-ui-kit-v2-action-bar.d.ts +140 -0
- package/types/rt-tools-ui-kit-v2-aside-section.d.ts +25 -0
- package/types/rt-tools-ui-kit-v2-aside.d.ts +436 -0
- package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +78 -0
- package/types/rt-tools-ui-kit-v2-bar-list.d.ts +41 -0
- package/types/rt-tools-ui-kit-v2-bottom-sheet.d.ts +20 -0
- package/types/rt-tools-ui-kit-v2-button.d.ts +76 -0
- package/types/rt-tools-ui-kit-v2-calendar.d.ts +96 -0
- package/types/rt-tools-ui-kit-v2-card.d.ts +38 -0
- package/types/rt-tools-ui-kit-v2-checkbox.d.ts +46 -0
- package/types/rt-tools-ui-kit-v2-collapsible-text.d.ts +20 -0
- package/types/rt-tools-ui-kit-v2-confirm-popover.d.ts +85 -0
- package/types/rt-tools-ui-kit-v2-container.d.ts +425 -0
- package/types/rt-tools-ui-kit-v2-core.d.ts +1222 -0
- package/types/rt-tools-ui-kit-v2-counter-row.d.ts +22 -0
- package/types/rt-tools-ui-kit-v2-counter.d.ts +70 -0
- package/types/rt-tools-ui-kit-v2-data-list.d.ts +334 -0
- package/types/rt-tools-ui-kit-v2-data-table.d.ts +494 -0
- package/types/rt-tools-ui-kit-v2-date-picker.d.ts +181 -0
- package/types/rt-tools-ui-kit-v2-date-range.d.ts +98 -0
- package/types/rt-tools-ui-kit-v2-delta-view.d.ts +20 -0
- package/types/rt-tools-ui-kit-v2-detail-list.d.ts +33 -0
- package/types/rt-tools-ui-kit-v2-dialog.d.ts +287 -0
- package/types/rt-tools-ui-kit-v2-dot-field.d.ts +17 -0
- package/types/rt-tools-ui-kit-v2-download-link.d.ts +17 -0
- package/types/rt-tools-ui-kit-v2-dynamic-list.d.ts +139 -0
- package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +384 -0
- package/types/rt-tools-ui-kit-v2-empty-state.d.ts +29 -0
- package/types/rt-tools-ui-kit-v2-expansion-panel.d.ts +57 -0
- package/types/rt-tools-ui-kit-v2-field.d.ts +82 -0
- package/types/rt-tools-ui-kit-v2-file-card.d.ts +114 -0
- package/types/rt-tools-ui-kit-v2-file-drop.d.ts +75 -0
- package/types/rt-tools-ui-kit-v2-file-input.d.ts +48 -0
- package/types/rt-tools-ui-kit-v2-file-list.d.ts +13 -0
- package/types/rt-tools-ui-kit-v2-filter-control.d.ts +72 -0
- package/types/rt-tools-ui-kit-v2-form-control.d.ts +74 -0
- package/types/rt-tools-ui-kit-v2-header.d.ts +40 -0
- package/types/rt-tools-ui-kit-v2-icon-button.d.ts +113 -0
- package/types/rt-tools-ui-kit-v2-icon.d.ts +238 -0
- package/types/rt-tools-ui-kit-v2-image-cropper.d.ts +168 -0
- package/types/rt-tools-ui-kit-v2-image-upload.d.ts +102 -0
- package/types/rt-tools-ui-kit-v2-info-item.d.ts +13 -0
- package/types/rt-tools-ui-kit-v2-input-number.d.ts +67 -0
- package/types/rt-tools-ui-kit-v2-input.d.ts +54 -0
- package/types/rt-tools-ui-kit-v2-live-badge.d.ts +23 -0
- package/types/rt-tools-ui-kit-v2-logo.d.ts +58 -0
- package/types/rt-tools-ui-kit-v2-markdown-text.d.ts +30 -0
- package/types/rt-tools-ui-kit-v2-menu.d.ts +217 -0
- package/types/rt-tools-ui-kit-v2-message.d.ts +56 -0
- package/types/rt-tools-ui-kit-v2-money-list.d.ts +34 -0
- package/types/rt-tools-ui-kit-v2-multiselect.d.ts +93 -0
- package/types/rt-tools-ui-kit-v2-night-grid.d.ts +39 -0
- package/types/rt-tools-ui-kit-v2-note.d.ts +21 -0
- package/types/rt-tools-ui-kit-v2-notifications-bell.d.ts +23 -0
- package/types/rt-tools-ui-kit-v2-page-header.d.ts +173 -0
- package/types/rt-tools-ui-kit-v2-pagination.d.ts +64 -0
- package/types/rt-tools-ui-kit-v2-photo-viewer.d.ts +98 -0
- package/types/rt-tools-ui-kit-v2-popover.d.ts +79 -0
- package/types/rt-tools-ui-kit-v2-radio-button.d.ts +61 -0
- package/types/rt-tools-ui-kit-v2-radius.d.ts +37 -0
- package/types/rt-tools-ui-kit-v2-rich-editor.d.ts +16 -20
- package/types/rt-tools-ui-kit-v2-ripple.d.ts +39 -0
- package/types/rt-tools-ui-kit-v2-scroll-area.d.ts +91 -0
- package/types/rt-tools-ui-kit-v2-section-nav.d.ts +23 -0
- package/types/rt-tools-ui-kit-v2-select.d.ts +215 -0
- package/types/rt-tools-ui-kit-v2-side-menu.d.ts +483 -0
- package/types/rt-tools-ui-kit-v2-skeleton-wrapper.d.ts +32 -0
- package/types/rt-tools-ui-kit-v2-skeleton.d.ts +41 -0
- package/types/rt-tools-ui-kit-v2-spinner.d.ts +56 -0
- package/types/rt-tools-ui-kit-v2-split-button.d.ts +72 -0
- package/types/rt-tools-ui-kit-v2-stat-tile.d.ts +81 -0
- package/types/rt-tools-ui-kit-v2-stepper.d.ts +51 -0
- package/types/rt-tools-ui-kit-v2-table.d.ts +739 -0
- package/types/rt-tools-ui-kit-v2-tabs.d.ts +153 -0
- package/types/rt-tools-ui-kit-v2-tag.d.ts +70 -0
- package/types/rt-tools-ui-kit-v2-textarea.d.ts +36 -0
- package/types/rt-tools-ui-kit-v2-theme-toggle.d.ts +43 -0
- package/types/rt-tools-ui-kit-v2-thread-list.d.ts +111 -0
- package/types/rt-tools-ui-kit-v2-timeline.d.ts +35 -0
- package/types/rt-tools-ui-kit-v2-toast.d.ts +103 -0
- package/types/rt-tools-ui-kit-v2-toggle-button-group.d.ts +109 -0
- package/types/rt-tools-ui-kit-v2-toggle-switch.d.ts +85 -0
- package/types/rt-tools-ui-kit-v2-toolbar.d.ts +66 -0
- package/types/rt-tools-ui-kit-v2-tooltip.d.ts +74 -0
- package/types/rt-tools-ui-kit-v2-welcome-dialog.d.ts +33 -0
- package/types/rt-tools-ui-kit-v2-workspace-details.d.ts +128 -0
- package/types/rt-tools-ui-kit-v2-workspace.d.ts +84 -0
- package/types/rt-tools-ui-kit-v2.d.ts +87 -9290
- package/rt-tools-ui-kit-v2-0.16.0.tgz +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { CdkTrapFocus } from '@angular/cdk/a11y';
|
|
2
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, ElementRef, viewChild, signal, computed, afterNextRender, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
|
+
import { BlockDirective, ElemDirective } from '@rt-tools/core';
|
|
6
|
+
import { RT_KIT_LABELS } from '@rt-tools/ui-kit-v2/core';
|
|
7
|
+
import { RT_DIALOG_DATA, RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';
|
|
8
|
+
import { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Чистая логика просмотрщика: переходы между кадрами и то, какие из них стоит
|
|
12
|
+
* держать загруженными.
|
|
13
|
+
*/
|
|
14
|
+
/** Листание по кругу: с последнего кадра стрелка вправо ведёт на первый */
|
|
15
|
+
function nextPhotoIndex(current, total, step) {
|
|
16
|
+
if (total <= 0) {
|
|
17
|
+
return 0;
|
|
18
|
+
}
|
|
19
|
+
return (((current + step) % total) + total) % total;
|
|
20
|
+
}
|
|
21
|
+
/** Индекс из адреса: в URL кадры нумеруются с единицы, внутри — с нуля */
|
|
22
|
+
function indexFromParam(param, total) {
|
|
23
|
+
const parsed = Number(param);
|
|
24
|
+
if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) {
|
|
25
|
+
return -1;
|
|
26
|
+
}
|
|
27
|
+
return parsed - 1;
|
|
28
|
+
}
|
|
29
|
+
function paramFromIndex(index) {
|
|
30
|
+
return String(index + 1);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Индекс кадра по позиции прокрутки. Нужен, потому что листать можно свайпом:
|
|
34
|
+
* без этого счётчик показывал бы один кадр, а на экране был бы другой.
|
|
35
|
+
*/
|
|
36
|
+
function indexFromScroll(scrollLeft, slideWidth, total) {
|
|
37
|
+
if (slideWidth <= 0 || total <= 0) {
|
|
38
|
+
return 0;
|
|
39
|
+
}
|
|
40
|
+
const index = Math.round(scrollLeft / slideWidth);
|
|
41
|
+
return Math.min(Math.max(index, 0), total - 1);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Кадры, которые держим в разметке: текущий и соседние. Полноразмерная
|
|
45
|
+
* фотография весит сотни килобайт, и загружать все пятьдесят разом — значит
|
|
46
|
+
* выесть трафик пользователя ради кадров, до которых он не долистает.
|
|
47
|
+
*/
|
|
48
|
+
function shouldRenderPhoto(index, current, total) {
|
|
49
|
+
if (total <= 0) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
const distance = Math.min(Math.abs(index - current), total - Math.abs(index - current));
|
|
53
|
+
return distance <= 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const BEM_BLOCK = 'rt-photo-viewer';
|
|
57
|
+
/**
|
|
58
|
+
* Полноэкранный просмотрщик фотографий поверх `RtDialogService`.
|
|
59
|
+
*
|
|
60
|
+
* Листание — нативный `scroll-snap`: свайп, инерцию и прилипание даёт сам
|
|
61
|
+
* браузер, и на телефоне это ощущается правильнее любой реализации на
|
|
62
|
+
* обработчиках. Готовую библиотеку не берём — весь кит здесь собственный, а
|
|
63
|
+
* токены, BEM и `qa-dataid` ложатся естественно.
|
|
64
|
+
*
|
|
65
|
+
* Руками сделаны: стрелки, клавиши `←` `→`, счётчик и предзагрузка соседних
|
|
66
|
+
* кадров. `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог.
|
|
67
|
+
*/
|
|
68
|
+
class RtPhotoViewerComponent {
|
|
69
|
+
#data;
|
|
70
|
+
#dialogRef;
|
|
71
|
+
#host;
|
|
72
|
+
constructor() {
|
|
73
|
+
this.#data = inject(RT_DIALOG_DATA);
|
|
74
|
+
this.#dialogRef = inject((RtDialogRef));
|
|
75
|
+
this.#host = inject((ElementRef));
|
|
76
|
+
this.t = inject(RT_KIT_LABELS);
|
|
77
|
+
this.track = viewChild('track', /* @ts-ignore */
|
|
78
|
+
...(ngDevMode ? [{ debugName: "track" }] : /* istanbul ignore next */ []));
|
|
79
|
+
this.photos = this.#data.photos;
|
|
80
|
+
/** Панель правки под дорожкой; там, где кадр только смотрят, её нет */
|
|
81
|
+
this.panel = this.#data.panel ?? null;
|
|
82
|
+
this.currentIndex = signal(this.#data.startIndex, /* @ts-ignore */
|
|
83
|
+
...(ngDevMode ? [{ debugName: "currentIndex" }] : /* istanbul ignore next */ []));
|
|
84
|
+
this.counter = computed(() => `${this.currentIndex() + 1} / ${this.photos.length}`, /* @ts-ignore */
|
|
85
|
+
...(ngDevMode ? [{ debugName: "counter" }] : /* istanbul ignore next */ []));
|
|
86
|
+
/** Соседние кадры готовятся заранее, дальние в разметку не попадают */
|
|
87
|
+
this.rendered = computed(() => this.photos.map((_photo, index) => shouldRenderPhoto(index, this.currentIndex(), this.photos.length)), /* @ts-ignore */
|
|
88
|
+
...(ngDevMode ? [{ debugName: "rendered" }] : /* istanbul ignore next */ []));
|
|
89
|
+
// Открытие может быть не с первого кадра: без явной прокрутки счётчик
|
|
90
|
+
// показывал бы второй кадр, а на экране остался бы первый
|
|
91
|
+
afterNextRender(() => {
|
|
92
|
+
this.#host.nativeElement.focus();
|
|
93
|
+
this.#scrollToCurrent();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/** Наружу отдаётся кадр, на котором закрыли: по нему вызывающая сторона чистит адрес */
|
|
97
|
+
close() {
|
|
98
|
+
this.#dialogRef.close(this.currentIndex());
|
|
99
|
+
}
|
|
100
|
+
move(step) {
|
|
101
|
+
this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));
|
|
102
|
+
}
|
|
103
|
+
select(index) {
|
|
104
|
+
this.currentIndex.set(index);
|
|
105
|
+
this.#scrollToCurrent();
|
|
106
|
+
}
|
|
107
|
+
/** Свайп листает мимо стрелок — счётчик догоняет позицию дорожки */
|
|
108
|
+
onScroll() {
|
|
109
|
+
const track = this.track()?.nativeElement;
|
|
110
|
+
if (track) {
|
|
111
|
+
this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Прокрутка ленты вслед за выбором. Позиция считается от ширины дорожки, а
|
|
116
|
+
* не от `scrollWidth`: кадры за пределами окна ещё не отрисованы, и общая
|
|
117
|
+
* ширина до их появления меньше реальной.
|
|
118
|
+
*
|
|
119
|
+
* Переход мгновенный: `behavior: 'smooth'` внутри оверлея молча не двигает
|
|
120
|
+
* дорожку — счётчик менялся, а кадр оставался прежним. Свайп при этом
|
|
121
|
+
* по-прежнему анимирует сам браузер через scroll-snap.
|
|
122
|
+
*/
|
|
123
|
+
#scrollToCurrent() {
|
|
124
|
+
const track = this.track()?.nativeElement;
|
|
125
|
+
if (!track) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });
|
|
129
|
+
}
|
|
130
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPhotoViewerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
131
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtPhotoViewerComponent, isStandalone: true, selector: "rt-photo-viewer", host: { attributes: { "role": "dialog", "aria-modal": "true", "tabindex": "-1" }, listeners: { "keydown.escape": "close()", "keydown.arrowLeft": "move(-1)", "keydown.arrowRight": "move(1)" }, classAttribute: "rt-photo-viewer" }, viewQueries: [{ propertyName: "track", first: true, predicate: ["track"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"], dependencies: [{ kind: "directive", type:
|
|
132
|
+
// angular
|
|
133
|
+
CdkTrapFocus, selector: "[cdkTrapFocus]", inputs: ["cdkTrapFocus", "cdkTrapFocusAutoCapture"], exportAs: ["cdkTrapFocus"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type:
|
|
134
|
+
// rt-tools
|
|
135
|
+
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "component", type:
|
|
136
|
+
// components
|
|
137
|
+
RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
138
|
+
}
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPhotoViewerComponent, decorators: [{
|
|
140
|
+
type: Component,
|
|
141
|
+
args: [{ selector: 'rt-photo-viewer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
142
|
+
// angular
|
|
143
|
+
CdkTrapFocus,
|
|
144
|
+
NgTemplateOutlet,
|
|
145
|
+
// rt-tools
|
|
146
|
+
BlockDirective,
|
|
147
|
+
ElemDirective,
|
|
148
|
+
// components
|
|
149
|
+
RtIconButtonComponent,
|
|
150
|
+
// transloco
|
|
151
|
+
], host: {
|
|
152
|
+
class: BEM_BLOCK,
|
|
153
|
+
role: 'dialog',
|
|
154
|
+
'aria-modal': 'true',
|
|
155
|
+
// Клавиши слушает сам просмотрщик, поэтому фокус переводится на него
|
|
156
|
+
// сразу после открытия — иначе стрелки и Esc не работали бы, пока
|
|
157
|
+
// пользователь куда-нибудь не кликнет
|
|
158
|
+
tabindex: '-1',
|
|
159
|
+
'(keydown.escape)': 'close()',
|
|
160
|
+
'(keydown.arrowLeft)': 'move(-1)',
|
|
161
|
+
'(keydown.arrowRight)': 'move(1)',
|
|
162
|
+
}, template: "<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- \u041B\u043E\u0432\u0443\u0448\u043A\u0430 \u0434\u0435\u0440\u0436\u0438\u0442 Tab \u0432\u043D\u0443\u0442\u0440\u0438; \u0444\u043E\u043A\u0443\u0441 \u0441\u0442\u0430\u0432\u0438\u0442 \u0441\u0430\u043C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442, \u043F\u043E\u044D\u0442\u043E\u043C\u0443\n \u0430\u0432\u0442\u043E-\u0437\u0430\u0445\u0432\u0430\u0442\u0430 \u0437\u0434\u0435\u0441\u044C \u043D\u0435\u0442: \u043E\u043D \u043F\u0435\u0440\u0435\u0431\u0438\u0432\u0430\u043B \u0444\u043E\u043A\u0443\u0441 \u043D\u0430 \u043F\u0440\u043E\u0441\u043C\u043E\u0442\u0440\u0449\u0438\u043A\u0435, \u0438 \u043A\u043B\u0430\u0432\u0438\u0448\u0438\n \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u0430\u043B\u0438 \u043B\u0438\u0441\u0442\u0430\u0442\u044C -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- \u041F\u0430\u043D\u0435\u043B\u044C \u043F\u0440\u0438\u0445\u043E\u0434\u0438\u0442 \u0441\u043D\u0430\u0440\u0443\u0436\u0438: \u0433\u0434\u0435-\u0442\u043E \u043A\u0430\u0434\u0440 \u0442\u043E\u043B\u044C\u043A\u043E \u0441\u043C\u043E\u0442\u0440\u044F\u0442, \u0433\u0434\u0435-\u0442\u043E \u0438\u043C \u0435\u0449\u0451 \u0438\n \u0443\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0442, \u0438 \u0440\u0435\u0448\u0435\u043D\u0438\u0435 \u043F\u0440\u0438\u043D\u0438\u043C\u0430\u0435\u0442\u0441\u044F \u0442\u0430\u043C \u0436\u0435, \u0433\u0434\u0435 \u043A\u0430\u0434\u0440 \u0432\u0438\u0434\u0435\u043D \u043A\u0440\u0443\u043F\u043D\u043E -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-photo-viewer{--rt-photo-viewer-control-radius: var(--rt-radius-md);--rt-photo-viewer-control-color: var(--rt-color-text-inverse);--rt-photo-viewer-control-bg: color-mix(in srgb, var(--rt-color-bg-inverse) 55%, transparent);--rt-photo-viewer-control-bg-hover: color-mix(in srgb, var(--rt-color-text-inverse) 24%, transparent);display:block;width:100vw;height:100vh;height:100dvh;background:var(--rt-color-bg-inverse)}.rt-photo-viewer__surface{position:absolute;inset:0}.rt-photo-viewer__bar{position:absolute;z-index:1;top:0;right:0;left:0;display:flex;align-items:center;justify-content:space-between;padding:var(--rt-space-4) var(--rt-space-5);color:var(--rt-color-text-inverse);gap:var(--rt-space-4)}.rt-photo-viewer__counter{font-size:var(--rt-text-sm);font-variant-numeric:tabular-nums}.rt-photo-viewer__track{display:flex;overflow:auto hidden;width:100%;height:100%;scroll-snap-type:x mandatory;scrollbar-width:none}.rt-photo-viewer__slide{display:flex;flex:0 0 100%;align-items:center;justify-content:center;padding:var(--rt-space-10) var(--rt-space-4);margin:0;scroll-snap-align:center}.rt-photo-viewer__slide picture{display:flex;width:100%;height:100%;align-items:center;justify-content:center}.rt-photo-viewer__image{display:block;max-width:100%;max-height:100%;object-fit:contain}.rt-photo-viewer__panel{position:absolute;z-index:1;right:0;bottom:0;left:0;padding:var(--rt-space-4) var(--rt-space-5);background:var(--rt-color-bg-surface);color:var(--rt-color-text-primary)}.rt-photo-viewer__bar rt-icon-button,.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{--rt-icon-button-color: var(--rt-photo-viewer-control-color);--rt-icon-button-color-bg-hover: var(--rt-photo-viewer-control-bg-hover);border-radius:var(--rt-photo-viewer-control-radius);background:var(--rt-photo-viewer-control-bg)}.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{position:absolute;top:50%;transform:translateY(-50%)}.rt-photo-viewer__arrow-prev{left:var(--rt-space-4)}.rt-photo-viewer__arrow-next{right:var(--rt-space-4)}.rt-photo-viewer:has(.rt-photo-viewer__panel) .rt-photo-viewer__slide{padding-bottom:var(--rt-space-20)}@media(width<=700px){.rt-photo-viewer__arrow-prev,.rt-photo-viewer__arrow-next{display:none}.rt-photo-viewer__slide{padding:var(--rt-space-12) 0}}}\n"] }]
|
|
163
|
+
}], ctorParameters: () => [], propDecorators: { track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generated bundle index. Do not edit.
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
export { RtPhotoViewerComponent, indexFromParam, indexFromScroll, nextPhotoIndex, paramFromIndex, shouldRenderPhoto };
|
|
170
|
+
//# sourceMappingURL=rt-tools-ui-kit-v2-photo-viewer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-photo-viewer.mjs","sources":["../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.logic.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.ts","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-photo-viewer.component.html","../../../projects/ui-kit-v2/src/lib/components/photo-viewer/rt-tools-ui-kit-v2-photo-viewer.ts"],"sourcesContent":["/**\n * Чистая логика просмотрщика: переходы между кадрами и то, какие из них стоит\n * держать загруженными.\n */\n\n/** Листание по кругу: с последнего кадра стрелка вправо ведёт на первый */\nexport function nextPhotoIndex(current: number, total: number, step: number): number {\n if (total <= 0) {\n return 0;\n }\n return (((current + step) % total) + total) % total;\n}\n\n/** Индекс из адреса: в URL кадры нумеруются с единицы, внутри — с нуля */\nexport function indexFromParam(param: string | null, total: number): number {\n const parsed: number = Number(param);\n if (!Number.isInteger(parsed) || parsed < 1 || parsed > total) {\n return -1;\n }\n return parsed - 1;\n}\n\nexport function paramFromIndex(index: number): string {\n return String(index + 1);\n}\n\n/**\n * Индекс кадра по позиции прокрутки. Нужен, потому что листать можно свайпом:\n * без этого счётчик показывал бы один кадр, а на экране был бы другой.\n */\nexport function indexFromScroll(scrollLeft: number, slideWidth: number, total: number): number {\n if (slideWidth <= 0 || total <= 0) {\n return 0;\n }\n const index: number = Math.round(scrollLeft / slideWidth);\n return Math.min(Math.max(index, 0), total - 1);\n}\n\n/**\n * Кадры, которые держим в разметке: текущий и соседние. Полноразмерная\n * фотография весит сотни килобайт, и загружать все пятьдесят разом — значит\n * выесть трафик пользователя ради кадров, до которых он не долистает.\n */\nexport function shouldRenderPhoto(index: number, current: number, total: number): boolean {\n if (total <= 0) {\n return false;\n }\n const distance: number = Math.min(Math.abs(index - current), total - Math.abs(index - current));\n return distance <= 1;\n}\n","import { CdkTrapFocus } from '@angular/cdk/a11y';\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n afterNextRender,\n computed,\n inject,\n signal,\n viewChild,\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n Signal,\n TemplateRef,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\n// rt-tools\nimport { BlockDirective, ElemDirective } from '@rt-tools/core';\n\nimport { RT_KIT_LABELS, TRtKitLabelMap } from '@rt-tools/ui-kit-v2/core';\nimport { RtDialogRef } from '@rt-tools/ui-kit-v2/dialog';\nimport { RT_DIALOG_DATA } from '@rt-tools/ui-kit-v2/dialog';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { indexFromScroll, nextPhotoIndex, shouldRenderPhoto } from './rt-photo-viewer.logic';\nimport { IRtPhotoViewer } from './rt-photo-viewer.model';\n\nconst BEM_BLOCK: string = 'rt-photo-viewer';\n\n/**\n * Полноэкранный просмотрщик фотографий поверх `RtDialogService`.\n *\n * Листание — нативный `scroll-snap`: свайп, инерцию и прилипание даёт сам\n * браузер, и на телефоне это ощущается правильнее любой реализации на\n * обработчиках. Готовую библиотеку не берём — весь кит здесь собственный, а\n * токены, BEM и `qa-dataid` ложатся естественно.\n *\n * Руками сделаны: стрелки, клавиши `←` `→`, счётчик и предзагрузка соседних\n * кадров. `Esc`, блокировку прокрутки фона и затемнение даёт сам диалог.\n */\n@Component({\n selector: 'rt-photo-viewer',\n templateUrl: './rt-photo-viewer.component.html',\n styleUrl: './rt-photo-viewer.component.scss',\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n // angular\n CdkTrapFocus,\n NgTemplateOutlet,\n\n // rt-tools\n BlockDirective,\n ElemDirective,\n\n // components\n RtIconButtonComponent,\n\n // transloco\n ],\n host: {\n class: BEM_BLOCK,\n role: 'dialog',\n 'aria-modal': 'true',\n // Клавиши слушает сам просмотрщик, поэтому фокус переводится на него\n // сразу после открытия — иначе стрелки и Esc не работали бы, пока\n // пользователь куда-нибудь не кликнет\n tabindex: '-1',\n '(keydown.escape)': 'close()',\n '(keydown.arrowLeft)': 'move(-1)',\n '(keydown.arrowRight)': 'move(1)',\n },\n})\nexport class RtPhotoViewerComponent {\n readonly #data: IRtPhotoViewer.Data = inject(RT_DIALOG_DATA) as IRtPhotoViewer.Data;\n readonly #dialogRef: RtDialogRef<number> = inject(RtDialogRef<number>);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef<HTMLElement>);\n\n protected readonly t: Signal<TRtKitLabelMap> = inject(RT_KIT_LABELS);\n\n protected readonly track: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('track');\n\n protected readonly photos: readonly IRtPhotoViewer.Photo[] = this.#data.photos;\n\n /** Панель правки под дорожкой; там, где кадр только смотрят, её нет */\n protected readonly panel: TemplateRef<IRtPhotoViewer.PanelContext> | null = this.#data.panel ?? null;\n\n protected readonly currentIndex: WritableSignal<number> = signal<number>(this.#data.startIndex);\n\n protected readonly counter: Signal<string> = computed((): string => `${this.currentIndex() + 1} / ${this.photos.length}`);\n\n /** Соседние кадры готовятся заранее, дальние в разметку не попадают */\n protected readonly rendered: Signal<readonly boolean[]> = computed((): readonly boolean[] =>\n this.photos.map((_photo: IRtPhotoViewer.Photo, index: number): boolean =>\n shouldRenderPhoto(index, this.currentIndex(), this.photos.length)\n )\n );\n\n constructor() {\n // Открытие может быть не с первого кадра: без явной прокрутки счётчик\n // показывал бы второй кадр, а на экране остался бы первый\n afterNextRender((): void => {\n this.#host.nativeElement.focus();\n this.#scrollToCurrent();\n });\n }\n\n /** Наружу отдаётся кадр, на котором закрыли: по нему вызывающая сторона чистит адрес */\n public close(): void {\n this.#dialogRef.close(this.currentIndex());\n }\n\n protected move(step: number): void {\n this.select(nextPhotoIndex(this.currentIndex(), this.photos.length, step));\n }\n\n protected select(index: number): void {\n this.currentIndex.set(index);\n this.#scrollToCurrent();\n }\n\n /** Свайп листает мимо стрелок — счётчик догоняет позицию дорожки */\n protected onScroll(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (track) {\n this.currentIndex.set(indexFromScroll(track.scrollLeft, track.clientWidth, this.photos.length));\n }\n }\n\n /**\n * Прокрутка ленты вслед за выбором. Позиция считается от ширины дорожки, а\n * не от `scrollWidth`: кадры за пределами окна ещё не отрисованы, и общая\n * ширина до их появления меньше реальной.\n *\n * Переход мгновенный: `behavior: 'smooth'` внутри оверлея молча не двигает\n * дорожку — счётчик менялся, а кадр оставался прежним. Свайп при этом\n * по-прежнему анимирует сам браузер через scroll-snap.\n */\n #scrollToCurrent(): void {\n const track: HTMLElement | undefined = this.track()?.nativeElement;\n if (!track) {\n return;\n }\n track.scrollTo({ left: track.clientWidth * this.currentIndex(), behavior: 'instant' });\n }\n}\n","<ng-container rtBlock=\"rt-photo-viewer\">\n <!-- Ловушка держит Tab внутри; фокус ставит сам компонент, поэтому\n авто-захвата здесь нет: он перебивал фокус на просмотрщике, и клавиши\n переставали листать -->\n <div rtElem=\"surface\" cdkTrapFocus>\n <header rtElem=\"bar\">\n <span rtElem=\"counter\" qa-dataid=\"photo-viewer-counter\">{{ counter() }}</span>\n <rt-icon-button\n qa-dataid=\"photo-viewer-close\"\n icon=\"close\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerClose\"\n (clicked)=\"close()\" />\n </header>\n\n <div rtElem=\"track\" qa-dataid=\"photo-viewer-track\" #track (scroll)=\"onScroll()\">\n @for (photo of photos; track photo.id; let index = $index) {\n <figure rtElem=\"slide\" [attr.data-photo-index]=\"index\">\n @if (rendered()[index]) {\n <picture>\n @for (source of photo.picture.sources; track source.type) {\n <source sizes=\"100vw\" [type]=\"source.type\" [srcset]=\"source.srcset\" />\n }\n <img\n rtElem=\"image\"\n qa-dataid=\"photo-viewer-image\"\n decoding=\"async\"\n sizes=\"100vw\"\n [srcset]=\"photo.picture.fallbackSrcset\"\n [src]=\"photo.picture.fallbackSrc\"\n [alt]=\"photo.alt\" />\n </picture>\n }\n </figure>\n }\n </div>\n\n @if (photos.length > 1) {\n <rt-icon-button\n rtElem=\"arrow-prev\"\n qa-dataid=\"photo-viewer-prev\"\n icon=\"angle-left\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerPrev\"\n (clicked)=\"move(-1)\" />\n\n <rt-icon-button\n rtElem=\"arrow-next\"\n qa-dataid=\"photo-viewer-next\"\n icon=\"angle-right\"\n variant=\"ghost\"\n [ariaLabel]=\"t().photoViewerNext\"\n (clicked)=\"move(1)\" />\n }\n\n <!-- Панель приходит снаружи: где-то кадр только смотрят, где-то им ещё и\n управляют, и решение принимается там же, где кадр виден крупно -->\n @if (panel) {\n <footer rtElem=\"panel\" qa-dataid=\"photo-viewer-panel\">\n <ng-container\n [ngTemplateOutlet]=\"panel\"\n [ngTemplateOutletContext]=\"{ $implicit: photos[currentIndex()], index: currentIndex() }\" />\n </footer>\n }\n </div>\n</ng-container>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { Overlay, ScrollStrategyOptions, OverlayConfig } from '@angular/cdk/overlay';
|
|
2
|
+
import { TemplatePortal } from '@angular/cdk/portal';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { inject, ElementRef, ViewContainerRef, DestroyRef, signal, input, booleanAttribute, numberAttribute, output, Directive } from '@angular/core';
|
|
5
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
6
|
+
import { Subject, switchMap, merge, map } from 'rxjs';
|
|
7
|
+
import { carryThemeScope, materialPresetClassesOf } from '@rt-tools/ui-kit-v2/core';
|
|
8
|
+
|
|
9
|
+
const HOVER_CLOSE_DELAY_MS = 100;
|
|
10
|
+
/**
|
|
11
|
+
* Высота, ниже которой ужимать панель под вьюпорт бессмысленно: в такой полосе не
|
|
12
|
+
* видно и одного пункта. Нужна только режиму `fitViewport` — по ней CDK решает,
|
|
13
|
+
* что свободного места под триггером хватает.
|
|
14
|
+
*/
|
|
15
|
+
const FIT_VIEWPORT_MIN_HEIGHT_PX = 120;
|
|
16
|
+
/**
|
|
17
|
+
* Переиспользуемый popover-примитив поверх CDK Overlay. Привязывает плавающую
|
|
18
|
+
* панель (`<ng-template>`) к host-элементу. Движок — CDK Overlay
|
|
19
|
+
* (`flexibleConnectedTo`): авто-flip у края viewport, reposition при скролле,
|
|
20
|
+
* outside-click и Escape закрытие из коробки — без ручного `position: absolute`
|
|
21
|
+
* в `:host` (тот резался `overflow: hidden` предками: таблицы, aside, карточки).
|
|
22
|
+
*
|
|
23
|
+
* Триггеры:
|
|
24
|
+
* - `click` (default) — toggle по клику на host; outside-click закрывает.
|
|
25
|
+
* - `hover` — открытие на `mouseenter`, закрытие с grace-периодом на `mouseleave`
|
|
26
|
+
* (наведение на панель удерживает её открытой).
|
|
27
|
+
* - `manual` — авто-listener'ы не вешаются; host сам зовёт `open()/close()`.
|
|
28
|
+
* Используется form-control'ами с собственным жестом (autocomplete — открытие
|
|
29
|
+
* по набору текста).
|
|
30
|
+
*
|
|
31
|
+
* `open()/close()/toggle()/isOpen()` публичны во ВСЕХ режимах — `trigger` лишь
|
|
32
|
+
* определяет авто-listener'ы. `width: 'trigger'` синхронизирует ширину панели с
|
|
33
|
+
* host'ом (dropdown'ы), `'trigger-min'` не даёт панели стать уже host'а и дальше пускает
|
|
34
|
+
* её по содержимому, `'auto'` — content-sized (тултипы).
|
|
35
|
+
*/
|
|
36
|
+
class RtPopoverDirective {
|
|
37
|
+
#overlay;
|
|
38
|
+
#elementRef;
|
|
39
|
+
#viewContainerRef;
|
|
40
|
+
#scrollStrategies;
|
|
41
|
+
#destroyRef;
|
|
42
|
+
#overlayCreatedSource;
|
|
43
|
+
#overlayRef;
|
|
44
|
+
#hoverCloseTimer;
|
|
45
|
+
constructor() {
|
|
46
|
+
this.#overlay = inject(Overlay);
|
|
47
|
+
this.#elementRef = inject(ElementRef);
|
|
48
|
+
this.#viewContainerRef = inject(ViewContainerRef);
|
|
49
|
+
this.#scrollStrategies = inject(ScrollStrategyOptions);
|
|
50
|
+
this.#destroyRef = inject(DestroyRef);
|
|
51
|
+
this.#overlayCreatedSource = new Subject();
|
|
52
|
+
this.#overlayRef = null;
|
|
53
|
+
this.#hoverCloseTimer = null;
|
|
54
|
+
this.isOpenState = signal(false, /* @ts-ignore */
|
|
55
|
+
...(ngDevMode ? [{ debugName: "isOpenState" }] : /* istanbul ignore next */ []));
|
|
56
|
+
this.template = input(null, { ...(ngDevMode ? { debugName: "template" } : /* istanbul ignore next */ {}), alias: 'rtPopover' });
|
|
57
|
+
this.trigger = input('click', { ...(ngDevMode ? { debugName: "trigger" } : /* istanbul ignore next */ {}), alias: 'rtPopoverTrigger' });
|
|
58
|
+
this.width = input('auto', { ...(ngDevMode ? { debugName: "width" } : /* istanbul ignore next */ {}), alias: 'rtPopoverWidth' });
|
|
59
|
+
/** `end` — панель прижата к правому краю host'а (триггеры у правого края viewport). */
|
|
60
|
+
this.align = input('start', { ...(ngDevMode ? { debugName: "align" } : /* istanbul ignore next */ {}), alias: 'rtPopoverAlign' });
|
|
61
|
+
/**
|
|
62
|
+
* Панель обязана уместиться во вьюпорт: не влезающая по ширине сдвигается от
|
|
63
|
+
* края экрана внутрь, не влезающая по высоте ужимается до свободного места под
|
|
64
|
+
* триггером, и содержимое в ней прокручивается.
|
|
65
|
+
*
|
|
66
|
+
* По умолчанию выключено: панель стоит ровно там, куда её ставит привязка к
|
|
67
|
+
* триггеру, даже если часть её ушла за экран. Включать там, где панель широкая
|
|
68
|
+
* или высокая настолько, что привязка сама по себе её не удерживает, — иначе
|
|
69
|
+
* пункты навигации остаются за краем экрана и недостижимы.
|
|
70
|
+
*/
|
|
71
|
+
this.fitViewport = input(false, { ...(ngDevMode ? { debugName: "fitViewport" } : /* istanbul ignore next */ {}), alias: 'rtPopoverFitViewport',
|
|
72
|
+
transform: booleanAttribute });
|
|
73
|
+
this.context = input(null, { ...(ngDevMode ? { debugName: "context" } : /* istanbul ignore next */ {}), alias: 'rtPopoverContext' });
|
|
74
|
+
this.panelClass = input('', { ...(ngDevMode ? { debugName: "panelClass" } : /* istanbul ignore next */ {}), alias: 'rtPopoverPanelClass' });
|
|
75
|
+
this.disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), alias: 'rtPopoverDisabled',
|
|
76
|
+
transform: booleanAttribute });
|
|
77
|
+
this.offsetY = input(4, { ...(ngDevMode ? { debugName: "offsetY" } : /* istanbul ignore next */ {}), alias: 'rtPopoverOffsetY',
|
|
78
|
+
transform: numberAttribute });
|
|
79
|
+
this.offsetX = input(0, { ...(ngDevMode ? { debugName: "offsetX" } : /* istanbul ignore next */ {}), alias: 'rtPopoverOffsetX',
|
|
80
|
+
transform: numberAttribute });
|
|
81
|
+
this.opened = output({ alias: 'rtPopoverOpened' });
|
|
82
|
+
this.closed = output({ alias: 'rtPopoverClosed' });
|
|
83
|
+
this.isOpen = this.isOpenState.asReadonly();
|
|
84
|
+
// Outside-click / ESC подписки на единожды создаваемый overlay живут в
|
|
85
|
+
// одном конструкторном стриме: #ensureOverlay эмитит OverlayRef после
|
|
86
|
+
// создания (ровно один раз за жизнь директивы), стрим живёт до destroy.
|
|
87
|
+
this.#overlayCreatedSource
|
|
88
|
+
.pipe(switchMap((overlayRef) => merge(overlayRef.outsidePointerEvents().pipe(map((event) => () => {
|
|
89
|
+
// Клик по host в режиме click обрабатывает onHostClick (toggle) —
|
|
90
|
+
// не закрываем здесь, иначе двойной toggle (close + open).
|
|
91
|
+
if (this.#elementRef.nativeElement.contains(event.target)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
// Клик по вложенной панели — не клик снаружи: закрытие
|
|
95
|
+
// разрушило бы её вместе с нашей до того, как выбор
|
|
96
|
+
// дойдёт до обработчика
|
|
97
|
+
if (this.#isNestedOverlay(event.target)) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
this.close();
|
|
101
|
+
})), overlayRef.keydownEvents().pipe(map((event) => () => {
|
|
102
|
+
if (event.key === 'Escape') {
|
|
103
|
+
event.preventDefault();
|
|
104
|
+
this.close();
|
|
105
|
+
}
|
|
106
|
+
})))), takeUntilDestroyed(this.#destroyRef))
|
|
107
|
+
.subscribe((handleCloseIntent) => handleCloseIntent());
|
|
108
|
+
}
|
|
109
|
+
ngOnDestroy() {
|
|
110
|
+
this.#clearHoverTimer();
|
|
111
|
+
this.#disposeOverlay();
|
|
112
|
+
}
|
|
113
|
+
open() {
|
|
114
|
+
if (this.disabled() || this.isOpenState() || this.template() === null) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const overlayRef = this.#ensureOverlay();
|
|
118
|
+
const hostWidth = this.#elementRef.nativeElement.offsetWidth;
|
|
119
|
+
const sizeByWidth = {
|
|
120
|
+
trigger: { width: hostWidth },
|
|
121
|
+
'trigger-min': { minWidth: hostWidth },
|
|
122
|
+
auto: {},
|
|
123
|
+
};
|
|
124
|
+
overlayRef.updateSize(sizeByWidth[this.width()]);
|
|
125
|
+
// Тема куска `rtTheme` вокруг host'а едет на коробку панели: панель лежит в конце
|
|
126
|
+
// страницы, и без этого список из тёмной карточки рисовался темой страницы.
|
|
127
|
+
carryThemeScope(overlayRef.overlayElement, this.#elementRef.nativeElement);
|
|
128
|
+
const portal = new TemplatePortal(this.template(), this.#viewContainerRef, {
|
|
129
|
+
$implicit: this.context(),
|
|
130
|
+
});
|
|
131
|
+
overlayRef.attach(portal);
|
|
132
|
+
this.isOpenState.set(true);
|
|
133
|
+
this.opened.emit();
|
|
134
|
+
}
|
|
135
|
+
close() {
|
|
136
|
+
if (!this.isOpenState()) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.#overlayRef?.detach();
|
|
140
|
+
this.isOpenState.set(false);
|
|
141
|
+
this.closed.emit();
|
|
142
|
+
}
|
|
143
|
+
toggle() {
|
|
144
|
+
if (this.isOpenState()) {
|
|
145
|
+
this.close();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.open();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// --- авто-triggers (HostListener'ы навешиваются на host безусловно, но
|
|
152
|
+
// проверяют активный режим — это дешевле, чем условная подписка) ---
|
|
153
|
+
onHostClick() {
|
|
154
|
+
if (this.trigger() !== 'click' || this.disabled()) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.toggle();
|
|
158
|
+
}
|
|
159
|
+
onHostMouseEnter() {
|
|
160
|
+
if (this.trigger() !== 'hover') {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
this.#clearHoverTimer();
|
|
164
|
+
this.open();
|
|
165
|
+
}
|
|
166
|
+
onHostMouseLeave(event) {
|
|
167
|
+
if (this.trigger() !== 'hover') {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (this.#isNestedOverlay(event.relatedTarget)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
this.#scheduleHoverClose();
|
|
174
|
+
}
|
|
175
|
+
#ensureOverlay() {
|
|
176
|
+
if (this.#overlayRef !== null) {
|
|
177
|
+
return this.#overlayRef;
|
|
178
|
+
}
|
|
179
|
+
const alignX = this.align();
|
|
180
|
+
const positions = [
|
|
181
|
+
{
|
|
182
|
+
originX: alignX,
|
|
183
|
+
originY: 'bottom',
|
|
184
|
+
overlayX: alignX,
|
|
185
|
+
overlayY: 'top',
|
|
186
|
+
offsetX: this.offsetX(),
|
|
187
|
+
offsetY: this.offsetY(),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
originX: alignX,
|
|
191
|
+
originY: 'top',
|
|
192
|
+
overlayX: alignX,
|
|
193
|
+
overlayY: 'bottom',
|
|
194
|
+
offsetX: this.offsetX(),
|
|
195
|
+
offsetY: -this.offsetY(),
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
// Сдвиг к краю экрана и ужатие по высоте включаются только по просьбе
|
|
199
|
+
// потребителя: панель, которая и так помещается, обязана стоять у своего
|
|
200
|
+
// триггера — меню строки таблицы, подтверждение, меню профиля.
|
|
201
|
+
const fitViewport = this.fitViewport();
|
|
202
|
+
const positionStrategy = this.#overlay
|
|
203
|
+
.position()
|
|
204
|
+
.flexibleConnectedTo(this.#elementRef)
|
|
205
|
+
.withPush(fitViewport)
|
|
206
|
+
.withFlexibleDimensions(fitViewport)
|
|
207
|
+
// Отступ от края экрана равен отступу от триггера: свободное место
|
|
208
|
+
// считается без него, и ужатая по высоте панель свисала бы за нижний
|
|
209
|
+
// край ровно на этот отступ — вместе со своей рамкой.
|
|
210
|
+
.withViewportMargin(fitViewport ? Math.abs(this.offsetY()) : 0)
|
|
211
|
+
.withPositions(positions);
|
|
212
|
+
const config = new OverlayConfig({
|
|
213
|
+
positionStrategy,
|
|
214
|
+
scrollStrategy: this.#scrollStrategies.reposition(),
|
|
215
|
+
hasBackdrop: false,
|
|
216
|
+
panelClass: this.#resolvePanelClasses(),
|
|
217
|
+
// Порог, ниже которого свободного места под триггером считается
|
|
218
|
+
// недостаточно и панель не ужимается, а сдвигается целиком.
|
|
219
|
+
minHeight: fitViewport ? FIT_VIEWPORT_MIN_HEIGHT_PX : undefined,
|
|
220
|
+
});
|
|
221
|
+
const overlayRef = this.#overlay.create(config);
|
|
222
|
+
// Hover-bridge: при переходе курсора с host'а на панель отложенный close
|
|
223
|
+
// (grace-период) гасится; уход с панели планирует его заново. Без этого
|
|
224
|
+
// hover-popover закрывался бы по таймеру, как только мышь покидает host.
|
|
225
|
+
// Listeners живут на overlay-элементе и умирают вместе с ним в dispose().
|
|
226
|
+
overlayRef.overlayElement.addEventListener('mouseenter', () => {
|
|
227
|
+
if (this.trigger() === 'hover') {
|
|
228
|
+
this.#clearHoverTimer();
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
overlayRef.overlayElement.addEventListener('mouseleave', (event) => {
|
|
232
|
+
if (this.trigger() === 'hover' && !this.#isNestedOverlay(event.relatedTarget)) {
|
|
233
|
+
this.#scheduleHoverClose();
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
// Outside-click / ESC подписки объявлены один раз в конструкторе —
|
|
237
|
+
// здесь только эмит созданного OverlayRef (см. #overlayCreatedSource-стрим).
|
|
238
|
+
this.#overlayCreatedSource.next(overlayRef);
|
|
239
|
+
this.#overlayRef = overlayRef;
|
|
240
|
+
return overlayRef;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Панель, открытая из нашей — список `rt-select`, календарь, вложенное меню.
|
|
244
|
+
*
|
|
245
|
+
* В DOM она лежит не внутри нашей панели, а рядом, отдельным оверлеем CDK.
|
|
246
|
+
* Поэтому и уход курсора на неё, и клик по ней выглядят как «ушёл наружу»:
|
|
247
|
+
* меню профиля закрывалось прямо под рукой, стоило потянуться к списку
|
|
248
|
+
* языков, а выбранное значение до обработчика не доходило — панель со
|
|
249
|
+
* `rt-select` разрушалась раньше.
|
|
250
|
+
*
|
|
251
|
+
* Вложенность определяется порядком в контейнере оверлеев: CDK добавляет их
|
|
252
|
+
* по мере открытия, поэтому панель, открытая из нашей, всегда следует за
|
|
253
|
+
* нашей. Соседний оверлей, открытый до нас, этой проверкой не удерживается —
|
|
254
|
+
* клик по нему закрывает попап, как и любой клик снаружи.
|
|
255
|
+
*/
|
|
256
|
+
#isNestedOverlay(target) {
|
|
257
|
+
const panel = this.#overlayRef?.overlayElement;
|
|
258
|
+
if (!panel || !(target instanceof Element)) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
const nested = target.closest('.cdk-overlay-pane');
|
|
262
|
+
const container = panel.parentElement;
|
|
263
|
+
if (nested === null || nested === panel || container === null) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
// `querySelectorAll` отдаёт узлы в порядке документа — им и меряется,
|
|
267
|
+
// какая панель открыта позже
|
|
268
|
+
const panes = Array.from(container.querySelectorAll('.cdk-overlay-pane'));
|
|
269
|
+
return panes.indexOf(nested) > panes.indexOf(panel);
|
|
270
|
+
}
|
|
271
|
+
#resolvePanelClasses() {
|
|
272
|
+
const base = 'rt-popover-panel';
|
|
273
|
+
const extra = this.panelClass().trim();
|
|
274
|
+
const preset = materialPresetClassesOf(this.#elementRef.nativeElement);
|
|
275
|
+
return extra ? [base, ...extra.split(/\s+/), ...preset] : [base, ...preset];
|
|
276
|
+
}
|
|
277
|
+
#scheduleHoverClose() {
|
|
278
|
+
this.#clearHoverTimer();
|
|
279
|
+
this.#hoverCloseTimer = setTimeout(() => this.close(), HOVER_CLOSE_DELAY_MS);
|
|
280
|
+
}
|
|
281
|
+
#clearHoverTimer() {
|
|
282
|
+
if (this.#hoverCloseTimer !== null) {
|
|
283
|
+
clearTimeout(this.#hoverCloseTimer);
|
|
284
|
+
this.#hoverCloseTimer = null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
#disposeOverlay() {
|
|
288
|
+
this.#overlayRef?.dispose();
|
|
289
|
+
this.#overlayRef = null;
|
|
290
|
+
}
|
|
291
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPopoverDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
292
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.1.6", type: RtPopoverDirective, isStandalone: true, selector: "[rtPopover]", inputs: { template: { classPropertyName: "template", publicName: "rtPopover", isSignal: true, isRequired: false, transformFunction: null }, trigger: { classPropertyName: "trigger", publicName: "rtPopoverTrigger", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "rtPopoverWidth", isSignal: true, isRequired: false, transformFunction: null }, align: { classPropertyName: "align", publicName: "rtPopoverAlign", isSignal: true, isRequired: false, transformFunction: null }, fitViewport: { classPropertyName: "fitViewport", publicName: "rtPopoverFitViewport", isSignal: true, isRequired: false, transformFunction: null }, context: { classPropertyName: "context", publicName: "rtPopoverContext", isSignal: true, isRequired: false, transformFunction: null }, panelClass: { classPropertyName: "panelClass", publicName: "rtPopoverPanelClass", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "rtPopoverDisabled", isSignal: true, isRequired: false, transformFunction: null }, offsetY: { classPropertyName: "offsetY", publicName: "rtPopoverOffsetY", isSignal: true, isRequired: false, transformFunction: null }, offsetX: { classPropertyName: "offsetX", publicName: "rtPopoverOffsetX", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { opened: "rtPopoverOpened", closed: "rtPopoverClosed" }, host: { listeners: { "click": "onHostClick()", "mouseenter": "onHostMouseEnter()", "mouseleave": "onHostMouseLeave($event)" } }, exportAs: ["rtPopover"], ngImport: i0 }); }
|
|
293
|
+
}
|
|
294
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtPopoverDirective, decorators: [{
|
|
295
|
+
type: Directive,
|
|
296
|
+
args: [{
|
|
297
|
+
selector: '[rtPopover]',
|
|
298
|
+
exportAs: 'rtPopover',
|
|
299
|
+
host: {
|
|
300
|
+
'(click)': 'onHostClick()',
|
|
301
|
+
'(mouseenter)': 'onHostMouseEnter()',
|
|
302
|
+
'(mouseleave)': 'onHostMouseLeave($event)',
|
|
303
|
+
},
|
|
304
|
+
}]
|
|
305
|
+
}], ctorParameters: () => [], propDecorators: { template: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopover", required: false }] }], trigger: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverTrigger", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverWidth", required: false }] }], align: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverAlign", required: false }] }], fitViewport: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverFitViewport", required: false }] }], context: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverContext", required: false }] }], panelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverPanelClass", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverDisabled", required: false }] }], offsetY: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverOffsetY", required: false }] }], offsetX: [{ type: i0.Input, args: [{ isSignal: true, alias: "rtPopoverOffsetX", required: false }] }], opened: [{ type: i0.Output, args: ["rtPopoverOpened"] }], closed: [{ type: i0.Output, args: ["rtPopoverClosed"] }] } });
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Generated bundle index. Do not edit.
|
|
309
|
+
*/
|
|
310
|
+
|
|
311
|
+
export { RtPopoverDirective };
|
|
312
|
+
//# sourceMappingURL=rt-tools-ui-kit-v2-popover.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rt-tools-ui-kit-v2-popover.mjs","sources":["../../../projects/ui-kit-v2/src/lib/components/popover/rt-popover.directive.ts","../../../projects/ui-kit-v2/src/lib/components/popover/rt-tools-ui-kit-v2-popover.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 #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 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 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\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 }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|