@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,1493 @@
|
|
|
1
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
2
|
+
import * as i0 from '@angular/core';
|
|
3
|
+
import { InjectionToken, computed, inject, signal, DestroyRef, Injectable, makeEnvironmentProviders, Pipe, input, booleanAttribute, output, ViewEncapsulation, ChangeDetectionStrategy, Component, Injector, ElementRef, linkedSignal, viewChildren, afterNextRender, Directive, TemplateRef, Renderer2, viewChild, contentChild, isDevMode, effect } from '@angular/core';
|
|
4
|
+
import * as i1 from '@angular/forms';
|
|
5
|
+
import { FormControl, ReactiveFormsModule } from '@angular/forms';
|
|
6
|
+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
7
|
+
import { RouterLink } from '@angular/router';
|
|
8
|
+
import { LOCAL_STORAGE, PlatformService, WINDOW, BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';
|
|
9
|
+
import { ERtStorageKeys, BreakpointsService, rtKitLabel } from '@rt-tools/ui-kit-v2/core';
|
|
10
|
+
import { isKitIconName, materialPairOf, RtIconComponent } from '@rt-tools/ui-kit-v2/icon';
|
|
11
|
+
import { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';
|
|
12
|
+
import { RtInputComponent } from '@rt-tools/ui-kit-v2/input';
|
|
13
|
+
import { RtScrollAreaComponent, RtScrollAreaContentDirective, RtScrollAreaFooterDirective, RtScrollAreaHeaderDirective } from '@rt-tools/ui-kit-v2/scroll-area';
|
|
14
|
+
import { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';
|
|
15
|
+
import { CdkDrag, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';
|
|
16
|
+
import { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';
|
|
17
|
+
import { isRecord } from '@rt-tools/utils';
|
|
18
|
+
|
|
19
|
+
/** Только строки и числа, без повторов, в прежнем порядке. */
|
|
20
|
+
function normalizeSideMenuFavorites(values) {
|
|
21
|
+
return values.reduce((kept, value) => {
|
|
22
|
+
if ((typeof value === 'string' || typeof value === 'number') && !kept.includes(value)) {
|
|
23
|
+
kept.push(value);
|
|
24
|
+
}
|
|
25
|
+
return kept;
|
|
26
|
+
}, []);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Перенос записи списка с места на место.
|
|
30
|
+
*
|
|
31
|
+
* Номер места вне списка у источника ничего не меняет — переносить нечего; у цели прижимается к
|
|
32
|
+
* краю: рука, бросившая строку ниже последней, хотела поставить её последней.
|
|
33
|
+
*/
|
|
34
|
+
function moveSideMenuFavorite(ids, from, to) {
|
|
35
|
+
const next = [...ids];
|
|
36
|
+
if (from < 0 || from >= next.length) {
|
|
37
|
+
return next;
|
|
38
|
+
}
|
|
39
|
+
const moved = next.splice(from, 1)[0];
|
|
40
|
+
const target = Math.min(Math.max(to, 0), next.length);
|
|
41
|
+
next.splice(target, 0, moved);
|
|
42
|
+
return next;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Перенос строки блока — места в блоке, а не в списке.
|
|
46
|
+
*
|
|
47
|
+
* Блок показывает только те номера, пункты которых в меню есть, поэтому место в блоке — не место в
|
|
48
|
+
* списке. Видимые номера переставляются между собой и встают в те же ячейки списка, которые
|
|
49
|
+
* занимали; скрытые остаются на своих местах: их пункт вернётся вместе с правом, и человек найдёт
|
|
50
|
+
* его там, куда ставил. Видимый номер, которого в списке нет, пропускается: список могла поменять
|
|
51
|
+
* другая вкладка, а место номера, которого нет, занять нечем.
|
|
52
|
+
*/
|
|
53
|
+
function moveVisibleSideMenuFavorite(ids, visible, from, to) {
|
|
54
|
+
const listed = new Set(ids);
|
|
55
|
+
const reordered = moveSideMenuFavorite(normalizeSideMenuFavorites(visible), from, to).filter((id) => listed.has(id));
|
|
56
|
+
const slots = new Set(reordered);
|
|
57
|
+
let next = 0;
|
|
58
|
+
return ids.map((id) => {
|
|
59
|
+
if (!slots.has(id)) {
|
|
60
|
+
return id;
|
|
61
|
+
}
|
|
62
|
+
const placed = reordered[next];
|
|
63
|
+
next += 1;
|
|
64
|
+
return placed;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Может ли пункт стоять в избранном: ссылка есть — может; папка без ссылки никуда не ведёт, а
|
|
69
|
+
* пункт с флагом `favoriteDisabled` приложение исключило само.
|
|
70
|
+
*/
|
|
71
|
+
function isSideMenuFavoriteCandidate(item) {
|
|
72
|
+
return Boolean(item.link?.trim()) && !item.favoriteDisabled;
|
|
73
|
+
}
|
|
74
|
+
function collectLinkedItems(items, byId) {
|
|
75
|
+
for (const item of items) {
|
|
76
|
+
if (isSideMenuFavoriteCandidate(item) && !byId.has(item.id)) {
|
|
77
|
+
byId.set(item.id, item);
|
|
78
|
+
}
|
|
79
|
+
collectLinkedItems(item.submenu ?? [], byId);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Пункты меню, стоящие за номерами списка, в порядке списка.
|
|
84
|
+
*
|
|
85
|
+
* Пункт ищется в подменю пунктов полосы, вглубь папок, и берётся первый со ссылкой: избранное,
|
|
86
|
+
* которое никуда не ведёт, — сломанное избранное. Номер без пункта пропускается, но из списка не
|
|
87
|
+
* уходит — это решает сервис, а не меню.
|
|
88
|
+
*/
|
|
89
|
+
function findSideMenuFavoriteItems(menu, ids) {
|
|
90
|
+
const byId = new Map();
|
|
91
|
+
collectLinkedItems(menu.flatMap((item) => item.submenu ?? []), byId);
|
|
92
|
+
return ids.reduce((found, id) => {
|
|
93
|
+
const item = byId.get(id);
|
|
94
|
+
if (item) {
|
|
95
|
+
found.push(item);
|
|
96
|
+
}
|
|
97
|
+
return found;
|
|
98
|
+
}, []);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Пункт полосы, чьё подменю сейчас показано, если избранное у него включено.
|
|
102
|
+
*
|
|
103
|
+
* Избранное у каждого пункта своё и по умолчанию выключено: включает его потребитель флагом
|
|
104
|
+
* `favorites` пункта полосы. Показанный набор даёт меню — закреплённый или открытый наведением, —
|
|
105
|
+
* и пустой набор не принадлежит никому: подменю без списка не показывает ничьё избранное.
|
|
106
|
+
*
|
|
107
|
+
* Раздел узнаётся сначала по самому набору, затем по номеру его первого пункта: приложение вправе
|
|
108
|
+
* передать меню заново теми же пунктами в новых объектах — при смене языка или прав, — и набор,
|
|
109
|
+
* который подменю ещё держит, перестаёт совпадать с пунктами полосы по ссылке. Номера пунктов
|
|
110
|
+
* подменю единственны на всё меню — это обязанность потребителя.
|
|
111
|
+
*/
|
|
112
|
+
function sideMenuFavoritesSection(menu, shown) {
|
|
113
|
+
const first = shown[0];
|
|
114
|
+
if (!first) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const section = menu.find((item) => item.submenu === shown) ??
|
|
118
|
+
menu.find((item) => !!item.submenu?.some((child) => child.id === first.id));
|
|
119
|
+
return section?.favorites ? section : null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Строку отпустили за пределами панели. Пока её тянули, уход указателя с панели не закрывал
|
|
123
|
+
* подменю, открытое наведением, — после броска он должен сработать так, будто случился сейчас.
|
|
124
|
+
*/
|
|
125
|
+
function isSideMenuDropOutside(panel, point) {
|
|
126
|
+
return point.x < panel.left || point.x > panel.right || point.y < panel.top || point.y > panel.bottom;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Отбор пунктов подменю по подстроке подписи.
|
|
131
|
+
*
|
|
132
|
+
* Отбор спускается внутрь папок на любую глубину: потребитель кладёт в папки то, что человек и ищет
|
|
133
|
+
* по имени, и по одному верхнему уровню такой пункт не нашёлся бы.
|
|
134
|
+
*
|
|
135
|
+
* В выдаче стоят только строки, содержащие запрос. Папка остаётся путём к совпавшим детям, а её
|
|
136
|
+
* состав отбирается тем же правилом: иначе одно совпадение по имени папки вытаскивало бы весь её
|
|
137
|
+
* состав. Папка, совпавшая только по имени, остаётся одной строкой без детей.
|
|
138
|
+
*
|
|
139
|
+
* Отобранная папка — новый объект: правка на месте переписала бы набор потребителя, и стёртый
|
|
140
|
+
* запрос вернул бы урезанное меню.
|
|
141
|
+
*/
|
|
142
|
+
function filterSideMenuItems(items, query) {
|
|
143
|
+
const needle = query.trim().toLowerCase();
|
|
144
|
+
if (needle === '') {
|
|
145
|
+
return [...items];
|
|
146
|
+
}
|
|
147
|
+
return items.reduce((kept, item) => {
|
|
148
|
+
const isFolder = Boolean(item.submenu?.length);
|
|
149
|
+
const inside = isFolder ? filterSideMenuItems(item.submenu ?? [], query) : [];
|
|
150
|
+
if (inside.length) {
|
|
151
|
+
kept.push({ ...item, submenu: inside });
|
|
152
|
+
return kept;
|
|
153
|
+
}
|
|
154
|
+
if (item.name?.toLowerCase().includes(needle)) {
|
|
155
|
+
kept.push(isFolder ? { ...item, submenu: [] } : item);
|
|
156
|
+
}
|
|
157
|
+
return kept;
|
|
158
|
+
}, []);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Папки отобранного списка — те, что должны стоять раскрытыми. Возвращаются номера, а не пункты:
|
|
162
|
+
* отобранный список пересобирается на каждую букву запроса, и ссылка на пункт этого не переживает.
|
|
163
|
+
*/
|
|
164
|
+
function sideMenuIdsToExpand(items) {
|
|
165
|
+
return items.reduce((ids, item) => {
|
|
166
|
+
if (item.submenu?.length) {
|
|
167
|
+
ids.push(item.id, ...sideMenuIdsToExpand(item.submenu));
|
|
168
|
+
}
|
|
169
|
+
return ids;
|
|
170
|
+
}, []);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Пределы ширины подменю в пикселях. Уже нижнего не помещается ни одна подпись пункта, шире
|
|
174
|
+
* верхнего подменю закрывает содержимое страницы.
|
|
175
|
+
*/
|
|
176
|
+
const RT_SIDE_MENU_WIDTH_MIN = 120;
|
|
177
|
+
const RT_SIDE_MENU_WIDTH_MAX = 480;
|
|
178
|
+
/** Шаг ширины с клавиатуры: стрелку держат нажатой, и крупный шаг проскакивал бы подпись целиком. */
|
|
179
|
+
const RT_SIDE_MENU_WIDTH_STEP = 16;
|
|
180
|
+
/** Приведение ширины к пределам. */
|
|
181
|
+
function clampSideMenuWidth(width) {
|
|
182
|
+
return Math.min(RT_SIDE_MENU_WIDTH_MAX, Math.max(RT_SIDE_MENU_WIDTH_MIN, width));
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Резка подписи на совпавшие и несовпавшие куски.
|
|
186
|
+
*
|
|
187
|
+
* Отмечаются все вхождения: отмеченное одно первое читается как «второго нет». Сравнение без учёта
|
|
188
|
+
* регистра, а куски режутся из оригинала — приведение к нижнему регистру переписало бы названия.
|
|
189
|
+
*/
|
|
190
|
+
function splitSideMenuTitle(name, query) {
|
|
191
|
+
if (name === '') {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
const needle = query.trim().toLowerCase();
|
|
195
|
+
if (needle === '') {
|
|
196
|
+
return [{ text: name, matched: false }];
|
|
197
|
+
}
|
|
198
|
+
const haystack = name.toLowerCase();
|
|
199
|
+
const parts = [];
|
|
200
|
+
let from = 0;
|
|
201
|
+
for (let at = haystack.indexOf(needle, from); at !== -1; at = haystack.indexOf(needle, from)) {
|
|
202
|
+
if (at > from) {
|
|
203
|
+
parts.push({ text: name.slice(from, at), matched: false });
|
|
204
|
+
}
|
|
205
|
+
parts.push({ text: name.slice(at, at + needle.length), matched: true });
|
|
206
|
+
from = at + needle.length;
|
|
207
|
+
}
|
|
208
|
+
if (from < name.length) {
|
|
209
|
+
parts.push({ text: name.slice(from), matched: false });
|
|
210
|
+
}
|
|
211
|
+
return parts;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Пункты подменю в том порядке, в каком они стоят на экране. Внутрь папки список спускается, только
|
|
215
|
+
* если та раскрыта: ходьба стрелками идёт по видимому, а не по всему набору.
|
|
216
|
+
*/
|
|
217
|
+
function walkSideMenuItems(items, expandedIds) {
|
|
218
|
+
return items.reduce((walk, item) => {
|
|
219
|
+
walk.push(item);
|
|
220
|
+
if (item.submenu?.length && expandedIds.includes(item.id)) {
|
|
221
|
+
walk.push(...walkSideMenuItems(item.submenu, expandedIds));
|
|
222
|
+
}
|
|
223
|
+
return walk;
|
|
224
|
+
}, []);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Куда уходит подсветка на шаг стрелкой. Считается по номеру пункта: видимый список пересобирается
|
|
228
|
+
* на каждую букву запроса. Без подсветки вниз берёт первый пункт, вверх — последний. У краёв ходьба
|
|
229
|
+
* останавливается: заворот уводил бы взгляд через всю панель.
|
|
230
|
+
*/
|
|
231
|
+
function stepSideMenuHighlight(walk, highlightedId, step) {
|
|
232
|
+
if (walk.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const at = walk.findIndex((item) => item.id === highlightedId);
|
|
236
|
+
if (at === -1) {
|
|
237
|
+
return step > 0 ? walk[0].id : walk[walk.length - 1].id;
|
|
238
|
+
}
|
|
239
|
+
return walk[Math.min(walk.length - 1, Math.max(0, at + step))].id;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Тяга ширины подменю указательными событиями: мышиных палец и перо не дают. Слушатели висят на
|
|
243
|
+
* ручке, а держит их захват указателя — слушатель на документе терял бы движение над чужим кадром.
|
|
244
|
+
* Указатель, отнятый средой, кончает тягу так же, как отпускание.
|
|
245
|
+
*
|
|
246
|
+
* Возвращает снятие слушателей. Пустое значение — тянуть нечем.
|
|
247
|
+
*/
|
|
248
|
+
function startSideMenuWidthDrag(event, startWidth, listen, hooks) {
|
|
249
|
+
const handle = event.currentTarget;
|
|
250
|
+
// Тянет только основная кнопка: отпускание правой над меню среды ручке не приходит.
|
|
251
|
+
if (!(handle instanceof HTMLElement) || event.button !== 0) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
const pointerId = event.pointerId;
|
|
255
|
+
const startX = event.clientX;
|
|
256
|
+
// Захвата нет у среды, где идут проверки.
|
|
257
|
+
if (typeof handle.setPointerCapture === 'function') {
|
|
258
|
+
handle.setPointerCapture(pointerId);
|
|
259
|
+
}
|
|
260
|
+
const stopMove = listen(handle, 'pointermove', (moveEvent) => {
|
|
261
|
+
if (moveEvent.pointerId === pointerId) {
|
|
262
|
+
hooks.onWidth(clampSideMenuWidth(startWidth + moveEvent.clientX - startX));
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
const stopUp = listen(handle, 'pointerup', () => hooks.onEnd());
|
|
266
|
+
const stopCancel = listen(handle, 'pointercancel', () => hooks.onEnd());
|
|
267
|
+
// Захват снимается и без отпускания — ручку убрали из разметки; тогда конец тяги — сама потеря.
|
|
268
|
+
const stopLost = listen(handle, 'lostpointercapture', () => hooks.onEnd());
|
|
269
|
+
return () => {
|
|
270
|
+
stopMove();
|
|
271
|
+
stopUp();
|
|
272
|
+
stopCancel();
|
|
273
|
+
stopLost();
|
|
274
|
+
if (typeof handle.releasePointerCapture === 'function' && handle.hasPointerCapture(pointerId)) {
|
|
275
|
+
handle.releasePointerCapture(pointerId);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Ширина, которой панель нарисована. Панели нет или раскладка не посчитана — нижний предел: с нуля
|
|
281
|
+
* тяга уводила бы ширину в отрицательные числа.
|
|
282
|
+
*/
|
|
283
|
+
function drawnSideMenuWidth(panel) {
|
|
284
|
+
const width = panel?.getBoundingClientRect().width ?? 0;
|
|
285
|
+
return width > 0 ? width : RT_SIDE_MENU_WIDTH_MIN;
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Число, которое уходит наружу по концу тяги: большее из натянутого и нарисованного. Нижний предел
|
|
289
|
+
* держит оформление, и панель не бывает уже его, даже если рука ушла левее.
|
|
290
|
+
*/
|
|
291
|
+
function reportedSideMenuWidth(dragged, panel) {
|
|
292
|
+
return Math.max(dragged, drawnSideMenuWidth(panel));
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Новая ширина по нажатой клавише. Пустое значение — клавиша не о ширине, и умолчание не отменяется:
|
|
296
|
+
* иначе ручка съедала бы табуляцию. Влево значит уже при любом направлении письма: ручка стоит у
|
|
297
|
+
* правого края панели.
|
|
298
|
+
*/
|
|
299
|
+
function sideMenuWidthByKey(key, width) {
|
|
300
|
+
switch (key) {
|
|
301
|
+
case 'ArrowRight':
|
|
302
|
+
return clampSideMenuWidth(width + RT_SIDE_MENU_WIDTH_STEP);
|
|
303
|
+
case 'ArrowLeft':
|
|
304
|
+
return clampSideMenuWidth(width - RT_SIDE_MENU_WIDTH_STEP);
|
|
305
|
+
case 'Home':
|
|
306
|
+
return RT_SIDE_MENU_WIDTH_MIN;
|
|
307
|
+
case 'End':
|
|
308
|
+
return RT_SIDE_MENU_WIDTH_MAX;
|
|
309
|
+
default:
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Что показывает закреплённое подменю: выбранный человеком раздел, а пока выбора нет — раздел
|
|
315
|
+
* активного адреса. Выбор впереди активности: иначе до соседнего раздела не добраться.
|
|
316
|
+
*/
|
|
317
|
+
function pinnedSideMenuItems(picked, activeIds, items) {
|
|
318
|
+
if (picked?.length) {
|
|
319
|
+
return picked;
|
|
320
|
+
}
|
|
321
|
+
return items.find((item) => activeIds.includes(item.id) && !!item.submenu?.length)?.submenu ?? [];
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Номер меню, которому приложение своего не задало. */
|
|
325
|
+
const RT_SIDE_MENU_DEFAULT_ID = 'main';
|
|
326
|
+
/**
|
|
327
|
+
* Номер меню, под которым лежат его настройки. Пустой — тот же, что не заданный: атрибут без
|
|
328
|
+
* значения даёт пустую строку, и настройки легли бы под ключ, которого никто не называл.
|
|
329
|
+
*/
|
|
330
|
+
function normalizeSideMenuId(menuId) {
|
|
331
|
+
return menuId?.trim() || RT_SIDE_MENU_DEFAULT_ID;
|
|
332
|
+
}
|
|
333
|
+
function isSubMenuMode(value) {
|
|
334
|
+
return value === 'hover' || value === 'pinned';
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Настройки одного меню из того, что лежит под его номером. Недопустимое поле читается как
|
|
338
|
+
* отсутствие значения, соседние поля остаются: сломанная ширина не стирает режим. Незнакомые поля
|
|
339
|
+
* не удаляются. Ширина приводится к пределам подменю, списки избранного — к номерам без повторов.
|
|
340
|
+
*/
|
|
341
|
+
function normalizeSideMenuSettings(value) {
|
|
342
|
+
if (!isRecord(value)) {
|
|
343
|
+
return {};
|
|
344
|
+
}
|
|
345
|
+
const settings = { ...value };
|
|
346
|
+
const width = value['subMenuWidth'];
|
|
347
|
+
const favorites = value['favorites'];
|
|
348
|
+
const collapsed = value['favoritesCollapsed'];
|
|
349
|
+
if (!isSubMenuMode(value['subMenuMode'])) {
|
|
350
|
+
delete settings.subMenuMode;
|
|
351
|
+
}
|
|
352
|
+
if (typeof width === 'number' && Number.isFinite(width)) {
|
|
353
|
+
settings.subMenuWidth = clampSideMenuWidth(width);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
delete settings.subMenuWidth;
|
|
357
|
+
}
|
|
358
|
+
// Список — только строки и числа без повторов; не массив читается отсутствием списка.
|
|
359
|
+
if (Array.isArray(favorites)) {
|
|
360
|
+
settings.favorites = normalizeSideMenuFavorites(favorites);
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
delete settings.favorites;
|
|
364
|
+
}
|
|
365
|
+
if (Array.isArray(collapsed)) {
|
|
366
|
+
settings.favoritesCollapsed = normalizeSideMenuFavorites(collapsed);
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
delete settings.favoritesCollapsed;
|
|
370
|
+
}
|
|
371
|
+
return settings;
|
|
372
|
+
}
|
|
373
|
+
/** Запись хранилища как объект. Не JSON, не объект, пустое хранилище — одинаково пустая запись. */
|
|
374
|
+
function parseSideMenuSettingsRecord(raw) {
|
|
375
|
+
if (raw === null) {
|
|
376
|
+
return {};
|
|
377
|
+
}
|
|
378
|
+
try {
|
|
379
|
+
const value = JSON.parse(raw);
|
|
380
|
+
return isRecord(value) ? value : {};
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return {};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Настройки всех меню из записи. Меню, чьё значение не объект, пропускается: сломанный угол одного
|
|
388
|
+
* меню не стирает соседние.
|
|
389
|
+
*/
|
|
390
|
+
function readSideMenuSettings(record) {
|
|
391
|
+
return Object.fromEntries(Object.entries(record)
|
|
392
|
+
.filter(([, settings]) => isRecord(settings))
|
|
393
|
+
.map(([menuId, settings]) => [menuId, normalizeSideMenuSettings(settings)]));
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Запись с правкой полей одного меню. Остальные меню и поля уходят такими, какими лежали, даже
|
|
397
|
+
* сломанные: кит правит только то, о чём его попросили.
|
|
398
|
+
*/
|
|
399
|
+
function patchSideMenuSettings(record, menuId, patch) {
|
|
400
|
+
const current = record[menuId];
|
|
401
|
+
return { ...record, [menuId]: { ...(isRecord(current) ? current : {}), ...patch } };
|
|
402
|
+
}
|
|
403
|
+
/** Запись без настроек одного меню. */
|
|
404
|
+
function omitSideMenuSettings(record, menuId) {
|
|
405
|
+
return Object.fromEntries(Object.entries(record).filter(([id]) => id !== menuId));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/** Корзина и вертикальная двойная стрелка — те же знаки, что у первого кита. */
|
|
409
|
+
const DEFAULT_FAVORITES_ICONS = { remove: 'trash', drag: 'arrows-v' };
|
|
410
|
+
const RT_SIDE_MENU_SETTINGS_CONFIG = new InjectionToken('RT_SIDE_MENU_SETTINGS_CONFIG');
|
|
411
|
+
/** Сигнал на номер меню один: повторный вызов отдаёт тот же. */
|
|
412
|
+
function cached(cache, menuId, read) {
|
|
413
|
+
const id = normalizeSideMenuId(menuId);
|
|
414
|
+
let value = cache.get(id);
|
|
415
|
+
if (!value) {
|
|
416
|
+
value = computed(() => read(id));
|
|
417
|
+
cache.set(id, value);
|
|
418
|
+
}
|
|
419
|
+
return value;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Настройки боковых меню в хранилище браузера: режим и ширина подменю, избранное и свёрнутые блоки
|
|
423
|
+
* избранного под номером каждого меню.
|
|
424
|
+
*
|
|
425
|
+
* Перед каждой записью ключ читается заново: соседняя вкладка или приложение могли записать своё,
|
|
426
|
+
* и запись из копии затёрла бы их. Правится одно поле одного меню, остальное уходит как лежало.
|
|
427
|
+
* Изменения из других вкладок приходят событием `storage` и обновляют сигналы.
|
|
428
|
+
*
|
|
429
|
+
* Хранилище берётся токеном `@rt-tools/core` напрямую, а не общим сервисом хранилища: сервису
|
|
430
|
+
* нужно знать, что запись не удалась, и держать правду в памяти до перезагрузки. Без хранилища, вне
|
|
431
|
+
* браузера, закрытое или полное — настройки живут в памяти, и ни чтение, ни запись не бросают.
|
|
432
|
+
*
|
|
433
|
+
* Сервис ставится провайдером окружения — `provideRtSideMenuSettings()`, — а меню берёт его
|
|
434
|
+
* необязательно: не поставлен — меню ничего не хранит, режим и ширина приходят входами.
|
|
435
|
+
*/
|
|
436
|
+
class RtSideMenuSettingsService {
|
|
437
|
+
#config;
|
|
438
|
+
#storage;
|
|
439
|
+
#key;
|
|
440
|
+
#menus;
|
|
441
|
+
#modes;
|
|
442
|
+
#widths;
|
|
443
|
+
#favorites;
|
|
444
|
+
#collapsed;
|
|
445
|
+
/** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */
|
|
446
|
+
#record;
|
|
447
|
+
/** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */
|
|
448
|
+
#unsaved;
|
|
449
|
+
#settings;
|
|
450
|
+
constructor() {
|
|
451
|
+
this.#config = inject(RT_SIDE_MENU_SETTINGS_CONFIG, { optional: true }) ?? {};
|
|
452
|
+
this.#storage = inject(LOCAL_STORAGE, { optional: true }) ?? null;
|
|
453
|
+
this.#key = this.#config.storageKey?.trim() || ERtStorageKeys.SideMenu;
|
|
454
|
+
this.#menus = new Map();
|
|
455
|
+
this.#modes = new Map();
|
|
456
|
+
this.#widths = new Map();
|
|
457
|
+
this.#favorites = new Map();
|
|
458
|
+
this.#collapsed = new Map();
|
|
459
|
+
/** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */
|
|
460
|
+
this.#record = this.#load() ?? {};
|
|
461
|
+
/** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */
|
|
462
|
+
this.#unsaved = false;
|
|
463
|
+
this.#settings = signal(readSideMenuSettings(this.#record), /* @ts-ignore */
|
|
464
|
+
...(ngDevMode ? [{ debugName: "#settings" }] : /* istanbul ignore next */ []));
|
|
465
|
+
/** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */
|
|
466
|
+
this.favoritesIcons = { ...DEFAULT_FAVORITES_ICONS, ...this.#config.icons };
|
|
467
|
+
/** Номера меню, чьи настройки лежат в хранилище. */
|
|
468
|
+
this.menuIds = computed(() => Object.keys(this.#settings()), /* @ts-ignore */
|
|
469
|
+
...(ngDevMode ? [{ debugName: "menuIds" }] : /* istanbul ignore next */ []));
|
|
470
|
+
if (!inject(PlatformService).isPlatformBrowser) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const view = inject(WINDOW);
|
|
474
|
+
const onStorage = (event) => {
|
|
475
|
+
if (event.key === this.#key || event.key === null) {
|
|
476
|
+
this.#unsaved = false;
|
|
477
|
+
this.#apply(this.#fresh());
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
view.addEventListener('storage', onStorage);
|
|
481
|
+
inject(DestroyRef).onDestroy(() => view.removeEventListener('storage', onStorage));
|
|
482
|
+
}
|
|
483
|
+
/** Настройки одного меню целиком, с незнакомыми полями; не сохранены — пустой объект. */
|
|
484
|
+
settings(menuId) {
|
|
485
|
+
return cached(this.#menus, menuId, (id) => this.#settings()[id] ?? {});
|
|
486
|
+
}
|
|
487
|
+
subMenuMode(menuId) {
|
|
488
|
+
return cached(this.#modes, menuId, (id) => this.#settings()[id]?.subMenuMode ?? 'hover');
|
|
489
|
+
}
|
|
490
|
+
subMenuWidth(menuId) {
|
|
491
|
+
return cached(this.#widths, menuId, (id) => this.#settings()[id]?.subMenuWidth ?? null);
|
|
492
|
+
}
|
|
493
|
+
/** Избранное меню в порядке списка; пусто — пустой список. */
|
|
494
|
+
favoriteIds(menuId) {
|
|
495
|
+
return cached(this.#favorites, menuId, (id) => this.#settings()[id]?.favorites ?? []);
|
|
496
|
+
}
|
|
497
|
+
/** Пункты полосы, чей блок избранного свёрнут; ничего не свёрнуто — пустой список. */
|
|
498
|
+
favoritesCollapsed(menuId) {
|
|
499
|
+
return cached(this.#collapsed, menuId, (id) => this.#settings()[id]?.favoritesCollapsed ?? []);
|
|
500
|
+
}
|
|
501
|
+
/** Сворачивает или разворачивает блок одного раздела; остальные разделы остаются как были. */
|
|
502
|
+
setFavoritesCollapsed(menuId, sectionId, collapsed) {
|
|
503
|
+
const current = this.#freshSettings(menuId).favoritesCollapsed ?? [];
|
|
504
|
+
const others = current.filter((id) => id !== sectionId);
|
|
505
|
+
this.#update(menuId, { favoritesCollapsed: collapsed ? [...others, sectionId] : others });
|
|
506
|
+
}
|
|
507
|
+
/** Звезда: нет в списке — встаёт последним, есть — уходит. */
|
|
508
|
+
toggleFavorite(menuId, id) {
|
|
509
|
+
this.#updateFavorites(menuId, (ids) => ids.includes(id) ? ids.filter((kept) => kept !== id) : [...ids, id]);
|
|
510
|
+
}
|
|
511
|
+
removeFavorite(menuId, id) {
|
|
512
|
+
this.#updateFavorites(menuId, (ids) => ids.filter((kept) => kept !== id));
|
|
513
|
+
}
|
|
514
|
+
/** Перенос строки блока: места в блоке, скрытые номера остаются на своих. */
|
|
515
|
+
moveVisibleSideMenuFavorite(menuId, visibleIds, from, to) {
|
|
516
|
+
this.#updateFavorites(menuId, (ids) => moveVisibleSideMenuFavorite(ids, visibleIds, from, to));
|
|
517
|
+
}
|
|
518
|
+
/** Список целиком: приложение заводит избранное само, например по умолчанию для роли. */
|
|
519
|
+
setFavorites(menuId, ids) {
|
|
520
|
+
this.#updateFavorites(menuId, () => normalizeSideMenuFavorites(ids));
|
|
521
|
+
}
|
|
522
|
+
setSubMenuMode(menuId, mode) {
|
|
523
|
+
this.#update(menuId, { subMenuMode: mode });
|
|
524
|
+
}
|
|
525
|
+
setSubMenuWidth(menuId, width) {
|
|
526
|
+
this.#update(menuId, { subMenuWidth: clampSideMenuWidth(width) });
|
|
527
|
+
}
|
|
528
|
+
/** Настройки меню уходят только по вызову приложения: кит сам ничего не удаляет. */
|
|
529
|
+
deleteSettings(menuId) {
|
|
530
|
+
this.#commit(omitSideMenuSettings(this.#fresh(), normalizeSideMenuId(menuId)));
|
|
531
|
+
}
|
|
532
|
+
/** Прочитать хранилище → поправить поля одного меню → записать. */
|
|
533
|
+
#update(menuId, patch) {
|
|
534
|
+
const record = this.#fresh();
|
|
535
|
+
const id = normalizeSideMenuId(menuId);
|
|
536
|
+
this.#commit(patchSideMenuSettings(record, id, { ...normalizeSideMenuSettings(record[id]), ...patch }));
|
|
537
|
+
}
|
|
538
|
+
/** Список правится по свежей записи хранилища: соседняя вкладка могла его поменять. */
|
|
539
|
+
#updateFavorites(menuId, next) {
|
|
540
|
+
this.#update(menuId, { favorites: next([...(this.#freshSettings(menuId).favorites ?? [])]) });
|
|
541
|
+
}
|
|
542
|
+
#freshSettings(menuId) {
|
|
543
|
+
return normalizeSideMenuSettings(this.#fresh()[normalizeSideMenuId(menuId)]);
|
|
544
|
+
}
|
|
545
|
+
#commit(record) {
|
|
546
|
+
this.#apply(record);
|
|
547
|
+
try {
|
|
548
|
+
this.#storage?.setItem(this.#key, JSON.stringify(record));
|
|
549
|
+
this.#unsaved = false;
|
|
550
|
+
}
|
|
551
|
+
catch {
|
|
552
|
+
// хранилище закрыто или полно — настройки живут в памяти до перезагрузки
|
|
553
|
+
this.#unsaved = true;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
#apply(record) {
|
|
557
|
+
this.#record = record;
|
|
558
|
+
this.#settings.set(readSideMenuSettings(record));
|
|
559
|
+
}
|
|
560
|
+
/** Запись из хранилища, а если оно недоступно или не приняло прошлую запись — последняя известная. */
|
|
561
|
+
#fresh() {
|
|
562
|
+
return this.#unsaved ? this.#record : (this.#load() ?? this.#record);
|
|
563
|
+
}
|
|
564
|
+
/** Пусто — хранилища нет или оно не отвечает. Сломанная запись читается пустой, но читается. */
|
|
565
|
+
#load() {
|
|
566
|
+
if (!this.#storage) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
return parseSideMenuSettingsRecord(this.#storage.getItem(this.#key));
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
return null;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
577
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService }); }
|
|
578
|
+
}
|
|
579
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSettingsService, decorators: [{
|
|
580
|
+
type: Injectable
|
|
581
|
+
}], ctorParameters: () => [] });
|
|
582
|
+
/**
|
|
583
|
+
* Сервис настроек бокового меню для приложения.
|
|
584
|
+
*
|
|
585
|
+
* ```ts
|
|
586
|
+
* bootstrapApplication(App, { providers: [provideRtSideMenuSettings()] });
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
function provideRtSideMenuSettings(config = {}) {
|
|
590
|
+
return makeEnvironmentProviders([{ provide: RT_SIDE_MENU_SETTINGS_CONFIG, useValue: config }, RtSideMenuSettingsService]);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const RT_SIDE_MENU = new InjectionToken('RT_SIDE_MENU');
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Значок пункта по его `icon`: имя кита как есть, имя Material первого кита — его парой из перечня
|
|
597
|
+
* кита, как у `rt-menu-item`. `null` — кит такой значок не рисует: пункт берёт свой шаблон меню или
|
|
598
|
+
* стоит без значка.
|
|
599
|
+
*/
|
|
600
|
+
function sideMenuIconName(icon) {
|
|
601
|
+
if (!icon) {
|
|
602
|
+
return null;
|
|
603
|
+
}
|
|
604
|
+
if (isKitIconName(icon)) {
|
|
605
|
+
return icon;
|
|
606
|
+
}
|
|
607
|
+
return materialPairOf(icon);
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Имена значков меню, о которых оно должно предупредить: ни имени кита, ни пары в перечне. И значок
|
|
611
|
+
* пункта, и значок кнопки строки закрывает свой шаблон меню.
|
|
612
|
+
* Без предупреждения такой значок пуст, и пропуск не заметен. Обходит пункты на любую глубину;
|
|
613
|
+
* каждое имя — один раз.
|
|
614
|
+
*/
|
|
615
|
+
function unpairedSideMenuIcons(items, hasOwnIcon) {
|
|
616
|
+
const found = new Set();
|
|
617
|
+
const walk = (list) => {
|
|
618
|
+
for (const item of list) {
|
|
619
|
+
if (!hasOwnIcon && item.icon && sideMenuIconName(item.icon) === null) {
|
|
620
|
+
found.add(item.icon);
|
|
621
|
+
}
|
|
622
|
+
if (!hasOwnIcon && item.iconButton?.icon && sideMenuIconName(item.iconButton.icon) === null) {
|
|
623
|
+
found.add(item.iconButton.icon);
|
|
624
|
+
}
|
|
625
|
+
walk(item.submenu ?? []);
|
|
626
|
+
}
|
|
627
|
+
};
|
|
628
|
+
walk(items);
|
|
629
|
+
return [...found];
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** Значок пункта, который рисует кит: имя кита или пара имени Material; `null` — такого нет. */
|
|
633
|
+
class RtSideMenuIconPipe {
|
|
634
|
+
transform(icon) {
|
|
635
|
+
return sideMenuIconName(icon);
|
|
636
|
+
}
|
|
637
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
638
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, isStandalone: true, name: "rtSideMenuIcon" }); }
|
|
639
|
+
}
|
|
640
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconPipe, decorators: [{
|
|
641
|
+
type: Pipe,
|
|
642
|
+
args: [{
|
|
643
|
+
name: 'rtSideMenuIcon',
|
|
644
|
+
}]
|
|
645
|
+
}] });
|
|
646
|
+
|
|
647
|
+
/** Подпись пункта кусками: отмечено ровно то, чем она совпала с запросом поиска. */
|
|
648
|
+
class RtSideMenuTitlePartsPipe {
|
|
649
|
+
transform(name, query) {
|
|
650
|
+
return splitSideMenuTitle(name ?? '', query);
|
|
651
|
+
}
|
|
652
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
|
|
653
|
+
static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, isStandalone: true, name: "rtSideMenuTitleParts" }); }
|
|
654
|
+
}
|
|
655
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuTitlePartsPipe, decorators: [{
|
|
656
|
+
type: Pipe,
|
|
657
|
+
args: [{
|
|
658
|
+
name: 'rtSideMenuTitleParts',
|
|
659
|
+
}]
|
|
660
|
+
}] });
|
|
661
|
+
|
|
662
|
+
const BEM_BLOCK$2 = 'rt-side-menu-sub-item';
|
|
663
|
+
/** Строка блока избранного и кнопка избранного в ней: фокус уходит на соседнюю до удаления строки. */
|
|
664
|
+
const FAVORITE_ROW = '.rt-side-menu-favorites__row';
|
|
665
|
+
const FAVORITE_BUTTON = '.rt-side-menu-sub-item__favorite button';
|
|
666
|
+
/**
|
|
667
|
+
* Строка подменю: пункт со ссылкой или папка со своими пунктами. Раскрытость, подсветку и запрос
|
|
668
|
+
* строка берёт у меню — своего состояния у неё нет.
|
|
669
|
+
*
|
|
670
|
+
* В разделе, где приложение включило избранное, у пункта со ссылкой стоит звезда; в строке блока
|
|
671
|
+
* избранного — кнопка «убрать» и слот `[rtSideMenuSubItemAction]`, куда блок кладёт ручку.
|
|
672
|
+
*/
|
|
673
|
+
class RtSideMenuSubItemComponent {
|
|
674
|
+
constructor() {
|
|
675
|
+
this.#settings = inject(RtSideMenuSettingsService, { optional: true });
|
|
676
|
+
this.menuRef = inject(RT_SIDE_MENU);
|
|
677
|
+
this.narrow = inject(BreakpointsService).narrow;
|
|
678
|
+
/** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */
|
|
679
|
+
this.favoritesIcons = this.#settings?.favoritesIcons ?? null;
|
|
680
|
+
this.addLabel = rtKitLabel('sideMenuFavoriteAdd');
|
|
681
|
+
this.removeLabel = rtKitLabel('sideMenuFavoriteRemove');
|
|
682
|
+
/** Избранное включено у раздела, чьё подменю показано, и приложение поставило настройки меню. */
|
|
683
|
+
this.favoritesOn = computed(() => !!this.#settings && sideMenuFavoritesSection(this.menuRef.menuItems(), this.menuRef.shownSubMenu()) !== null, /* @ts-ignore */
|
|
684
|
+
...(ngDevMode ? [{ debugName: "favoritesOn" }] : /* istanbul ignore next */ []));
|
|
685
|
+
this.favoriteIds = computed(() => this.#settings?.favoriteIds(this.menuRef.menuId())() ?? [], /* @ts-ignore */
|
|
686
|
+
...(ngDevMode ? [{ debugName: "favoriteIds" }] : /* istanbul ignore next */ []));
|
|
687
|
+
this.item = input.required(/* @ts-ignore */
|
|
688
|
+
...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
|
|
689
|
+
/** Строка стоит в блоке избранного: вместо звезды — «убрать», без номера и подсветки клавиатуры. */
|
|
690
|
+
this.inFavorites = input(false, { ...(ngDevMode ? { debugName: "inFavorites" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
691
|
+
this.clickSubMenuAction = output();
|
|
692
|
+
this.clickSubMenuAdditionalAction = output();
|
|
693
|
+
}
|
|
694
|
+
#settings;
|
|
695
|
+
onClickSubMenu(item, event) {
|
|
696
|
+
this.clickSubMenuAction.emit({ item, event });
|
|
697
|
+
}
|
|
698
|
+
/** Кнопка потребителя в строке: ни перехода по ссылке строки, ни закрытия подменю. */
|
|
699
|
+
onClickSubMenuAdditional(item, event) {
|
|
700
|
+
event.preventDefault();
|
|
701
|
+
event.stopPropagation();
|
|
702
|
+
this.clickSubMenuAdditionalAction.emit({ data: item.iconButton?.data, event });
|
|
703
|
+
}
|
|
704
|
+
/** Звезда переключает избранное и больше ничего: ни перехода, ни закрытия подменю. */
|
|
705
|
+
onToggleFavorite(item, event) {
|
|
706
|
+
event.preventDefault();
|
|
707
|
+
event.stopPropagation();
|
|
708
|
+
this.#settings?.toggleFavorite(this.menuRef.menuId(), item.id);
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* «Убрать» в строке блока. Фокус уходит на такую же кнопку соседней строки до того, как строка
|
|
712
|
+
* исчезнет: иначе он падал бы на страницу, и человек с клавиатуры терял бы место.
|
|
713
|
+
*/
|
|
714
|
+
onRemoveFavorite(item, event) {
|
|
715
|
+
event.preventDefault();
|
|
716
|
+
event.stopPropagation();
|
|
717
|
+
const row = event.target instanceof Element ? event.target.closest(FAVORITE_ROW) : null;
|
|
718
|
+
const neighbour = row?.nextElementSibling ?? row?.previousElementSibling;
|
|
719
|
+
neighbour?.querySelector(FAVORITE_BUTTON)?.focus();
|
|
720
|
+
this.#settings?.removeFavorite(this.menuRef.menuId(), item.id);
|
|
721
|
+
}
|
|
722
|
+
onToggleFolder(item) {
|
|
723
|
+
this.menuRef.toggleFolder(item);
|
|
724
|
+
}
|
|
725
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSubItemComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
726
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuSubItemComponent, isStandalone: true, selector: "rt-side-menu-sub-item", inputs: { item: { classPropertyName: "item", publicName: "item", isSignal: true, isRequired: true, transformFunction: null }, inFavorites: { classPropertyName: "inFavorites", publicName: "inFavorites", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { classAttribute: "rt-side-menu-sub-item" }, ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0431\u0435\u0437 \u043D\u043E\u043C\u0435\u0440\u0430: \u043F\u043E \u043D\u043E\u043C\u0435\u0440\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443 \u0441\u043F\u0438\u0441\u043A\u0430. \u0418 \u0431\u0435\u0437\n \u0440\u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u044F \u0441\u0441\u044B\u043B\u043A\u0438: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u043D\u0430 \u0440\u0443\u0447\u043A\u0443 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0437\u0430\u0431\u0438\u0440\u0430\u043B \u0441\u0435\u0431\u0435,\n \u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u043B\u043E\u0441\u044C.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [routerLink]=\"row.link\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- \u041A\u0443\u0441\u043E\u043A \u0431\u0435\u0437 \u043E\u0442\u043C\u0435\u0442\u043A\u0438 \u0442\u043E\u0436\u0435 \u0443\u0437\u0435\u043B: \u0433\u043E\u043B\u0430\u044F \u0438\u043D\u0442\u0435\u0440\u043F\u043E\u043B\u044F\u0446\u0438\u044F \u0440\u0430\u0437\u0440\u044B\u0432\u0430\u043B\u0430 \u0431\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u043F\u0440\u043E\u0431\u0435\u043B\u0430\u043C\u0438. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u043D\u0435\u0441\u0451\u0442 \u00AB\u0443\u0431\u0440\u0430\u0442\u044C\u00BB, \u0430 \u043D\u0435 \u0437\u0432\u0435\u0437\u0434\u0443: \u0432\u0441\u0451 \u0432 \u0431\u043B\u043E\u043A\u0435 \u0443\u0436\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C. \u0417\u0432\u0435\u0437\u0434\u0430 \u2014\n \u0443 \u043F\u0443\u043D\u043A\u0442\u0430 \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u043C \u0432 \u0440\u0430\u0437\u0434\u0435\u043B\u0435 \u0441 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u043C: \u043F\u043E\u043B\u0430\u044F \u2014 \u043D\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C,\n \u0437\u0430\u043A\u0440\u0430\u0448\u0435\u043D\u043D\u0430\u044F \u0446\u0432\u0435\u0442\u043E\u043C \u0442\u0435\u043C\u044B \u2014 \u0432 \u043D\u0451\u043C. \u0418\u043C\u044F \u043A\u043D\u043E\u043F\u043A\u0438 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n shape=\"circle\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n shape=\"circle\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446: \u0431\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0430\u0434\u0451\u0442 \u0441\u044E\u0434\u0430 \u0440\u0443\u0447\u043A\u0443. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u0432\u0438\u0434\u043D\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0438 \u0441\u0442\u043E\u0438\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439: \u043A\u043D\u043E\u043F\u043A\u0438 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u0441\u0442\u0430\u044E\u0442 \u043F\u0435\u0440\u0435\u0434 \u043D\u0435\u0439. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- \u0418\u043C\u044F \u0431\u0435\u0437 \u0437\u043D\u0430\u0447\u043A\u0430 \u043A\u0438\u0442\u0430 \u0431\u0435\u0440\u0451\u0442 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E, \u043A\u0430\u043A \u0437\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438. \u0411\u0435\u0437 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0430 \u043F\u0443\u0441\u0442\u0430 \u0438\n \u043D\u0435\u0437\u043D\u0430\u043A\u043E\u043C\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0437\u043D\u0430\u0447\u043A\u0443 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442: \u0437\u043D\u0430\u0447\u043A\u0430 \u0441 \u0442\u0430\u043A\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u043D\u0435\u0442, \u0438 \u0436\u0434\u0430\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043A\u043E\u043C\u0443. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n shape=\"circle\"\n [icon]=\"buttonKitIcon\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n \u041F\u0430\u043F\u043A\u0430 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430. \u0420\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0438 \u0441 \u043F\u0443\u0441\u0442\u044B\u043C \u0441\u043E\u0441\u0442\u0430\u0432\u043E\u043C: \u043E\u0442\u0431\u043E\u0440 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0442\u0430\u043A\n \u0442\u0443, \u0447\u0442\u043E \u0441\u043E\u0432\u043F\u0430\u043B\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u044C\u044E. \u0428\u0435\u0432\u0440\u043E\u043D\u0430 \u0443 \u0442\u0430\u043A\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. \u0420\u0430\u0441\u043A\u0440\u044B\u0442\u0438\u0435\n \u0434\u0435\u0440\u0436\u0438\u0442 \u043C\u0435\u043D\u044E: \u043F\u0430\u043D\u0435\u043B\u044C \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043E \u043D\u0430\u0436\u0430\u0442\u0438\u0438, \u043C\u0435\u043D\u044E \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0430\u043F\u043A\u0443. \u041A\u043D\u043E\u043F\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\n \u043E\u0442\u0434\u0430\u043D \u043D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u043F\u043E \u043D\u0435\u043C\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043C\u0435\u043D\u044E \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- \u0417\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438: \u0438\u043C\u044F \u043A\u0438\u0442\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u0430 \u0438\u043C\u0435\u043D\u0438 Material, \u0430 \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);display:block}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-control-height-md);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:var(--rt-space-sm);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-space-md) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-space-sm);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-color-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu-sub-item__icon{flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__favorite{flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc(var(--rt-space-md) * (1 + var(--rt-side-menu-sub-item-depth)));--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm);--rt-expansion-panel-header-bg: transparent}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-color-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtExpansionPanelComponent, selector: "rt-expansion-panel", inputs: ["expanded", "disabled", "hideToggle", "appearance", "headerId", "ariaLabel"], outputs: ["expandedChange"] }, { kind: "directive", type: RtExpansionPanelContentDirective, selector: "ng-template[rtExpansionPanelContent]" }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "directive", type: RtTooltipDirective, selector: "[rtTooltip]", inputs: ["rtTooltip", "rtTooltipPlacement", "rtTooltipWhenTruncated"], exportAs: ["rtTooltip"] }, { kind: "pipe", type: RtSideMenuTitlePartsPipe, name: "rtSideMenuTitleParts" }, { kind: "pipe", type: RtSideMenuIconPipe, name: "rtSideMenuIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
727
|
+
}
|
|
728
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuSubItemComponent, decorators: [{
|
|
729
|
+
type: Component,
|
|
730
|
+
args: [{ selector: 'rt-side-menu-sub-item', host: { class: BEM_BLOCK$2 }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
731
|
+
NgTemplateOutlet,
|
|
732
|
+
RouterLink,
|
|
733
|
+
BlockDirective,
|
|
734
|
+
ElemDirective,
|
|
735
|
+
ModDirective,
|
|
736
|
+
RtExpansionPanelComponent,
|
|
737
|
+
RtExpansionPanelContentDirective,
|
|
738
|
+
RtIconComponent,
|
|
739
|
+
RtIconButtonComponent,
|
|
740
|
+
RtTooltipDirective,
|
|
741
|
+
RtSideMenuTitlePartsPipe,
|
|
742
|
+
RtSideMenuIconPipe,
|
|
743
|
+
], template: "<ng-container rtBlock=\"rt-side-menu-sub-item\">\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: item(), depth: 0 }\" />\n <ng-template #rowTpl let-row let-depth=\"depth\">\n @let isActive = menuRef.activeMenuIds().includes(row.id);\n @let isHighlighted = !inFavorites() && menuRef.highlightedMenuId() === row.id;\n @if (row.link) {\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0431\u0435\u0437 \u043D\u043E\u043C\u0435\u0440\u0430: \u043F\u043E \u043D\u043E\u043C\u0435\u0440\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443 \u0441\u043F\u0438\u0441\u043A\u0430. \u0418 \u0431\u0435\u0437\n \u0440\u043E\u0434\u043D\u043E\u0433\u043E \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u044F \u0441\u0441\u044B\u043B\u043A\u0438: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u043D\u0430 \u0440\u0443\u0447\u043A\u0443 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438 \u0431\u0440\u0430\u0443\u0437\u0435\u0440 \u0437\u0430\u0431\u0438\u0440\u0430\u043B \u0441\u0435\u0431\u0435,\n \u0438 \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043D\u0435 \u043D\u0430\u0447\u0438\u043D\u0430\u043B\u043E\u0441\u044C.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [routerLink]=\"row.link\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ active: isActive, highlighted: isHighlighted }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickSubMenu(row, $event)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n <!-- \u041A\u0443\u0441\u043E\u043A \u0431\u0435\u0437 \u043E\u0442\u043C\u0435\u0442\u043A\u0438 \u0442\u043E\u0436\u0435 \u0443\u0437\u0435\u043B: \u0433\u043E\u043B\u0430\u044F \u0438\u043D\u0442\u0435\u0440\u043F\u043E\u043B\u044F\u0446\u0438\u044F \u0440\u0430\u0437\u0440\u044B\u0432\u0430\u043B\u0430 \u0431\u044B \u043F\u043E\u0434\u043F\u0438\u0441\u044C \u043F\u0440\u043E\u0431\u0435\u043B\u0430\u043C\u0438. -->\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <!--\n \u0421\u0442\u0440\u043E\u043A\u0430 \u0431\u043B\u043E\u043A\u0430 \u043D\u0435\u0441\u0451\u0442 \u00AB\u0443\u0431\u0440\u0430\u0442\u044C\u00BB, \u0430 \u043D\u0435 \u0437\u0432\u0435\u0437\u0434\u0443: \u0432\u0441\u0451 \u0432 \u0431\u043B\u043E\u043A\u0435 \u0443\u0436\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C. \u0417\u0432\u0435\u0437\u0434\u0430 \u2014\n \u0443 \u043F\u0443\u043D\u043A\u0442\u0430 \u0441\u043E \u0441\u0432\u043E\u0438\u043C \u0430\u0434\u0440\u0435\u0441\u043E\u043C \u0432 \u0440\u0430\u0437\u0434\u0435\u043B\u0435 \u0441 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u043C: \u043F\u043E\u043B\u0430\u044F \u2014 \u043D\u0435 \u0432 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u043C,\n \u0437\u0430\u043A\u0440\u0430\u0448\u0435\u043D\u043D\u0430\u044F \u0446\u0432\u0435\u0442\u043E\u043C \u0442\u0435\u043C\u044B \u2014 \u0432 \u043D\u0451\u043C. \u0418\u043C\u044F \u043A\u043D\u043E\u043F\u043A\u0438 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\n shape=\"circle\"\n [rtMod]=\"{ remove: true }\"\n [icon]=\"favoritesIcons.remove\"\n [ariaLabel]=\"removeLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : removeLabel()\"\n (clicked)=\"onRemoveFavorite(row, $event)\" />\n } @else if (favoritesOn() && !row.favoriteDisabled) {\n @let isFavorite = favoriteIds().includes(row.id);\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-star\"\n size=\"sm\"\n shape=\"circle\"\n [rtMod]=\"{ on: isFavorite }\"\n [icon]=\"isFavorite ? 'star-fill' : 'star'\"\n [iconFill]=\"isFavorite\"\n [ariaLabel]=\"isFavorite ? removeLabel() : addLabel()\"\n [tooltip]=\"narrow() || !menuRef.subMenuTooltipsShown() ? '' : isFavorite ? removeLabel() : addLabel()\"\n (clicked)=\"onToggleFavorite(row, $event)\" />\n }\n\n <!-- \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044F, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u0441\u0442\u0440\u043E\u043A\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442 \u0432\u043B\u0430\u0434\u0435\u043B\u0435\u0446: \u0431\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u043A\u043B\u0430\u0434\u0451\u0442 \u0441\u044E\u0434\u0430 \u0440\u0443\u0447\u043A\u0443. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- \u041A\u043D\u043E\u043F\u043A\u0430 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F \u0432\u0438\u0434\u043D\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0438 \u0441\u0442\u043E\u0438\u0442 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439: \u043A\u043D\u043E\u043F\u043A\u0438 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0432\u0441\u0442\u0430\u044E\u0442 \u043F\u0435\u0440\u0435\u0434 \u043D\u0435\u0439. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- \u0418\u043C\u044F \u0431\u0435\u0437 \u0437\u043D\u0430\u0447\u043A\u0430 \u043A\u0438\u0442\u0430 \u0431\u0435\u0440\u0451\u0442 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E, \u043A\u0430\u043A \u0437\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438. \u0411\u0435\u0437 \u0448\u0430\u0431\u043B\u043E\u043D\u0430 \u043A\u043D\u043E\u043F\u043A\u0430 \u043F\u0443\u0441\u0442\u0430 \u0438\n \u043D\u0435\u0437\u043D\u0430\u043A\u043E\u043C\u043E\u0433\u043E \u0438\u043C\u0435\u043D\u0438 \u0437\u043D\u0430\u0447\u043A\u0443 \u043D\u0435 \u043F\u0435\u0440\u0435\u0434\u0430\u0451\u0442: \u0437\u043D\u0430\u0447\u043A\u0430 \u0441 \u0442\u0430\u043A\u0438\u043C \u0438\u043C\u0435\u043D\u0435\u043C \u043D\u0435\u0442, \u0438 \u0436\u0434\u0430\u0442\u044C \u0435\u0433\u043E \u043D\u0435\u043A\u043E\u043C\u0443. -->\n @let buttonKitIcon = buttonIcon | rtSideMenuIcon;\n @let buttonOwnTpl = buttonKitIcon ? null : menuRef.ownIconTpl();\n <rt-icon-button\n rtElem=\"action\"\n qa-dataid=\"side-menu-sub-item-action\"\n size=\"sm\"\n shape=\"circle\"\n [icon]=\"buttonKitIcon\"\n [ariaLabel]=\"row.name ?? ''\"\n (clicked)=\"onClickSubMenuAdditional(row, $event)\">\n @if (buttonOwnTpl) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-button-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"buttonOwnTpl\"\n [ngTemplateOutletContext]=\"{ $implicit: row, icon: buttonIcon, slot: 'iconButton' }\" />\n </span>\n }\n </rt-icon-button>\n }\n </a>\n } @else if (row.submenu) {\n <!--\n \u041F\u0430\u043F\u043A\u0430 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430. \u0420\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0438 \u0441 \u043F\u0443\u0441\u0442\u044B\u043C \u0441\u043E\u0441\u0442\u0430\u0432\u043E\u043C: \u043E\u0442\u0431\u043E\u0440 \u043E\u0441\u0442\u0430\u0432\u043B\u044F\u0435\u0442 \u0442\u0430\u043A\n \u0442\u0443, \u0447\u0442\u043E \u0441\u043E\u0432\u043F\u0430\u043B\u0430 \u043F\u043E\u0434\u043F\u0438\u0441\u044C\u044E. \u0428\u0435\u0432\u0440\u043E\u043D\u0430 \u0443 \u0442\u0430\u043A\u043E\u0439 \u043F\u0430\u043F\u043A\u0438 \u043D\u0435\u0442 \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. \u0420\u0430\u0441\u043A\u0440\u044B\u0442\u0438\u0435\n \u0434\u0435\u0440\u0436\u0438\u0442 \u043C\u0435\u043D\u044E: \u043F\u0430\u043D\u0435\u043B\u044C \u0441\u043E\u043E\u0431\u0449\u0430\u0435\u0442 \u043E \u043D\u0430\u0436\u0430\u0442\u0438\u0438, \u043C\u0435\u043D\u044E \u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0430\u0435\u0442 \u043F\u0430\u043F\u043A\u0443. \u041A\u043D\u043E\u043F\u043A\u0435 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430\n \u043E\u0442\u0434\u0430\u043D \u043D\u043E\u043C\u0435\u0440 \u043F\u0443\u043D\u043A\u0442\u0430 \u2014 \u043F\u043E \u043D\u0435\u043C\u0443 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430 \u043C\u0435\u043D\u044E \u043D\u0430\u0436\u0438\u043C\u0430\u0435\u0442 \u0441\u0442\u0440\u043E\u043A\u0443.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [hideToggle]=\"!row.submenu.length\"\n [expanded]=\"isOpen\"\n [style.--rt-side-menu-sub-item-depth]=\"depth\"\n [rtMod]=\"{ highlighted: isHighlighted }\"\n (expandedChange)=\"onToggleFolder(row)\">\n @if (row.icon) {\n <ng-container [ngTemplateOutlet]=\"iconTpl\" [ngTemplateOutletContext]=\"{ $implicit: row }\" />\n }\n\n <span rtElem=\"title\" qa-dataid=\"side-menu-folder\" [rtTooltip]=\"menuRef.subMenuTooltipsShown() ? row.name : ''\">\n @for (part of row.name | rtSideMenuTitleParts: menuRef.subMenuQuery(); track $index) {\n @if (part.matched) {\n <span rtElem=\"match\">{{ part.text }}</span>\n } @else {\n <span rtElem=\"part\">{{ part.text }}</span>\n }\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div rtElem=\"children\" role=\"group\">\n @for (child of row.submenu; track child.id) {\n <ng-container [ngTemplateOutlet]=\"rowTpl\" [ngTemplateOutletContext]=\"{ $implicit: child, depth: depth + 1 }\" />\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n </ng-template>\n\n <!-- \u0417\u043D\u0430\u0447\u043E\u043A \u0441\u0442\u0440\u043E\u043A\u0438: \u0438\u043C\u044F \u043A\u0438\u0442\u0430 \u0438\u043B\u0438 \u043F\u0430\u0440\u0430 \u0438\u043C\u0435\u043D\u0438 Material, \u0430 \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0439 \u0448\u0430\u0431\u043B\u043E\u043D \u043C\u0435\u043D\u044E. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" />\n } @else if (menuRef.ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: row, icon: row.icon, slot: 'icon' }\" />\n </span>\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-sub-item{--rt-side-menu-sub-item-radius: var(--rt-radius-full);--rt-side-menu-sub-item-transition-duration: var(--rt-duration-fast);--rt-side-menu-sub-item-match-bg: var(--rt-yellow-400);--rt-side-menu-sub-item-match-color: var(--rt-neutral-950);--rt-side-menu-sub-item-match-radius: var(--rt-radius-xs);display:block}.rt-side-menu-sub-item__row{--rt-side-menu-sub-item-depth: 0;display:flex;width:100%;min-height:var(--rt-control-height-md);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-sub-item-radius);background:transparent;color:var(--rt-color-text-primary);cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:var(--rt-space-sm);padding-block:var(--rt-space-2);padding-inline:calc(var(--rt-space-md) * (1 + var(--rt-side-menu-sub-item-depth))) var(--rt-space-sm);text-align:start;text-decoration:none;transition:background-color var(--rt-side-menu-sub-item-transition-duration) ease}.rt-side-menu-sub-item__row:hover,.rt-side-menu-sub-item__row:active,.rt-side-menu-sub-item__row--highlighted{background:var(--rt-color-bg-hover)}.rt-side-menu-sub-item__row:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu-sub-item__row--active{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu-sub-item__icon{flex-shrink:0;color:inherit}.rt-side-menu-sub-item__own-icon{--rt-side-menu-sub-item-own-icon-size: var(--rt-size-4);display:inline-flex;width:var(--rt-side-menu-sub-item-own-icon-size);height:var(--rt-side-menu-sub-item-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu-sub-item__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu-sub-item__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-sub-item__part{color:inherit}.rt-side-menu-sub-item__match{border-radius:var(--rt-side-menu-sub-item-match-radius);background:var(--rt-side-menu-sub-item-match-bg);color:var(--rt-side-menu-sub-item-match-color)}.rt-side-menu-sub-item__action{--rt-side-menu-sub-item-action-ring-width: var(--rt-border-width-thin);--rt-side-menu-sub-item-action-radius: var(--rt-radius-full);flex-shrink:0;border-radius:var(--rt-side-menu-sub-item-action-radius);outline:var(--rt-side-menu-sub-item-action-ring-width) solid transparent;outline-offset:calc(var(--rt-side-menu-sub-item-action-ring-width) * -1)}.rt-side-menu-sub-item__action:hover{outline-color:var(--rt-color-border-strong)}.rt-side-menu-sub-item__favorite{flex-shrink:0;opacity:0}.rt-side-menu-sub-item__favorite--on{--rt-icon-button-color: var(--rt-color-action-primary);opacity:1}@media(hover:hover){.rt-side-menu-sub-item__favorite--remove:hover{--rt-icon-button-color: var(--rt-color-state-danger)}}.rt-side-menu-sub-item__favorite--remove:has(:focus-visible){--rt-icon-button-color: var(--rt-color-state-danger)}@media(hover:none){.rt-side-menu-sub-item__favorite{opacity:1}}.rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{opacity:1}.rt-side-menu-sub-item__folder.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-side-menu-sub-item-transition-duration);--rt-expansion-panel-header-radius: var(--rt-side-menu-sub-item-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: calc(var(--rt-space-md) * (1 + var(--rt-side-menu-sub-item-depth)));--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm);--rt-expansion-panel-header-bg: transparent}.rt-side-menu-sub-item__folder--highlighted.rt-expansion-panel{--rt-expansion-panel-header-bg: var(--rt-color-bg-hover)}.rt-side-menu-sub-item__children{display:flex;flex-direction:column}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__favorite:not(.rt-side-menu-sub-item__favorite--on){overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:hover .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:focus-visible .rt-side-menu-sub-item__favorite,.rt-side-menu--favorite-actions-none .rt-side-menu-sub-item__row:has(:focus-visible) .rt-side-menu-sub-item__favorite{overflow:visible;width:auto;margin-inline-start:0}}}\n"] }]
|
|
744
|
+
}], propDecorators: { item: [{ type: i0.Input, args: [{ isSignal: true, alias: "item", required: true }] }], inFavorites: [{ type: i0.Input, args: [{ isSignal: true, alias: "inFavorites", required: false }] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }] } });
|
|
745
|
+
|
|
746
|
+
const BEM_BLOCK$1 = 'rt-side-menu-favorites';
|
|
747
|
+
/** Панель подменю: уход указателя с неё закрывает подменю, открытое наведением. */
|
|
748
|
+
const SUB_MENU_PANEL = '.rt-side-menu__panel';
|
|
749
|
+
/**
|
|
750
|
+
* Блок избранного вверху подменю: выбранные человеком разделы в порядке его списка. Перенос блока
|
|
751
|
+
* первого кита на части второго, без Material: блок — раскрывающаяся панель кита, как папка
|
|
752
|
+
* подменю, строки тянутся за ручку перетаскиванием CDK.
|
|
753
|
+
*
|
|
754
|
+
* Строки собираются из пунктов самого меню, а не из хранилища: подпись следует языку пунктов, адрес
|
|
755
|
+
* — объявлению. Номер без пункта в меню не показывается, но из списка не уходит. Строку рисует тот же
|
|
756
|
+
* подпункт, что и в списке.
|
|
757
|
+
*
|
|
758
|
+
* Избранное у каждого пункта полосы своё и по умолчанию выключено: блок стоит только в подменю пункта
|
|
759
|
+
* с флагом `favorites`. Без настроек меню (`provideRtSideMenuSettings()`) блока нет вовсе. Показать
|
|
760
|
+
* нечего — блок места не занимает.
|
|
761
|
+
*/
|
|
762
|
+
class RtSideMenuFavoritesComponent {
|
|
763
|
+
#menu;
|
|
764
|
+
#settings;
|
|
765
|
+
#injector;
|
|
766
|
+
#host;
|
|
767
|
+
/** Строку тянут: подменю держится открытым, пока её не бросят. */
|
|
768
|
+
#dragging;
|
|
769
|
+
#section;
|
|
770
|
+
constructor() {
|
|
771
|
+
this.#menu = inject(RT_SIDE_MENU);
|
|
772
|
+
this.#settings = inject(RtSideMenuSettingsService, { optional: true });
|
|
773
|
+
this.#injector = inject(Injector);
|
|
774
|
+
this.#host = inject(ElementRef);
|
|
775
|
+
/** Строку тянут: подменю держится открытым, пока её не бросят. */
|
|
776
|
+
this.#dragging = signal(false, /* @ts-ignore */
|
|
777
|
+
...(ngDevMode ? [{ debugName: "#dragging" }] : /* istanbul ignore next */ []));
|
|
778
|
+
this.#section = computed(() => this.#settings ? sideMenuFavoritesSection(this.#menu.menuItems(), this.#menu.shownSubMenu()) : null, /* @ts-ignore */
|
|
779
|
+
...(ngDevMode ? [{ debugName: "#section" }] : /* istanbul ignore next */ []));
|
|
780
|
+
/** В поиске что-то набрано: блок показывает совпавшие строки раскрытым, сохранённое не трогая. */
|
|
781
|
+
this.searching = computed(() => this.#menu.subMenuQuery().trim() !== '', /* @ts-ignore */
|
|
782
|
+
...(ngDevMode ? [{ debugName: "searching" }] : /* istanbul ignore next */ []));
|
|
783
|
+
this.rows = computed(() => {
|
|
784
|
+
const section = this.#section();
|
|
785
|
+
if (!this.#settings || !section || (this.searching() && !this.#menu.isFavoritesSearchShown())) {
|
|
786
|
+
return [];
|
|
787
|
+
}
|
|
788
|
+
const found = findSideMenuFavoriteItems([section], this.#settings.favoriteIds(this.#menu.menuId())());
|
|
789
|
+
return filterSideMenuItems(found, this.#menu.subMenuQuery());
|
|
790
|
+
}, /* @ts-ignore */
|
|
791
|
+
...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
|
|
792
|
+
/**
|
|
793
|
+
* Блок раздела свёрнут: заголовок и черта остаются, строк нет. Состояние своё у каждого раздела и
|
|
794
|
+
* лежит в настройках меню. Поиск его не меняет: на время поиска блок стоит раскрытым.
|
|
795
|
+
*/
|
|
796
|
+
this.collapsed = computed(() => {
|
|
797
|
+
const section = this.#section();
|
|
798
|
+
return !!section && !!this.#settings?.favoritesCollapsed(this.#menu.menuId())().includes(section.id);
|
|
799
|
+
}, /* @ts-ignore */
|
|
800
|
+
...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
|
|
801
|
+
/**
|
|
802
|
+
* Раскрытие панели блока. Начинается заново с сохранённого выбора при каждой его смене и при
|
|
803
|
+
* входе в поиск и выходе из него: свёрнутый в поиске блок — выбор на время поиска, он не
|
|
804
|
+
* сохраняется и с концом поиска возвращается к сохранённому.
|
|
805
|
+
*/
|
|
806
|
+
this.open = linkedSignal({ ...(ngDevMode ? { debugName: "open" } : /* istanbul ignore next */ {}), source: () => ({ collapsed: this.collapsed(), searching: this.searching() }),
|
|
807
|
+
computation: (state) => !state.collapsed || state.searching });
|
|
808
|
+
/** Заголовок показывает число строк: всегда, у свёрнутого блока или никогда — по входу меню. */
|
|
809
|
+
this.countShown = computed(() => {
|
|
810
|
+
const mode = this.#menu.favoritesCount();
|
|
811
|
+
return mode === 'always' || (mode === 'collapsed' && !this.open());
|
|
812
|
+
}, /* @ts-ignore */
|
|
813
|
+
...(ngDevMode ? [{ debugName: "countShown" }] : /* istanbul ignore next */ []));
|
|
814
|
+
/** Ручки строк по порядку: стрелка возвращает фокус на ручку переставленной строки. */
|
|
815
|
+
this.handles = viewChildren('handle', { ...(ngDevMode ? { debugName: "handles" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
816
|
+
this.dragIcon = this.#settings?.favoritesIcons.drag ?? 'arrows-v';
|
|
817
|
+
/** Узкий экран: подсказка у ручки не показывается — наводиться там нечем. */
|
|
818
|
+
this.narrow = inject(BreakpointsService).narrow;
|
|
819
|
+
this.titleLabel = rtKitLabel('sideMenuFavorites');
|
|
820
|
+
this.dragLabel = rtKitLabel('sideMenuFavoriteDrag');
|
|
821
|
+
this.expandLabel = rtKitLabel('sideMenuFavoritesExpand');
|
|
822
|
+
this.collapseLabel = rtKitLabel('sideMenuFavoritesCollapse');
|
|
823
|
+
this.clickSubMenuAction = output();
|
|
824
|
+
this.clickSubMenuAdditionalAction = output();
|
|
825
|
+
// Блок уничтожили посреди тяги — указатель увёл подменю на другой раздел. CDK закрывает тягу
|
|
826
|
+
// без события конца, и удержание осталось бы навсегда.
|
|
827
|
+
inject(DestroyRef).onDestroy(() => this.#release());
|
|
828
|
+
}
|
|
829
|
+
/** Заголовок нажат: выбор ложится в настройки меню. Во время поиска он не сохраняется. */
|
|
830
|
+
onToggle(expanded) {
|
|
831
|
+
const section = this.#section();
|
|
832
|
+
if (this.#settings && section && !this.searching()) {
|
|
833
|
+
this.#settings.setFavoritesCollapsed(this.#menu.menuId(), section.id, !expanded);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
/** Строка в руке: подменю, открытое наведением, не закрывается, когда рука выходит за панель. */
|
|
837
|
+
onDragStart() {
|
|
838
|
+
this.#dragging.set(true);
|
|
839
|
+
this.#menu.holdSubMenu(true);
|
|
840
|
+
}
|
|
841
|
+
/** Строка брошена за панелью: уход указателя, пропущенный за время тяги, срабатывает сейчас. */
|
|
842
|
+
onDragEnd(event) {
|
|
843
|
+
this.#release();
|
|
844
|
+
const panel = this.#host.nativeElement.closest(SUB_MENU_PANEL);
|
|
845
|
+
if (panel && isSideMenuDropOutside(panel.getBoundingClientRect(), event.dropPoint)) {
|
|
846
|
+
this.#menu.toggleSubMenu();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/** Брошенная вне блока строка ничего не меняет: убирает из избранного кнопка, а не перетаскивание. */
|
|
850
|
+
onDrop(event) {
|
|
851
|
+
if (event.isPointerOverContainer) {
|
|
852
|
+
this.#move(event.previousIndex, event.currentIndex);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
/** Стрелка на ручке переставляет строку на соседнее место, и фокус едет вместе с ручкой. */
|
|
856
|
+
onHandleKey(event, index, step) {
|
|
857
|
+
// Стрелки не уходят в меню: там они ведут подсветку по списку раздела.
|
|
858
|
+
event.preventDefault();
|
|
859
|
+
event.stopPropagation();
|
|
860
|
+
const target = index + step;
|
|
861
|
+
if (target < 0 || target >= this.rows().length) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
this.#move(index, target);
|
|
865
|
+
afterNextRender(() => this.handles()[target]?.nativeElement.querySelector('button')?.focus(), {
|
|
866
|
+
injector: this.#injector,
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
/** Нажатие ручки — не переход по строке: она стоит внутри ссылки пункта. */
|
|
870
|
+
onHandleClick(event) {
|
|
871
|
+
event.preventDefault();
|
|
872
|
+
event.stopPropagation();
|
|
873
|
+
}
|
|
874
|
+
#release() {
|
|
875
|
+
if (!this.#dragging()) {
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
this.#dragging.set(false);
|
|
879
|
+
this.#menu.holdSubMenu(false);
|
|
880
|
+
}
|
|
881
|
+
/** Место в блоке переводится в место списка по свежей записи хранилища: скрытые номера остаются на своих. */
|
|
882
|
+
#move(from, to) {
|
|
883
|
+
if (!this.#settings || from === to) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const visible = this.rows().map((item) => item.id);
|
|
887
|
+
this.#settings.moveVisibleSideMenuFavorite(this.#menu.menuId(), visible, from, to);
|
|
888
|
+
}
|
|
889
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFavoritesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
890
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuFavoritesComponent, isStandalone: true, selector: "rt-side-menu-favorites", outputs: { clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { classAttribute: "rt-side-menu-favorites" }, viewQueries: [{ propertyName: "handles", predicate: ["handle"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n \u0411\u043B\u043E\u043A \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430, \u043A\u0430\u043A \u043F\u0430\u043F\u043A\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0441\u043E \u0437\u0432\u0435\u0437\u0434\u043E\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u043D\u0430\u0447\u043A\u0430 \u043F\u0430\u043F\u043A\u0438. \u0418\u043C\u044F\n \u043A\u043D\u043E\u043F\u043A\u0438 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \u043F\u043E \u0432\u0445\u043E\u0434\u0443 \u043C\u0435\u043D\u044E.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n \u0420\u0443\u0447\u043A\u0430 \u0441\u0442\u043E\u0438\u0442 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438, \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u043F\u0435\u0440\u0435\u0434 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F. \u0421\u0442\u0440\u043E\u043A\u0443 \u0442\u044F\u043D\u0443\u0442\n \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430 \u0440\u0443\u0447\u043A\u0443: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u2014 \u0441\u0441\u044B\u043B\u043A\u0430. \u0421 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044B \u0441\u0442\u0440\u043E\u043A\u0443 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\n \u0441\u0442\u0440\u0435\u043B\u043A\u0438 \u043D\u0430 \u0440\u0443\u0447\u043A\u0435: \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043A\u043B\u0430\u0432\u0438\u0448 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n shape=\"circle\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"], dependencies: [{ kind: "directive", type: CdkDrag, selector: "[cdkDrag]", inputs: ["cdkDragData", "cdkDragLockAxis", "cdkDragRootElement", "cdkDragBoundary", "cdkDragStartDelay", "cdkDragFreeDragPosition", "cdkDragDisabled", "cdkDragConstrainPosition", "cdkDragPreviewClass", "cdkDragPreviewContainer", "cdkDragScale"], outputs: ["cdkDragStarted", "cdkDragReleased", "cdkDragEnded", "cdkDragEntered", "cdkDragExited", "cdkDragDropped", "cdkDragMoved"], exportAs: ["cdkDrag"] }, { kind: "directive", type: CdkDragHandle, selector: "[cdkDragHandle]", inputs: ["cdkDragHandleDisabled"] }, { kind: "directive", type: CdkDropList, selector: "[cdkDropList], cdk-drop-list", inputs: ["cdkDropListConnectedTo", "cdkDropListData", "cdkDropListOrientation", "id", "cdkDropListLockAxis", "cdkDropListDisabled", "cdkDropListSortingDisabled", "cdkDropListEnterPredicate", "cdkDropListSortPredicate", "cdkDropListAutoScrollDisabled", "cdkDropListAutoScrollStep", "cdkDropListElementContainer", "cdkDropListHasAnchor"], outputs: ["cdkDropListDropped", "cdkDropListEntered", "cdkDropListExited", "cdkDropListSorted"], exportAs: ["cdkDropList"] }, { kind: "directive", type:
|
|
891
|
+
// directives
|
|
892
|
+
BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: RtExpansionPanelContentDirective, selector: "ng-template[rtExpansionPanelContent]" }, { kind: "component", type:
|
|
893
|
+
// components
|
|
894
|
+
RtExpansionPanelComponent, selector: "rt-expansion-panel", inputs: ["expanded", "disabled", "hideToggle", "appearance", "headerId", "ariaLabel"], outputs: ["expandedChange"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtSideMenuSubItemComponent, selector: "rt-side-menu-sub-item", inputs: ["item", "inFavorites"], outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
895
|
+
}
|
|
896
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFavoritesComponent, decorators: [{
|
|
897
|
+
type: Component,
|
|
898
|
+
args: [{ selector: 'rt-side-menu-favorites', host: { class: BEM_BLOCK$1 }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
899
|
+
CdkDrag,
|
|
900
|
+
CdkDragHandle,
|
|
901
|
+
CdkDropList,
|
|
902
|
+
// directives
|
|
903
|
+
BlockDirective,
|
|
904
|
+
ElemDirective,
|
|
905
|
+
ModDirective,
|
|
906
|
+
RtExpansionPanelContentDirective,
|
|
907
|
+
// components
|
|
908
|
+
RtExpansionPanelComponent,
|
|
909
|
+
RtIconButtonComponent,
|
|
910
|
+
RtIconComponent,
|
|
911
|
+
RtSideMenuSubItemComponent,
|
|
912
|
+
], template: "<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n \u0411\u043B\u043E\u043A \u2014 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0430\u044F\u0441\u044F \u043F\u0430\u043D\u0435\u043B\u044C \u043A\u0438\u0442\u0430, \u043A\u0430\u043A \u043F\u0430\u043F\u043A\u0430 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0441\u043E \u0437\u0432\u0435\u0437\u0434\u043E\u0439 \u0432\u043C\u0435\u0441\u0442\u043E \u0437\u043D\u0430\u0447\u043A\u0430 \u043F\u0430\u043F\u043A\u0438. \u0418\u043C\u044F\n \u043A\u043D\u043E\u043F\u043A\u0438 \u0437\u0430\u0433\u043E\u043B\u043E\u0432\u043A\u0430 \u043D\u0430\u0437\u044B\u0432\u0430\u0435\u0442 \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0435\u0435 \u043D\u0430\u0436\u0430\u0442\u0438\u0435; \u0447\u0438\u0441\u043B\u043E \u0441\u0442\u0440\u043E\u043A \u2014 \u043F\u043E \u0432\u0445\u043E\u0434\u0443 \u043C\u0435\u043D\u044E.\n -->\n <rt-expansion-panel\n rtElem=\"panel\"\n appearance=\"plain\"\n [ariaLabel]=\"open() ? collapseLabel() : expandLabel()\"\n [(expanded)]=\"open\"\n (expandedChange)=\"onToggle($event)\">\n <rt-icon rtElem=\"star\" size=\"sm\" name=\"star-fill\" [fill]=\"true\" />\n <span rtElem=\"title\" qa-dataid=\"side-menu-favorites-title\">\n {{ titleLabel() }}\n @if (countShown()) {\n ({{ rows().length }})\n }\n </span>\n\n <ng-template rtExpansionPanelContent>\n <div\n rtElem=\"list\"\n role=\"group\"\n qa-dataid=\"side-menu-favorites\"\n cdkDropList\n cdkDropListLockAxis=\"y\"\n [cdkDropListData]=\"rows()\"\n [attr.aria-label]=\"titleLabel()\"\n (cdkDropListDropped)=\"onDrop($event)\">\n @for (item of rows(); track item.id) {\n <div\n rtElem=\"row\"\n qa-dataid=\"side-menu-favorite-row\"\n cdkDrag\n [attr.data-id]=\"item.id\"\n (cdkDragStarted)=\"onDragStart()\"\n (cdkDragEnded)=\"onDragEnd($event)\">\n <!--\n \u0420\u0443\u0447\u043A\u0430 \u0441\u0442\u043E\u0438\u0442 \u0432\u043D\u0443\u0442\u0440\u0438 \u0441\u0442\u0440\u043E\u043A\u0438, \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u0435\u0439 \u043F\u0435\u0440\u0435\u0434 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043F\u043E\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043B\u044F. \u0421\u0442\u0440\u043E\u043A\u0443 \u0442\u044F\u043D\u0443\u0442\n \u0442\u043E\u043B\u044C\u043A\u043E \u0437\u0430 \u0440\u0443\u0447\u043A\u0443: \u043E\u0441\u0442\u0430\u043B\u044C\u043D\u0430\u044F \u0441\u0442\u0440\u043E\u043A\u0430 \u2014 \u0441\u0441\u044B\u043B\u043A\u0430. \u0421 \u043A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u044B \u0441\u0442\u0440\u043E\u043A\u0443 \u043F\u0435\u0440\u0435\u0441\u0442\u0430\u0432\u043B\u044F\u044E\u0442\n \u0441\u0442\u0440\u0435\u043B\u043A\u0438 \u043D\u0430 \u0440\u0443\u0447\u043A\u0435: \u043F\u0435\u0440\u0435\u0442\u0430\u0441\u043A\u0438\u0432\u0430\u043D\u0438\u0435 CDK \u043A\u043B\u0430\u0432\u0438\u0448 \u043D\u0435 \u0437\u043D\u0430\u0435\u0442.\n -->\n <rt-side-menu-sub-item\n rtElem=\"item\"\n [item]=\"item\"\n [inFavorites]=\"true\"\n (clickSubMenuAction)=\"clickSubMenuAction.emit($event)\"\n (clickSubMenuAdditionalAction)=\"clickSubMenuAdditionalAction.emit($event)\">\n <rt-icon-button\n #handle\n rtSideMenuSubItemAction\n cdkDragHandle\n rtElem=\"handle\"\n qa-dataid=\"side-menu-favorite-handle\"\n size=\"sm\"\n shape=\"circle\"\n aria-keyshortcuts=\"ArrowUp ArrowDown\"\n [icon]=\"dragIcon\"\n [ariaLabel]=\"dragLabel()\"\n [tooltip]=\"narrow() ? '' : dragLabel()\"\n (clicked)=\"onHandleClick($event)\"\n (keydown.arrowup)=\"onHandleKey($event, $index, -1)\"\n (keydown.arrowdown)=\"onHandleKey($event, $index, 1)\" />\n </rt-side-menu-sub-item>\n </div>\n }\n </div>\n </ng-template>\n </rt-expansion-panel>\n }\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu-favorites{--rt-side-menu-favorites-radius: var(--rt-radius-full);--rt-side-menu-favorites-divider-width: var(--rt-border-width-thin);display:block}.rt-side-menu-favorites:has(.rt-side-menu-favorites__panel){border-bottom:var(--rt-side-menu-favorites-divider-width) solid var(--rt-color-border-subtle);margin-block-end:var(--rt-space-sm);padding-block-end:var(--rt-space-sm)}.rt-side-menu-favorites__panel.rt-expansion-panel{--rt-expansion-panel-duration: var(--rt-duration-fast);--rt-expansion-panel-header-radius: var(--rt-side-menu-favorites-radius);--rt-expansion-panel-header-min-height: var(--rt-control-height-md);--rt-expansion-panel-header-padding-block: var(--rt-space-2);--rt-expansion-panel-header-padding-start: var(--rt-space-1);--rt-expansion-panel-header-padding-end: var(--rt-space-sm);--rt-expansion-panel-header-gap: var(--rt-space-sm);--rt-expansion-panel-header-font-size: var(--rt-text-sm)}.rt-side-menu-favorites__star{flex-shrink:0;color:var(--rt-color-action-primary)}.rt-side-menu-favorites__title{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}.rt-side-menu-favorites__list{display:flex;flex-direction:column}.rt-side-menu-favorites__item{min-width:0;flex:1 1 auto}.rt-side-menu-favorites__row{display:flex;align-items:center}.rt-side-menu-favorites__row.cdk-drag-preview{--rt-side-menu-favorites-preview-radius: var(--rt-radius-full);--rt-side-menu-favorites-preview-shadow: var(--rt-shadow-md);border-radius:var(--rt-side-menu-favorites-preview-radius);box-shadow:var(--rt-side-menu-favorites-preview-shadow)}.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__item{min-width:0;flex:1 1 auto;border-radius:var(--rt-side-menu-favorites-preview-radius);background:var(--rt-color-bg-surface)}.rt-side-menu-favorites__row.cdk-drag-placeholder{opacity:0}.rt-side-menu-favorites__handle{flex-shrink:0;cursor:grab;opacity:0}.rt-side-menu-favorites__handle:active{cursor:grabbing}@media(hover:none){.rt-side-menu-favorites__handle{opacity:1}}.rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle,.rt-side-menu-favorites__row.cdk-drag-preview .rt-side-menu-favorites__handle{opacity:1}@media(hover:hover){.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__handle{overflow:hidden;width:0;margin-inline-start:calc(var(--rt-space-sm) * -1)}.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:hover .rt-side-menu-favorites__handle,.rt-side-menu--favorite-actions-none .rt-side-menu-favorites__row:has(:focus-visible) .rt-side-menu-favorites__handle{overflow:visible;width:auto;margin-inline-start:0}}}\n"] }]
|
|
913
|
+
}], ctorParameters: () => [], propDecorators: { handles: [{ type: i0.ViewChildren, args: ['handle', { ...{
|
|
914
|
+
read: ElementRef,
|
|
915
|
+
}, isSignal: true }] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }] } });
|
|
916
|
+
|
|
917
|
+
/**
|
|
918
|
+
* Ходьба по подменю с клавиатуры. Проверяется вызовом, без подъёма компонента.
|
|
919
|
+
*
|
|
920
|
+
* Клавиши слушает поле поиска: фокус из поля не уходит, и между стрелками работает набор текста.
|
|
921
|
+
* Съедаются только клавиши ходьбы — на остальные ответ отрицательный, иначе поле перестанет
|
|
922
|
+
* принимать буквы. Подсветка держится по номеру пункта: видимый список пересобирается на каждую
|
|
923
|
+
* букву запроса.
|
|
924
|
+
*/
|
|
925
|
+
class RtSubMenuKeyboard {
|
|
926
|
+
#highlightedId;
|
|
927
|
+
/**
|
|
928
|
+
* Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от
|
|
929
|
+
* активного адреса, и свернуть такую папку можно, только помня об этом отдельно.
|
|
930
|
+
*/
|
|
931
|
+
#openedIds;
|
|
932
|
+
#closedIds;
|
|
933
|
+
#hooks;
|
|
934
|
+
constructor(hooks) {
|
|
935
|
+
this.#highlightedId = signal(null, /* @ts-ignore */
|
|
936
|
+
...(ngDevMode ? [{ debugName: "#highlightedId" }] : /* istanbul ignore next */ []));
|
|
937
|
+
/**
|
|
938
|
+
* Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от
|
|
939
|
+
* активного адреса, и свернуть такую папку можно, только помня об этом отдельно.
|
|
940
|
+
*/
|
|
941
|
+
this.#openedIds = signal([], /* @ts-ignore */
|
|
942
|
+
...(ngDevMode ? [{ debugName: "#openedIds" }] : /* istanbul ignore next */ []));
|
|
943
|
+
this.#closedIds = signal([], /* @ts-ignore */
|
|
944
|
+
...(ngDevMode ? [{ debugName: "#closedIds" }] : /* istanbul ignore next */ []));
|
|
945
|
+
this.highlightedId = this.#highlightedId.asReadonly();
|
|
946
|
+
this.openedIds = this.#openedIds.asReadonly();
|
|
947
|
+
this.closedIds = this.#closedIds.asReadonly();
|
|
948
|
+
this.#hooks = hooks;
|
|
949
|
+
}
|
|
950
|
+
/** Всё, что клавиатура успела наделать, снимается: набран новый запрос или подменю закрылось. */
|
|
951
|
+
reset() {
|
|
952
|
+
this.#highlightedId.set(null);
|
|
953
|
+
this.#openedIds.set([]);
|
|
954
|
+
this.#closedIds.set([]);
|
|
955
|
+
}
|
|
956
|
+
/** Нажата клавиша в поле поиска. Отвечает, съедена ли она: только съеденная отменяет умолчание. */
|
|
957
|
+
press(key, items, expandedIds) {
|
|
958
|
+
const walk = walkSideMenuItems(items, expandedIds);
|
|
959
|
+
const current = walk.find((item) => item.id === this.#highlightedId()) ?? null;
|
|
960
|
+
switch (key) {
|
|
961
|
+
case 'ArrowDown':
|
|
962
|
+
case 'ArrowUp':
|
|
963
|
+
this.#highlightedId.set(stepSideMenuHighlight(walk, this.#highlightedId(), key === 'ArrowDown' ? 1 : -1));
|
|
964
|
+
return true;
|
|
965
|
+
case 'ArrowRight':
|
|
966
|
+
return this.setFolderOpen(current, true);
|
|
967
|
+
case 'ArrowLeft':
|
|
968
|
+
return this.setFolderOpen(current, false);
|
|
969
|
+
case 'Enter':
|
|
970
|
+
return this.#enter(current);
|
|
971
|
+
case 'Escape':
|
|
972
|
+
this.reset();
|
|
973
|
+
this.#hooks.clearQuery();
|
|
974
|
+
return true;
|
|
975
|
+
default:
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
/** Enter открывает подсвеченное: у пункта со ссылкой это переход, у папки — раскрытие. */
|
|
980
|
+
#enter(item) {
|
|
981
|
+
if (item === null) {
|
|
982
|
+
return false;
|
|
983
|
+
}
|
|
984
|
+
if (item.link) {
|
|
985
|
+
this.#hooks.open(item);
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
return this.setFolderOpen(item, true);
|
|
989
|
+
}
|
|
990
|
+
/** Раскрыть или свернуть папку: стрелкой, Enter или нажатием её заголовка. */
|
|
991
|
+
setFolderOpen(item, open) {
|
|
992
|
+
if (item === null || !item.submenu?.length) {
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
const without = (ids) => ids.filter((id) => id !== item.id);
|
|
996
|
+
this.#openedIds.update((ids) => (open ? [...without(ids), item.id] : without(ids)));
|
|
997
|
+
this.#closedIds.update((ids) => (open ? without(ids) : [...without(ids), item.id]));
|
|
998
|
+
return true;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Тяга ширины закреплённого подменю: натянутая ширина и снятие слушателей. Механика событий —
|
|
1004
|
+
* `startSideMenuWidthDrag` в логике рядом, здесь держится только то, что живёт дольше одного события.
|
|
1005
|
+
*/
|
|
1006
|
+
class RtSideMenuResize {
|
|
1007
|
+
/** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */
|
|
1008
|
+
#draggedWidth;
|
|
1009
|
+
#listen;
|
|
1010
|
+
#hooks;
|
|
1011
|
+
#stop;
|
|
1012
|
+
/**
|
|
1013
|
+
* Меню, разрушенное посреди тяги, кончает её здесь же: иначе накрытие чужих кадров, снимаемое по
|
|
1014
|
+
* концу, осталось бы стоять.
|
|
1015
|
+
*/
|
|
1016
|
+
constructor(listen, hooks, destroyRef) {
|
|
1017
|
+
/** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */
|
|
1018
|
+
this.#draggedWidth = signal(null, /* @ts-ignore */
|
|
1019
|
+
...(ngDevMode ? [{ debugName: "#draggedWidth" }] : /* istanbul ignore next */ []));
|
|
1020
|
+
this.#stop = null;
|
|
1021
|
+
this.draggedWidth = this.#draggedWidth.asReadonly();
|
|
1022
|
+
/**
|
|
1023
|
+
* Ширина для диктора: только то, что кит знает сам. Не названо ничего и ничего не тянули — числа
|
|
1024
|
+
* нет: выдуманное назвало бы ширину, которой панель не нарисована.
|
|
1025
|
+
*/
|
|
1026
|
+
this.valueNow = computed(() => {
|
|
1027
|
+
const width = this.#draggedWidth() ?? this.#hooks.namedWidth();
|
|
1028
|
+
return width === null ? null : clampSideMenuWidth(width);
|
|
1029
|
+
}, /* @ts-ignore */
|
|
1030
|
+
...(ngDevMode ? [{ debugName: "valueNow" }] : /* istanbul ignore next */ []));
|
|
1031
|
+
this.#listen = listen;
|
|
1032
|
+
this.#hooks = hooks;
|
|
1033
|
+
destroyRef?.onDestroy(() => this.finish());
|
|
1034
|
+
}
|
|
1035
|
+
/** Тяга идёт. Второе нажатие при начатой тяге ничего не начинает: указатель уже захвачен. */
|
|
1036
|
+
isRunning() {
|
|
1037
|
+
return this.#stop !== null;
|
|
1038
|
+
}
|
|
1039
|
+
/**
|
|
1040
|
+
* Нажата клавиша на ручке. Просьба о ширине уходит сразу: жеста нет, и держать её не до чего.
|
|
1041
|
+
* Отрицательный ответ — клавиша не о ширине, и умолчание у неё не отменяется.
|
|
1042
|
+
*/
|
|
1043
|
+
pressKey(key, from) {
|
|
1044
|
+
const width = sideMenuWidthByKey(key, from);
|
|
1045
|
+
if (width === null) {
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
this.#hooks.askWidth(width);
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
start(event, startWidth) {
|
|
1052
|
+
if (this.isRunning()) {
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const stop = startSideMenuWidthDrag(event, startWidth, this.#listen, {
|
|
1056
|
+
onWidth: (width) => this.#draggedWidth.set(width),
|
|
1057
|
+
onEnd: () => this.finish(),
|
|
1058
|
+
});
|
|
1059
|
+
if (stop !== null) {
|
|
1060
|
+
this.#stop = stop;
|
|
1061
|
+
this.#hooks.started();
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/** Конец тяги. Наружу уходит то число, которым панель нарисована: нижний предел держит оформление. */
|
|
1065
|
+
finish() {
|
|
1066
|
+
if (!this.isRunning()) {
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
// Замер идёт до сброса натянутой ширины: сбросив её, панель перерисуют, и мерить будет нечего.
|
|
1070
|
+
const width = this.#draggedWidth();
|
|
1071
|
+
const drawn = width === null ? null : reportedSideMenuWidth(width, this.#hooks.panel());
|
|
1072
|
+
this.#stop?.();
|
|
1073
|
+
this.#stop = null;
|
|
1074
|
+
this.#draggedWidth.set(null);
|
|
1075
|
+
if (drawn !== null) {
|
|
1076
|
+
this.#hooks.askWidth(drawn);
|
|
1077
|
+
}
|
|
1078
|
+
this.#hooks.ended();
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
/** Шапка бокового меню: над полосой пунктов. */
|
|
1083
|
+
class RtSideMenuHeaderDirective {
|
|
1084
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHeaderDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1085
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuHeaderDirective, isStandalone: true, selector: "[rtSideMenuHeader]", ngImport: i0 }); }
|
|
1086
|
+
}
|
|
1087
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuHeaderDirective, decorators: [{
|
|
1088
|
+
type: Directive,
|
|
1089
|
+
args: [{
|
|
1090
|
+
selector: '[rtSideMenuHeader]',
|
|
1091
|
+
}]
|
|
1092
|
+
}] });
|
|
1093
|
+
/** Подвал бокового меню: под полосой пунктов. */
|
|
1094
|
+
class RtSideMenuFooterDirective {
|
|
1095
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1096
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuFooterDirective, isStandalone: true, selector: "[rtSideMenuFooter]", ngImport: i0 }); }
|
|
1097
|
+
}
|
|
1098
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuFooterDirective, decorators: [{
|
|
1099
|
+
type: Directive,
|
|
1100
|
+
args: [{
|
|
1101
|
+
selector: '[rtSideMenuFooter]',
|
|
1102
|
+
}]
|
|
1103
|
+
}] });
|
|
1104
|
+
/**
|
|
1105
|
+
* Помечает `<ng-template>` своего значка пунктов меню — для имён, которых нет ни в наборе кита, ни
|
|
1106
|
+
* в перечне имён Material. Шаблон один на меню и рисует и значок строки, и значок кнопки строки; кит
|
|
1107
|
+
* ставит его на место значка размером значка и цветом строки. Рисунок выбирают по `icon` контекста.
|
|
1108
|
+
* Значок, который кит рисует сам, шаблон не перебивает.
|
|
1109
|
+
*
|
|
1110
|
+
* ```html
|
|
1111
|
+
* <rt-side-menu [menuItems]="items">
|
|
1112
|
+
* <ng-template rtSideMenuIcon let-icon="icon"><img [src]="'/icons/' + icon + '.svg'" alt="" /></ng-template>
|
|
1113
|
+
* </rt-side-menu>
|
|
1114
|
+
* ```
|
|
1115
|
+
*/
|
|
1116
|
+
class RtSideMenuIconDirective {
|
|
1117
|
+
constructor() {
|
|
1118
|
+
this.templateRef = inject(TemplateRef);
|
|
1119
|
+
}
|
|
1120
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате; убрать его нечем, подпись задаёт каркас
|
|
1121
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
1125
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.6", type: RtSideMenuIconDirective, isStandalone: true, selector: "ng-template[rtSideMenuIcon]", ngImport: i0 }); }
|
|
1126
|
+
}
|
|
1127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuIconDirective, decorators: [{
|
|
1128
|
+
type: Directive,
|
|
1129
|
+
args: [{
|
|
1130
|
+
selector: 'ng-template[rtSideMenuIcon]',
|
|
1131
|
+
}]
|
|
1132
|
+
}] });
|
|
1133
|
+
|
|
1134
|
+
const BEM_BLOCK = 'rt-side-menu';
|
|
1135
|
+
/**
|
|
1136
|
+
* Боковая навигация: полоса пунктов и подменю раздела рядом. Перенос бокового меню первого кита на
|
|
1137
|
+
* части второго; `rt-menu` — другое семейство, выпадающее меню действий.
|
|
1138
|
+
*/
|
|
1139
|
+
class RtSideMenuComponent {
|
|
1140
|
+
#breakpoints;
|
|
1141
|
+
#renderer;
|
|
1142
|
+
/** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */
|
|
1143
|
+
#settings;
|
|
1144
|
+
/** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */
|
|
1145
|
+
#resize;
|
|
1146
|
+
/** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */
|
|
1147
|
+
#keyboard;
|
|
1148
|
+
/** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */
|
|
1149
|
+
#hoverOpened;
|
|
1150
|
+
/** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */
|
|
1151
|
+
#searchHeld;
|
|
1152
|
+
/** Строку избранного тянут: уход указателя за панель подменю не закрывает. */
|
|
1153
|
+
#dragHeld;
|
|
1154
|
+
/**
|
|
1155
|
+
* Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки
|
|
1156
|
+
* закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.
|
|
1157
|
+
*/
|
|
1158
|
+
#mode;
|
|
1159
|
+
#width;
|
|
1160
|
+
#pinnedSubMenu;
|
|
1161
|
+
constructor() {
|
|
1162
|
+
this.#breakpoints = inject(BreakpointsService);
|
|
1163
|
+
this.#renderer = inject(Renderer2);
|
|
1164
|
+
/** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */
|
|
1165
|
+
this.#settings = inject(RtSideMenuSettingsService, { optional: true });
|
|
1166
|
+
/** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */
|
|
1167
|
+
this.#resize = new RtSideMenuResize((target, name, handler) => this.#renderer.listen(target, name, handler), {
|
|
1168
|
+
askWidth: (width) => {
|
|
1169
|
+
this.#settings?.setSubMenuWidth(this.menuId(), width);
|
|
1170
|
+
this.subMenuWidthChange.emit(width);
|
|
1171
|
+
},
|
|
1172
|
+
started: () => this.subMenuResizeStart.emit(),
|
|
1173
|
+
ended: () => this.subMenuResizeEnd.emit(),
|
|
1174
|
+
panel: () => this.panelRef()?.nativeElement ?? null,
|
|
1175
|
+
namedWidth: () => this.#width(),
|
|
1176
|
+
}, inject(DestroyRef));
|
|
1177
|
+
/** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */
|
|
1178
|
+
this.#keyboard = new RtSubMenuKeyboard({
|
|
1179
|
+
open: (item) => this.#pressRow(item),
|
|
1180
|
+
clearQuery: () => this.searchControl.setValue(''),
|
|
1181
|
+
});
|
|
1182
|
+
/** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */
|
|
1183
|
+
this.#hoverOpened = signal(false, /* @ts-ignore */
|
|
1184
|
+
...(ngDevMode ? [{ debugName: "#hoverOpened" }] : /* istanbul ignore next */ []));
|
|
1185
|
+
/** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */
|
|
1186
|
+
this.#searchHeld = signal(false, /* @ts-ignore */
|
|
1187
|
+
...(ngDevMode ? [{ debugName: "#searchHeld" }] : /* istanbul ignore next */ []));
|
|
1188
|
+
/** Строку избранного тянут: уход указателя за панель подменю не закрывает. */
|
|
1189
|
+
this.#dragHeld = signal(false, /* @ts-ignore */
|
|
1190
|
+
...(ngDevMode ? [{ debugName: "#dragHeld" }] : /* istanbul ignore next */ []));
|
|
1191
|
+
/**
|
|
1192
|
+
* Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки
|
|
1193
|
+
* закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.
|
|
1194
|
+
*/
|
|
1195
|
+
this.#mode = computed(() => this.subMenuMode() ?? (this.pinShown() ? this.#settings?.subMenuMode(this.menuId())() : undefined) ?? 'hover', /* @ts-ignore */
|
|
1196
|
+
...(ngDevMode ? [{ debugName: "#mode" }] : /* istanbul ignore next */ []));
|
|
1197
|
+
this.#width = computed(() => this.subMenuWidth() ?? this.#settings?.subMenuWidth(this.menuId())() ?? null, /* @ts-ignore */
|
|
1198
|
+
...(ngDevMode ? [{ debugName: "#width" }] : /* istanbul ignore next */ []));
|
|
1199
|
+
this.#pinnedSubMenu = computed(() => this.isPinned() ? pinnedSideMenuItems(this.selectedSubMenu(), this.activeMenuIds(), this.menuItems()) : [], /* @ts-ignore */
|
|
1200
|
+
...(ngDevMode ? [{ debugName: "#pinnedSubMenu" }] : /* istanbul ignore next */ []));
|
|
1201
|
+
this.searchControl = new FormControl('');
|
|
1202
|
+
this.panelRef = viewChild('panel', /* @ts-ignore */
|
|
1203
|
+
...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
1204
|
+
this.headerTpl = contentChild(RtSideMenuHeaderDirective, { ...(ngDevMode ? { debugName: "headerTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
|
|
1205
|
+
this.footerTpl = contentChild(RtSideMenuFooterDirective, { ...(ngDevMode ? { debugName: "footerTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
|
|
1206
|
+
this.searchLabel = rtKitLabel('sideMenuSearch');
|
|
1207
|
+
this.pinLabel = rtKitLabel('sideMenuPin');
|
|
1208
|
+
this.unpinLabel = rtKitLabel('sideMenuUnpin');
|
|
1209
|
+
this.nothingFoundLabel = rtKitLabel('sideMenuNothingFound');
|
|
1210
|
+
this.resizeLabel = rtKitLabel('sideMenuResize');
|
|
1211
|
+
this.backLabel = rtKitLabel('sideMenuBack');
|
|
1212
|
+
/** Экран узкий: замер кита, и другого источника у этого признака нет. */
|
|
1213
|
+
this.narrow = this.#breakpoints.narrow;
|
|
1214
|
+
this.resizeValueNow = this.#resize.valueNow;
|
|
1215
|
+
this.resizeValueMin = RT_SIDE_MENU_WIDTH_MIN;
|
|
1216
|
+
this.resizeValueMax = RT_SIDE_MENU_WIDTH_MAX;
|
|
1217
|
+
/** Натянутая ширина панели. Своего выбора нет — свойство не ставится, и ширину берёт оформление. */
|
|
1218
|
+
this.panelWidthStyle = computed(() => {
|
|
1219
|
+
const width = this.#resize.draggedWidth() ?? this.#width();
|
|
1220
|
+
return width === null ? null : `${clampSideMenuWidth(width)}px`;
|
|
1221
|
+
}, /* @ts-ignore */
|
|
1222
|
+
...(ngDevMode ? [{ debugName: "panelWidthStyle" }] : /* istanbul ignore next */ []));
|
|
1223
|
+
/** Закрепление действует только на широком экране: на узком подменю занимает экран целиком. */
|
|
1224
|
+
this.isPinned = computed(() => this.#mode() === 'pinned' && !this.narrow(), /* @ts-ignore */
|
|
1225
|
+
...(ngDevMode ? [{ debugName: "isPinned" }] : /* istanbul ignore next */ []));
|
|
1226
|
+
/** Подменю держится набором запроса: уходом указателя не закрывается. */
|
|
1227
|
+
this.isSearchHeld = computed(() => this.#searchHeld() && !this.isPinned(), /* @ts-ignore */
|
|
1228
|
+
...(ngDevMode ? [{ debugName: "isSearchHeld" }] : /* istanbul ignore next */ []));
|
|
1229
|
+
/** Закреплённое подменю открыто, пока ему есть что показать: указатель на это не влияет. */
|
|
1230
|
+
this.subMenuOpened = computed(() => this.isPinned() ? this.#pinnedSubMenu().length > 0 : this.#hoverOpened(), /* @ts-ignore */
|
|
1231
|
+
...(ngDevMode ? [{ debugName: "subMenuOpened" }] : /* istanbul ignore next */ []));
|
|
1232
|
+
this.visibleSubMenuItems = computed(() => filterSideMenuItems(this.shownSubMenu(), this.subMenuQuery()), /* @ts-ignore */
|
|
1233
|
+
...(ngDevMode ? [{ debugName: "visibleSubMenuItems" }] : /* istanbul ignore next */ []));
|
|
1234
|
+
this.isNothingFound = computed(() => this.subMenuQuery().trim() !== '' && this.visibleSubMenuItems().length === 0, /* @ts-ignore */
|
|
1235
|
+
...(ngDevMode ? [{ debugName: "isNothingFound" }] : /* istanbul ignore next */ []));
|
|
1236
|
+
/** Пункт полосы подсвечен активным: выбранный человеком, а без выбора — пункт текущего адреса. */
|
|
1237
|
+
this.activeRailIds = computed(() => {
|
|
1238
|
+
const selected = this.selectedItem();
|
|
1239
|
+
return selected ? [selected.id] : this.activeMenuIds();
|
|
1240
|
+
}, /* @ts-ignore */
|
|
1241
|
+
...(ngDevMode ? [{ debugName: "activeRailIds" }] : /* istanbul ignore next */ []));
|
|
1242
|
+
/** Набор, который подменю наполняет сейчас, до отбора поиском. */
|
|
1243
|
+
this.shownSubMenu = computed(() => this.isPinned() ? this.#pinnedSubMenu() : (this.selectedSubMenu() ?? []), /* @ts-ignore */
|
|
1244
|
+
...(ngDevMode ? [{ debugName: "shownSubMenu" }] : /* istanbul ignore next */ []));
|
|
1245
|
+
this.selectedItem = signal(null, /* @ts-ignore */
|
|
1246
|
+
...(ngDevMode ? [{ debugName: "selectedItem" }] : /* istanbul ignore next */ []));
|
|
1247
|
+
this.selectedSubMenu = signal(null, /* @ts-ignore */
|
|
1248
|
+
...(ngDevMode ? [{ debugName: "selectedSubMenu" }] : /* istanbul ignore next */ []));
|
|
1249
|
+
/** Что набрано в поиске: строка берёт запрос отсюда, чтобы отметить совпавшее. */
|
|
1250
|
+
this.subMenuQuery = signal('', /* @ts-ignore */
|
|
1251
|
+
...(ngDevMode ? [{ debugName: "subMenuQuery" }] : /* istanbul ignore next */ []));
|
|
1252
|
+
this.highlightedMenuId = this.#keyboard.highlightedId;
|
|
1253
|
+
/**
|
|
1254
|
+
* Какие папки раскрыты. Пустой запрос оставляет раздел текущего адреса; непустой добавляет
|
|
1255
|
+
* папки с совпадением — иначе найденное лежит за закрытым заголовком.
|
|
1256
|
+
*/
|
|
1257
|
+
this.expandedMenuIds = computed(() => {
|
|
1258
|
+
const found = this.subMenuQuery().trim() === '' ? [] : sideMenuIdsToExpand(this.visibleSubMenuItems());
|
|
1259
|
+
const closed = this.#keyboard.closedIds();
|
|
1260
|
+
return [...this.activeMenuIds(), ...found, ...this.#keyboard.openedIds()].filter((id) => !closed.includes(id));
|
|
1261
|
+
}, /* @ts-ignore */
|
|
1262
|
+
...(ngDevMode ? [{ debugName: "expandedMenuIds" }] : /* istanbul ignore next */ []));
|
|
1263
|
+
this.activeMenuIds = input.required(/* @ts-ignore */
|
|
1264
|
+
...(ngDevMode ? [{ debugName: "activeMenuIds" }] : /* istanbul ignore next */ []));
|
|
1265
|
+
this.menuItems = input([], /* @ts-ignore */
|
|
1266
|
+
...(ngDevMode ? [{ debugName: "menuItems" }] : /* istanbul ignore next */ []));
|
|
1267
|
+
/** Режим подменю от приложения. Задан — важнее сохранённого; нет ни того, ни другого — наведение. */
|
|
1268
|
+
this.subMenuMode = input(undefined, /* @ts-ignore */
|
|
1269
|
+
...(ngDevMode ? [{ debugName: "subMenuMode" }] : /* istanbul ignore next */ []));
|
|
1270
|
+
/** Ширина закреплённого подменю в пикселях. Пусто — сохранённая, её нет — оформление. */
|
|
1271
|
+
this.subMenuWidth = input(undefined, /* @ts-ignore */
|
|
1272
|
+
...(ngDevMode ? [{ debugName: "subMenuWidth" }] : /* istanbul ignore next */ []));
|
|
1273
|
+
/** Номер, под которым меню хранит свои настройки: у двух меню приложения — два номера. */
|
|
1274
|
+
this.menuId = input(RT_SIDE_MENU_DEFAULT_ID, { ...(ngDevMode ? { debugName: "menuId" } : /* istanbul ignore next */ {}), transform: normalizeSideMenuId });
|
|
1275
|
+
/** Число строк в заголовке блока избранного: всегда, у свёрнутого блока или никогда. */
|
|
1276
|
+
this.favoritesCount = input('collapsed', /* @ts-ignore */
|
|
1277
|
+
...(ngDevMode ? [{ debugName: "favoritesCount" }] : /* istanbul ignore next */ []));
|
|
1278
|
+
/** Место под кнопки избранного, ждущие наведения: держать всегда или отдавать подписи. */
|
|
1279
|
+
this.favoriteActionsReserve = input('none', /* @ts-ignore */
|
|
1280
|
+
...(ngDevMode ? [{ debugName: "favoriteActionsReserve" }] : /* istanbul ignore next */ []));
|
|
1281
|
+
/** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */
|
|
1282
|
+
this.isFavoritesSearchShown = input(true, { ...(ngDevMode ? { debugName: "isFavoritesSearchShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1283
|
+
/** Кнопка закрепления в шапке подменю; выключено — подменю только всплывает, если режим не задан входом. */
|
|
1284
|
+
this.pinShown = input(true, { ...(ngDevMode ? { debugName: "pinShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1285
|
+
/** Подсказки строк подменю — у подписей и у кнопок строк. Доступные имена остаются. */
|
|
1286
|
+
this.subMenuTooltipsShown = input(true, { ...(ngDevMode ? { debugName: "subMenuTooltipsShown" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1287
|
+
this.subMenuModeChange = output();
|
|
1288
|
+
this.subMenuWidthChange = output();
|
|
1289
|
+
/** Начало и конец тяги: по ним потребитель накрывает чужие кадры и снимает накрытие. */
|
|
1290
|
+
this.subMenuResizeStart = output();
|
|
1291
|
+
this.subMenuResizeEnd = output();
|
|
1292
|
+
this.closeMobileMenuAction = output();
|
|
1293
|
+
this.clickSubMenuAction = output();
|
|
1294
|
+
this.clickSubMenuAdditionalAction = output();
|
|
1295
|
+
/** Свой значок пунктов, чьё имя кит не рисует; подпункты читают его через токен меню. */
|
|
1296
|
+
this.ownIconTpl = contentChild(RtSideMenuIconDirective, { ...(ngDevMode ? { debugName: "ownIconTpl" } : /* istanbul ignore next */ {}), read: TemplateRef });
|
|
1297
|
+
this.searchControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((query) => {
|
|
1298
|
+
this.subMenuQuery.set(query ?? '');
|
|
1299
|
+
this.onSearchHold();
|
|
1300
|
+
// Видимый список пересобрался: подсветка и раскрытое стрелками к нему больше не относятся.
|
|
1301
|
+
this.#keyboard.reset();
|
|
1302
|
+
});
|
|
1303
|
+
// Имя без значка кита и без пары рисует пункт без значка, и пропуск без предупреждения не заметен.
|
|
1304
|
+
if (isDevMode()) {
|
|
1305
|
+
effect(() => {
|
|
1306
|
+
const unpaired = unpairedSideMenuIcons(this.menuItems(), this.ownIconTpl() !== undefined);
|
|
1307
|
+
if (unpaired.length) {
|
|
1308
|
+
const names = unpaired.map((name) => `«${name}»`).join(', ');
|
|
1309
|
+
// eslint-disable-next-line no-console -- предупреждение разработчику приложения: другого канала у кита нет
|
|
1310
|
+
console.warn(`rt-side-menu «${this.menuId()}»: значков ${names} нет ни в наборе кита, ни в перечне имён Material. ` +
|
|
1311
|
+
'Задайте имя кита или свой значок через <ng-template rtSideMenuIcon>.');
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
onClickMenu(item) {
|
|
1317
|
+
if (this.isPinned()) {
|
|
1318
|
+
this.#pickPinnedSubMenu(item);
|
|
1319
|
+
this.closeMobileMenu();
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
this.selectedItem.set(item);
|
|
1323
|
+
if (item.submenu) {
|
|
1324
|
+
this.selectedSubMenu.set(item.submenu);
|
|
1325
|
+
this.#hoverOpened.set(true);
|
|
1326
|
+
}
|
|
1327
|
+
else if (this.selectedSubMenu()) {
|
|
1328
|
+
this.closeSubMenu();
|
|
1329
|
+
}
|
|
1330
|
+
else {
|
|
1331
|
+
// У пункта нет подменю, и открытого подменю тоже нет — закрывать нечего.
|
|
1332
|
+
}
|
|
1333
|
+
if (item.link) {
|
|
1334
|
+
this.closeMobileMenu();
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
onClickSubMenu({ item, event }) {
|
|
1338
|
+
if (!item.link) {
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
this.clickSubMenuAction.emit({ item, event });
|
|
1342
|
+
if (!this.isPinned()) {
|
|
1343
|
+
this.closeSubMenu();
|
|
1344
|
+
}
|
|
1345
|
+
this.closeMobileMenu();
|
|
1346
|
+
}
|
|
1347
|
+
onClickSubMenuAdditional(payload) {
|
|
1348
|
+
this.clickSubMenuAdditionalAction.emit(payload);
|
|
1349
|
+
}
|
|
1350
|
+
onBackToMainMenu() {
|
|
1351
|
+
this.selectedItem.set(null);
|
|
1352
|
+
this.selectedSubMenu.set(null);
|
|
1353
|
+
this.searchControl.setValue('', { emitEvent: false });
|
|
1354
|
+
this.subMenuQuery.set('');
|
|
1355
|
+
}
|
|
1356
|
+
/** Наведение на пункт полосы открывает его подменю; уход указателя с панели — без пункта. */
|
|
1357
|
+
toggleSubMenu(item) {
|
|
1358
|
+
if (this.isPinned() || (item === undefined && (this.#searchHeld() || this.#dragHeld()))) {
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
if (item?.submenu) {
|
|
1362
|
+
this.selectedSubMenu.set(item.submenu);
|
|
1363
|
+
this.#hoverOpened.set(true);
|
|
1364
|
+
}
|
|
1365
|
+
else if (this.selectedItem()?.submenu) {
|
|
1366
|
+
this.selectedSubMenu.set(this.selectedItem()?.submenu ?? null);
|
|
1367
|
+
}
|
|
1368
|
+
else {
|
|
1369
|
+
this.closeSubMenu();
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
closeSubMenu() {
|
|
1373
|
+
this.selectedItem.set(null);
|
|
1374
|
+
this.selectedSubMenu.set(null);
|
|
1375
|
+
this.searchControl.setValue('', { emitEvent: false });
|
|
1376
|
+
this.subMenuQuery.set('');
|
|
1377
|
+
this.#hoverOpened.set(false);
|
|
1378
|
+
this.#searchHeld.set(false);
|
|
1379
|
+
this.#keyboard.reset();
|
|
1380
|
+
}
|
|
1381
|
+
holdSubMenu(held) {
|
|
1382
|
+
this.#dragHeld.set(held);
|
|
1383
|
+
}
|
|
1384
|
+
toggleFolder(item) {
|
|
1385
|
+
this.#keyboard.setFolderOpen(item, !this.expandedMenuIds().includes(item.id));
|
|
1386
|
+
}
|
|
1387
|
+
/** Человек взялся за поле поиска: дальше подменю закрывает нажатие снаружи, а не уход указателя. */
|
|
1388
|
+
onSearchHold() {
|
|
1389
|
+
if (!this.isPinned()) {
|
|
1390
|
+
this.#searchHeld.set(true);
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
/** Клавиши поля поиска раздаются списку; умолчание отменяется только у съеденной. */
|
|
1394
|
+
onSearchKeydown(event) {
|
|
1395
|
+
if (this.#keyboard.press(event.key, this.visibleSubMenuItems(), this.expandedMenuIds())) {
|
|
1396
|
+
event.preventDefault();
|
|
1397
|
+
event.stopPropagation();
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
/** Нажат переключатель режима: выбор уходит в настройки меню и наружу. */
|
|
1401
|
+
onSubMenuModeToggle() {
|
|
1402
|
+
const mode = this.isPinned() ? 'hover' : 'pinned';
|
|
1403
|
+
this.#settings?.setSubMenuMode(this.menuId(), mode);
|
|
1404
|
+
this.subMenuModeChange.emit(mode);
|
|
1405
|
+
}
|
|
1406
|
+
onResizeStart(event) {
|
|
1407
|
+
if (!this.isPinned() || this.#resize.isRunning() || event.button !== 0) {
|
|
1408
|
+
return;
|
|
1409
|
+
}
|
|
1410
|
+
// Иначе указатель выделяет подписи пунктов, и тяга выглядит выделением текста.
|
|
1411
|
+
event.preventDefault();
|
|
1412
|
+
this.#resize.start(event, this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null));
|
|
1413
|
+
}
|
|
1414
|
+
onResizeKeydown(event) {
|
|
1415
|
+
const from = this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null);
|
|
1416
|
+
if (this.isPinned() && this.#resize.pressKey(event.key, from)) {
|
|
1417
|
+
event.preventDefault();
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
closeMobileMenu() {
|
|
1421
|
+
if (this.narrow()) {
|
|
1422
|
+
this.closeMobileMenuAction.emit();
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* Нажат пункт полосы, пока подменю закреплено: нажатие — выбор раздела. Пункт со своим адресом и
|
|
1427
|
+
* без разделов выбор снимает: у страницы нет разделов, и панель прежнего раздела врала бы.
|
|
1428
|
+
*/
|
|
1429
|
+
#pickPinnedSubMenu(item) {
|
|
1430
|
+
if (item.submenu?.length) {
|
|
1431
|
+
this.selectedItem.set(item);
|
|
1432
|
+
this.selectedSubMenu.set(item.submenu);
|
|
1433
|
+
}
|
|
1434
|
+
else if (item.link) {
|
|
1435
|
+
this.selectedItem.set(null);
|
|
1436
|
+
this.selectedSubMenu.set(null);
|
|
1437
|
+
}
|
|
1438
|
+
else {
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
this.searchControl.setValue('', { emitEvent: false });
|
|
1442
|
+
this.subMenuQuery.set('');
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Строка ищется по номеру среди узлов панели с id и нажимается как мышью: у ссылки номер стоит
|
|
1446
|
+
* на ней самой, у папки — на кнопке заголовка её раскрывающейся панели.
|
|
1447
|
+
*/
|
|
1448
|
+
#pressRow(item) {
|
|
1449
|
+
const rows = Array.from(this.panelRef()?.nativeElement.querySelectorAll('[id]') ?? []);
|
|
1450
|
+
rows.find((row) => row.id === String(item.id))?.click();
|
|
1451
|
+
}
|
|
1452
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1453
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.6", type: RtSideMenuComponent, isStandalone: true, selector: "rt-side-menu", inputs: { activeMenuIds: { classPropertyName: "activeMenuIds", publicName: "activeMenuIds", isSignal: true, isRequired: true, transformFunction: null }, menuItems: { classPropertyName: "menuItems", publicName: "menuItems", isSignal: true, isRequired: false, transformFunction: null }, subMenuMode: { classPropertyName: "subMenuMode", publicName: "subMenuMode", isSignal: true, isRequired: false, transformFunction: null }, subMenuWidth: { classPropertyName: "subMenuWidth", publicName: "subMenuWidth", isSignal: true, isRequired: false, transformFunction: null }, menuId: { classPropertyName: "menuId", publicName: "menuId", isSignal: true, isRequired: false, transformFunction: null }, favoritesCount: { classPropertyName: "favoritesCount", publicName: "favoritesCount", isSignal: true, isRequired: false, transformFunction: null }, favoriteActionsReserve: { classPropertyName: "favoriteActionsReserve", publicName: "favoriteActionsReserve", isSignal: true, isRequired: false, transformFunction: null }, isFavoritesSearchShown: { classPropertyName: "isFavoritesSearchShown", publicName: "isFavoritesSearchShown", isSignal: true, isRequired: false, transformFunction: null }, pinShown: { classPropertyName: "pinShown", publicName: "pinShown", isSignal: true, isRequired: false, transformFunction: null }, subMenuTooltipsShown: { classPropertyName: "subMenuTooltipsShown", publicName: "subMenuTooltipsShown", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { subMenuModeChange: "subMenuModeChange", subMenuWidthChange: "subMenuWidthChange", subMenuResizeStart: "subMenuResizeStart", subMenuResizeEnd: "subMenuResizeEnd", closeMobileMenuAction: "closeMobileMenuAction", clickSubMenuAction: "clickSubMenuAction", clickSubMenuAdditionalAction: "clickSubMenuAdditionalAction" }, host: { properties: { "class.rt-side-menu--pinned": "isPinned()", "style.--rt-side-menu-panel-dragged-width": "panelWidthStyle()", "class.rt-side-menu--favorite-actions-none": "favoriteActionsReserve() === 'none'" }, classAttribute: "rt-side-menu" }, providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }], queries: [{ propertyName: "headerTpl", first: true, predicate: RtSideMenuHeaderDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "footerTpl", first: true, predicate: RtSideMenuFooterDirective, descendants: true, read: TemplateRef, isSignal: true }, { propertyName: "ownIconTpl", first: true, predicate: RtSideMenuIconDirective, descendants: true, read: TemplateRef, isSignal: true }], viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- \u0423\u0437\u043A\u0438\u0439 \u044D\u043A\u0440\u0430\u043D: \u043E\u0434\u0438\u043D \u0441\u0442\u043E\u043B\u0431\u0435\u0446 \u2014 \u0433\u043B\u0430\u0432\u043D\u044B\u0435 \u043F\u0443\u043D\u043A\u0442\u044B \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u044E \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430 \u0441 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043D\u0430\u0437\u0430\u0434. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- \u041F\u043E\u0434\u043B\u043E\u0436\u043A\u0430 \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0439 \u043F\u0430\u043D\u0435\u043B\u0438: \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0432\u0437\u044F\u0442\u043E\u0435 \u043F\u043E\u043B\u0435\u043C \u043F\u043E\u0438\u0441\u043A\u0430, \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0441\u043D\u0430\u0440\u0443\u0436\u0438. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n size=\"sm\"\n iconLeft=\"search\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- \u0420\u0443\u0447\u043A\u0430 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0443 \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0434\u043C\u0435\u043D\u044E: \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u0440\u0430\u043D\u044C\u0448\u0435, \u0447\u0435\u043C \u0434\u043E \u043A\u0440\u0430\u044F \u0434\u043E\u0435\u0434\u0435\u0442 \u0440\u0443\u043A\u0430. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n size=\"sm\"\n iconLeft=\"search\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- \u0411\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430: \u0441\u0430\u043C \u0440\u0435\u0448\u0430\u0435\u0442, \u0435\u0441\u0442\u044C \u043B\u0438 \u0435\u043C\u0443 \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u043E\u043A \u043C\u0435\u0441\u0442\u0430 \u043D\u0435 \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- \u0418\u043C\u044F Material \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430 \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u0440\u043E\u0439; \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0438\u043C \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u043C \u043C\u0435\u043D\u044E. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- \u0421\u0442\u0440\u0435\u043B\u043A\u0430 \u0440\u0430\u0437\u0434\u0435\u043B\u0430, \u043A\u0430\u043A \u0443 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0443\u0432\u043E\u0434\u0438\u0442 \u0432 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0430 \u043D\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-color-bg-surface)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-space-md);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-color-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu__rail-icon{display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-color-bg-surface);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-space-sm);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-space-2)}.rt-side-menu__empty{padding:var(--rt-space-md);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:inherit;cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-color-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "directive", type: BlockDirective, selector: "[rtBlock]", inputs: ["rtMod"] }, { kind: "directive", type: ElemDirective, selector: "[rtElem]", inputs: ["rtMod"] }, { kind: "directive", type: ModDirective, selector: "[rtMod]" }, { kind: "component", type: RtIconComponent, selector: "rt-icon", inputs: ["name", "glyph", "size", "color", "fill", "spin", "rotate"] }, { kind: "component", type: RtIconButtonComponent, selector: "rt-icon-button", inputs: ["icon", "glyph", "ariaLabel", "variant", "iconColor", "iconFill", "size", "iconSize", "type", "tooltip", "tabIndex", "loading", "disabled", "active", "indicator"], outputs: ["clicked"] }, { kind: "component", type: RtInputComponent, selector: "rt-input", inputs: ["appearance", "type", "placeholder", "iconLeft", "iconRight", "passwordToggle", "autocomplete"] }, { kind: "component", type: RtScrollAreaComponent, selector: "rt-scroll-area", inputs: ["isScrollHintShown"] }, { kind: "directive", type: RtScrollAreaContentDirective, selector: "[rtScrollAreaContent]" }, { kind: "directive", type: RtScrollAreaFooterDirective, selector: "[rtScrollAreaFooter]" }, { kind: "directive", type: RtScrollAreaHeaderDirective, selector: "[rtScrollAreaHeader]" }, { kind: "directive", type: RtTooltipDirective, selector: "[rtTooltip]", inputs: ["rtTooltip", "rtTooltipPlacement", "rtTooltipWhenTruncated"], exportAs: ["rtTooltip"] }, { kind: "component", type: RtSideMenuFavoritesComponent, selector: "rt-side-menu-favorites", outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }, { kind: "component", type: RtSideMenuSubItemComponent, selector: "rt-side-menu-sub-item", inputs: ["item", "inFavorites"], outputs: ["clickSubMenuAction", "clickSubMenuAdditionalAction"] }, { kind: "pipe", type: RtSideMenuIconPipe, name: "rtSideMenuIcon" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
|
|
1454
|
+
}
|
|
1455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.6", ngImport: i0, type: RtSideMenuComponent, decorators: [{
|
|
1456
|
+
type: Component,
|
|
1457
|
+
args: [{ selector: 'rt-side-menu', host: {
|
|
1458
|
+
class: BEM_BLOCK,
|
|
1459
|
+
// Раскладка хоста меняется только у закреплённого режима: панель встаёт в поток рядом с полосой.
|
|
1460
|
+
'[class.rt-side-menu--pinned]': 'isPinned()',
|
|
1461
|
+
// Натянутая ширина подменю приходит своим свойством: панель берёт наибольшее из него и ширины оформления.
|
|
1462
|
+
'[style.--rt-side-menu-panel-dragged-width]': 'panelWidthStyle()',
|
|
1463
|
+
// Кнопки избранного, ждущие наведения, в покое ширины не занимают.
|
|
1464
|
+
'[class.rt-side-menu--favorite-actions-none]': "favoriteActionsReserve() === 'none'",
|
|
1465
|
+
}, providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }], changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [
|
|
1466
|
+
NgTemplateOutlet,
|
|
1467
|
+
ReactiveFormsModule,
|
|
1468
|
+
RouterLink,
|
|
1469
|
+
BlockDirective,
|
|
1470
|
+
ElemDirective,
|
|
1471
|
+
ModDirective,
|
|
1472
|
+
RtIconComponent,
|
|
1473
|
+
RtIconButtonComponent,
|
|
1474
|
+
RtInputComponent,
|
|
1475
|
+
RtScrollAreaComponent,
|
|
1476
|
+
RtScrollAreaContentDirective,
|
|
1477
|
+
RtScrollAreaFooterDirective,
|
|
1478
|
+
RtScrollAreaHeaderDirective,
|
|
1479
|
+
RtTooltipDirective,
|
|
1480
|
+
RtSideMenuFavoritesComponent,
|
|
1481
|
+
RtSideMenuIconPipe,
|
|
1482
|
+
RtSideMenuSubItemComponent,
|
|
1483
|
+
], template: "<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- \u0423\u0437\u043A\u0438\u0439 \u044D\u043A\u0440\u0430\u043D: \u043E\u0434\u0438\u043D \u0441\u0442\u043E\u043B\u0431\u0435\u0446 \u2014 \u0433\u043B\u0430\u0432\u043D\u044B\u0435 \u043F\u0443\u043D\u043A\u0442\u044B \u0438\u043B\u0438 \u043F\u043E\u0434\u043C\u0435\u043D\u044E \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430 \u0441 \u043A\u043D\u043E\u043F\u043A\u043E\u0439 \u043D\u0430\u0437\u0430\u0434. -->\n <rt-scroll-area rtElem=\"mobile\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n @if (selectedSubMenu()?.length) {\n <div rtElem=\"mobile-sub\">\n <button type=\"button\" rtElem=\"back\" qa-dataid=\"side-menu-back\" (click)=\"onBackToMainMenu()\">\n <rt-icon rtElem=\"back-icon\" size=\"sm\" name=\"arrow-left\" />\n <span rtElem=\"back-label\">{{ backLabel() }}</span>\n </button>\n\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </div>\n } @else {\n <nav rtElem=\"mobile-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"mobileItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n }\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n } @else {\n <rt-scroll-area rtElem=\"rail\" [isScrollHintShown]=\"true\">\n @if (headerTpl(); as header) {\n <ng-container *rtScrollAreaHeader>\n <ng-container [ngTemplateOutlet]=\"header\" />\n </ng-container>\n }\n\n <ng-container *rtScrollAreaContent>\n <nav rtElem=\"rail-list\">\n @for (item of menuItems(); track item.id) {\n <ng-container [ngTemplateOutlet]=\"railItemTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n }\n </nav>\n </ng-container>\n\n @if (footerTpl(); as footer) {\n <ng-container *rtScrollAreaFooter>\n <ng-container [ngTemplateOutlet]=\"footer\" />\n </ng-container>\n }\n </rt-scroll-area>\n\n @if (subMenuOpened()) {\n @if (isSearchHeld()) {\n <!-- \u041F\u043E\u0434\u043B\u043E\u0436\u043A\u0430 \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0439 \u043F\u0430\u043D\u0435\u043B\u0438: \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0432\u0437\u044F\u0442\u043E\u0435 \u043F\u043E\u043B\u0435\u043C \u043F\u043E\u0438\u0441\u043A\u0430, \u0437\u0430\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0441\u043D\u0430\u0440\u0443\u0436\u0438. -->\n <div\n rtElem=\"backdrop\"\n qa-dataid=\"side-menu-backdrop\"\n tabindex=\"-1\"\n (click)=\"closeSubMenu()\"\n (keydown.escape)=\"closeSubMenu()\"></div>\n }\n\n <div\n #panel\n rtElem=\"panel\"\n qa-dataid=\"side-menu-panel\"\n [rtMod]=\"{ pinned: isPinned(), held: isSearchHeld() }\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n size=\"sm\"\n iconLeft=\"search\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (focusin)=\"onSearchHold()\"\n (keydown)=\"onSearchKeydown($event)\" />\n\n @if (pinShown()) {\n <rt-icon-button\n rtElem=\"pin\"\n qa-dataid=\"side-menu-pin\"\n size=\"sm\"\n icon=\"pin\"\n [iconFill]=\"isPinned()\"\n [active]=\"isPinned()\"\n [ariaLabel]=\"isPinned() ? unpinLabel() : pinLabel()\"\n [tooltip]=\"isPinned() ? unpinLabel() : pinLabel()\"\n (clicked)=\"onSubMenuModeToggle()\" />\n }\n </div>\n\n <rt-scroll-area rtElem=\"panel-body\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- \u0420\u0443\u0447\u043A\u0430 \u0435\u0441\u0442\u044C \u0442\u043E\u043B\u044C\u043A\u043E \u0443 \u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0433\u043E \u043F\u043E\u0434\u043C\u0435\u043D\u044E: \u043D\u0435\u0437\u0430\u043A\u0440\u0435\u043F\u043B\u0451\u043D\u043D\u043E\u0435 \u0443\u0445\u043E\u0434\u0438\u0442 \u0440\u0430\u043D\u044C\u0448\u0435, \u0447\u0435\u043C \u0434\u043E \u043A\u0440\u0430\u044F \u0434\u043E\u0435\u0434\u0435\u0442 \u0440\u0443\u043A\u0430. -->\n <div\n rtElem=\"resizer\"\n qa-dataid=\"side-menu-resize\"\n role=\"separator\"\n aria-orientation=\"vertical\"\n tabindex=\"0\"\n [attr.aria-label]=\"resizeLabel()\"\n [attr.aria-valuenow]=\"resizeValueNow()\"\n [attr.aria-valuemin]=\"resizeValueNow() === null ? null : resizeValueMin\"\n [attr.aria-valuemax]=\"resizeValueNow() === null ? null : resizeValueMax\"\n (pointerdown)=\"onResizeStart($event)\"\n (keydown)=\"onResizeKeydown($event)\"></div>\n }\n }\n }\n <ng-template #subMenuTpl>\n @if (narrow()) {\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n size=\"sm\"\n iconLeft=\"search\"\n [placeholder]=\"searchLabel()\"\n [ariaLabel]=\"searchLabel()\"\n [formControl]=\"searchControl\"\n (keydown)=\"onSearchKeydown($event)\" />\n }\n\n @if (isNothingFound()) {\n <div rtElem=\"empty\" qa-dataid=\"side-menu-empty\">{{ nothingFoundLabel() }}</div>\n }\n\n <div rtElem=\"sub-list\">\n <!-- \u0411\u043B\u043E\u043A \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0433\u043E \u0440\u0430\u0437\u0434\u0435\u043B\u0430: \u0441\u0430\u043C \u0440\u0435\u0448\u0430\u0435\u0442, \u0435\u0441\u0442\u044C \u043B\u0438 \u0435\u043C\u0443 \u0447\u0442\u043E \u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C, \u0438 \u0431\u0435\u0437 \u0441\u0442\u0440\u043E\u043A \u043C\u0435\u0441\u0442\u0430 \u043D\u0435 \u0437\u0430\u043D\u0438\u043C\u0430\u0435\u0442. -->\n <rt-side-menu-favorites\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n\n @for (item of visibleSubMenuItems(); track item.id) {\n <rt-side-menu-sub-item\n [item]=\"item\"\n (clickSubMenuAction)=\"onClickSubMenu($event)\"\n (clickSubMenuAdditionalAction)=\"onClickSubMenuAdditional($event)\" />\n }\n </div>\n </ng-template>\n\n <ng-template #railItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-expanded]=\"item.submenu?.length ? subMenuOpened() && selectedItem() === item : null\"\n (mouseenter)=\"toggleSubMenu(item)\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"railItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #railItemBodyTpl let-item>\n @if (item.icon) {\n <!-- \u0418\u043C\u044F Material \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430 \u0440\u0438\u0441\u0443\u0435\u0442\u0441\u044F \u0441\u0432\u043E\u0435\u0439 \u043F\u0430\u0440\u043E\u0439; \u0438\u043C\u044F, \u043A\u043E\u0442\u043E\u0440\u043E\u0433\u043E \u043A\u0438\u0442 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442, \u2014 \u0441\u0432\u043E\u0438\u043C \u0448\u0430\u0431\u043B\u043E\u043D\u043E\u043C \u043C\u0435\u043D\u044E. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container\n [ngTemplateOutlet]=\"own\"\n [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n </span>\n }\n\n @if (item.name) {\n <span rtElem=\"rail-title\">{{ item.name }}</span>\n }\n </ng-template>\n\n <ng-template #mobileItemTpl let-item>\n @let isActive = activeRailIds().includes(item.id);\n @if (item.link && !item.submenu?.length) {\n <a\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </a>\n } @else {\n <button\n type=\"button\"\n rtElem=\"mobile-item\"\n qa-dataid=\"side-menu-item\"\n [rtMod]=\"{ active: isActive }\"\n (click)=\"onClickMenu(item)\">\n <ng-container [ngTemplateOutlet]=\"mobileItemBodyTpl\" [ngTemplateOutletContext]=\"{ $implicit: item }\" />\n </button>\n }\n </ng-template>\n\n <ng-template #mobileItemBodyTpl let-item>\n @if (item.icon) {\n @let kitIcon = item.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"mobile-icon\" [name]=\"kitIcon\" />\n } @else if (ownIconTpl(); as own) {\n <span rtElem=\"own-icon\" qa-dataid=\"side-menu-own-icon\" aria-hidden=\"true\">\n <ng-container [ngTemplateOutlet]=\"own\" [ngTemplateOutletContext]=\"{ $implicit: item, icon: item.icon, slot: 'icon' }\" />\n </span>\n }\n }\n\n <span rtElem=\"mobile-title\" [rtTooltip]=\"item.name\">{{ item.name }}</span>\n\n @if (item.submenu?.length) {\n <!-- \u0421\u0442\u0440\u0435\u043B\u043A\u0430 \u0440\u0430\u0437\u0434\u0435\u043B\u0430, \u043A\u0430\u043A \u0443 \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u043A\u0438\u0442\u0430: \u043D\u0430\u0436\u0430\u0442\u0438\u0435 \u0443\u0432\u043E\u0434\u0438\u0442 \u0432 \u043F\u043E\u0434\u043C\u0435\u043D\u044E, \u0430 \u043D\u0435 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u0435\u0442 \u043D\u0430 \u043C\u0435\u0441\u0442\u0435. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n", styles: ["@charset \"UTF-8\";@layer rt-kit.components{.rt-side-menu{--rt-side-menu-rail-width: 5.5rem;--rt-side-menu-panel-width: 15rem;--rt-side-menu-panel-held-width: 30rem;--rt-side-menu-mobile-width: 15rem;--rt-side-menu-panel-radius: var(--rt-radius-lg);--rt-side-menu-panel-shadow: var(--rt-shadow-nav-panel);--rt-side-menu-item-radius: var(--rt-radius-full);--rt-side-menu-resizer-border-width: var(--rt-border-width-thick);--rt-side-menu-panel-border-width: var(--rt-border-width-thin);--rt-side-menu-transition-duration: var(--rt-duration-fast);--rt-side-menu-panel-dragged-width: 0px;--rt-side-menu-panel-shown-width: max(var(--rt-side-menu-panel-width), var(--rt-side-menu-panel-dragged-width));position:relative;display:flex;height:100%;color:var(--rt-color-text-primary)}.rt-side-menu__rail{max-width:var(--rt-side-menu-rail-width);height:100%;box-sizing:border-box;flex:0 0 var(--rt-side-menu-rail-width);border-right:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-color-bg-surface)}.rt-side-menu__rail .rt-scroll-area__body{padding-inline:var(--rt-space-1)}.rt-side-menu__rail-list{display:flex;flex-direction:column;align-items:center;gap:var(--rt-space-md);padding-block:var(--rt-space-sm)}.rt-side-menu__rail-item{display:flex;width:100%;flex-direction:column;align-items:center;padding:0;border:0;background:transparent;color:inherit;cursor:pointer;font:inherit;gap:var(--rt-space-1);text-decoration:none}.rt-side-menu__rail-item:focus-visible{outline:none}.rt-side-menu__rail-item:focus-visible .rt-side-menu__rail-icon{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color)}.rt-side-menu__rail-item:hover .rt-side-menu__rail-icon,.rt-side-menu__rail-item:active .rt-side-menu__rail-icon{background:var(--rt-color-bg-hover)}.rt-side-menu__rail-item--active .rt-side-menu__rail-icon,.rt-side-menu__rail-item--active:hover .rt-side-menu__rail-icon{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu__rail-icon{display:flex;width:3.5rem;height:2rem;align-items:center;justify-content:center;border-radius:var(--rt-side-menu-item-radius);transition:background-color var(--rt-side-menu-transition-duration) ease}.rt-side-menu__rail-title{max-width:100%;font-size:var(--rt-text-xs);line-height:var(--rt-leading-snug);overflow-wrap:anywhere;text-align:center}.rt-side-menu__backdrop{position:fixed;z-index:var(--rt-z-dropdown);inset:0}.rt-side-menu__panel{position:absolute;z-index:var(--rt-z-dropdown);top:0;left:var(--rt-side-menu-rail-width);display:flex;width:var(--rt-side-menu-panel-shown-width);height:100%;box-sizing:border-box;flex-direction:column;border-radius:0 var(--rt-side-menu-panel-radius) var(--rt-side-menu-panel-radius) 0;border-left:var(--rt-side-menu-panel-border-width) solid var(--rt-color-border-default);background:var(--rt-color-bg-surface);box-shadow:var(--rt-side-menu-panel-shadow)}.rt-side-menu__panel--held{width:max(var(--rt-side-menu-panel-held-width),var(--rt-side-menu-panel-shown-width))}.rt-side-menu__panel--pinned{--rt-side-menu-panel-radius: 0;--rt-side-menu-panel-shadow: var(--rt-shadow-none);position:relative;left:0;flex-shrink:0}.rt-side-menu__panel-head{display:flex;align-items:center;padding:var(--rt-space-sm);gap:var(--rt-space-2)}.rt-side-menu__search{min-width:0;flex:1 1 auto}.rt-side-menu__pin{flex-shrink:0}.rt-side-menu__panel-body{min-height:0;flex:1 1 auto}.rt-side-menu__sub-list{display:flex;flex-direction:column;padding-block-end:var(--rt-space-sm);padding-inline:var(--rt-space-2)}.rt-side-menu__empty{padding:var(--rt-space-md);color:var(--rt-color-text-muted);font-size:var(--rt-text-xs)}.rt-side-menu__resizer{position:absolute;z-index:var(--rt-z-dropdown);top:0;bottom:0;left:calc(var(--rt-side-menu-rail-width) + var(--rt-side-menu-panel-shown-width) - var(--rt-space-2));width:var(--rt-space-sm);cursor:col-resize}.rt-side-menu__resizer:before{position:absolute;top:0;bottom:0;left:50%;border-left:var(--rt-side-menu-resizer-border-width) solid transparent;content:\"\"}.rt-side-menu__resizer:hover:before,.rt-side-menu__resizer:focus-visible:before{border-left-color:var(--rt-color-border-strong)}.rt-side-menu__resizer:focus-visible{outline:none}.rt-side-menu__mobile{min-width:0;max-width:100%;height:100%;flex:0 0 var(--rt-side-menu-mobile-width);background:var(--rt-color-bg-surface)}.rt-side-menu__mobile .rt-scroll-area__body{padding-inline:0}.rt-side-menu__mobile-list,.rt-side-menu__mobile-sub{display:flex;flex-direction:column;padding:var(--rt-space-sm);gap:var(--rt-space-1)}.rt-side-menu__mobile-item,.rt-side-menu__back{display:flex;width:100%;min-height:var(--rt-control-height-lg);box-sizing:border-box;align-items:center;border:0;border-radius:var(--rt-side-menu-item-radius);background:transparent;color:inherit;cursor:pointer;font:inherit;font-size:var(--rt-text-sm);gap:calc(var(--rt-space-sm) + var(--rt-space-xs));padding-inline:var(--rt-space-md);text-align:start;text-decoration:none}.rt-side-menu__mobile-item:hover,.rt-side-menu__back:hover{background:var(--rt-color-bg-hover)}.rt-side-menu__mobile-item:focus-visible,.rt-side-menu__back:focus-visible{outline:var(--rt-focus-ring-width) solid var(--rt-focus-ring-color);outline-offset:calc(var(--rt-focus-ring-width) * -1)}.rt-side-menu__mobile-item--active,.rt-side-menu__back--active{background:var(--rt-color-action-primary-subtle);color:var(--rt-color-action-primary-on-surface)}.rt-side-menu__own-icon{--rt-side-menu-own-icon-size: var(--rt-size-5);display:inline-flex;width:var(--rt-side-menu-own-icon-size);height:var(--rt-side-menu-own-icon-size);flex-shrink:0;align-items:center;justify-content:center;color:inherit;line-height:1}.rt-side-menu__own-icon>*{max-width:100%;max-height:100%}.rt-side-menu__mobile-icon,.rt-side-menu__back-icon,.rt-side-menu__mobile-chevron{flex-shrink:0}.rt-side-menu__mobile-title,.rt-side-menu__back-label{overflow:hidden;min-width:0;flex:1 1 auto;text-overflow:ellipsis;white-space:nowrap}}\n"] }]
|
|
1484
|
+
}], ctorParameters: () => [], propDecorators: { panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], headerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuHeaderDirective), { ...{ read: TemplateRef }, isSignal: true }] }], footerTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuFooterDirective), { ...{ read: TemplateRef }, isSignal: true }] }], activeMenuIds: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeMenuIds", required: true }] }], menuItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuItems", required: false }] }], subMenuMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuMode", required: false }] }], subMenuWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuWidth", required: false }] }], menuId: [{ type: i0.Input, args: [{ isSignal: true, alias: "menuId", required: false }] }], favoritesCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "favoritesCount", required: false }] }], favoriteActionsReserve: [{ type: i0.Input, args: [{ isSignal: true, alias: "favoriteActionsReserve", required: false }] }], isFavoritesSearchShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "isFavoritesSearchShown", required: false }] }], pinShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "pinShown", required: false }] }], subMenuTooltipsShown: [{ type: i0.Input, args: [{ isSignal: true, alias: "subMenuTooltipsShown", required: false }] }], subMenuModeChange: [{ type: i0.Output, args: ["subMenuModeChange"] }], subMenuWidthChange: [{ type: i0.Output, args: ["subMenuWidthChange"] }], subMenuResizeStart: [{ type: i0.Output, args: ["subMenuResizeStart"] }], subMenuResizeEnd: [{ type: i0.Output, args: ["subMenuResizeEnd"] }], closeMobileMenuAction: [{ type: i0.Output, args: ["closeMobileMenuAction"] }], clickSubMenuAction: [{ type: i0.Output, args: ["clickSubMenuAction"] }], clickSubMenuAdditionalAction: [{ type: i0.Output, args: ["clickSubMenuAdditionalAction"] }], ownIconTpl: [{ type: i0.ContentChild, args: [i0.forwardRef(() => RtSideMenuIconDirective), { ...{
|
|
1485
|
+
read: TemplateRef,
|
|
1486
|
+
}, isSignal: true }] }] } });
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* Generated bundle index. Do not edit.
|
|
1490
|
+
*/
|
|
1491
|
+
|
|
1492
|
+
export { RT_SIDE_MENU_DEFAULT_ID, RT_SIDE_MENU_SETTINGS_CONFIG, RT_SIDE_MENU_WIDTH_MAX, RT_SIDE_MENU_WIDTH_MIN, RT_SIDE_MENU_WIDTH_STEP, RtSideMenuComponent, RtSideMenuFooterDirective, RtSideMenuHeaderDirective, RtSideMenuIconDirective, RtSideMenuSettingsService, RtSideMenuSubItemComponent, clampSideMenuWidth, drawnSideMenuWidth, filterSideMenuItems, normalizeSideMenuId, normalizeSideMenuSettings, omitSideMenuSettings, parseSideMenuSettingsRecord, patchSideMenuSettings, pinnedSideMenuItems, provideRtSideMenuSettings, readSideMenuSettings, reportedSideMenuWidth, sideMenuIconName, sideMenuIdsToExpand, sideMenuWidthByKey, splitSideMenuTitle, startSideMenuWidthDrag, stepSideMenuHighlight, unpairedSideMenuIcons, walkSideMenuItems };
|
|
1493
|
+
//# sourceMappingURL=rt-tools-ui-kit-v2-side-menu.mjs.map
|