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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs +23 -29
  2. package/fesm2022/rt-tools-ui-kit-v2-autocomplete.mjs.map +1 -1
  3. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs +2 -2
  4. package/fesm2022/rt-tools-ui-kit-v2-calendar.mjs.map +1 -1
  5. package/fesm2022/rt-tools-ui-kit-v2-core.mjs +223 -3
  6. package/fesm2022/rt-tools-ui-kit-v2-core.mjs.map +1 -1
  7. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs +2 -2
  8. package/fesm2022/rt-tools-ui-kit-v2-data-list.mjs.map +1 -1
  9. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs +1 -2
  10. package/fesm2022/rt-tools-ui-kit-v2-draggable-tree.mjs.map +1 -1
  11. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs +2 -2
  12. package/fesm2022/rt-tools-ui-kit-v2-dynamic-list.mjs.map +1 -1
  13. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs +277 -904
  14. package/fesm2022/rt-tools-ui-kit-v2-dynamic-selector.mjs.map +1 -1
  15. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs +2 -2
  16. package/fesm2022/rt-tools-ui-kit-v2-filter-control.mjs.map +1 -1
  17. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs +1 -1
  18. package/fesm2022/rt-tools-ui-kit-v2-hybrid-tree.mjs.map +1 -1
  19. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs +35 -48
  20. package/fesm2022/rt-tools-ui-kit-v2-multiselect.mjs.map +1 -1
  21. package/fesm2022/rt-tools-ui-kit-v2-page-header.mjs +2 -2
  22. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs +2 -2
  23. package/fesm2022/rt-tools-ui-kit-v2-pdf-viewer.mjs.map +1 -1
  24. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs +4 -4
  25. package/fesm2022/rt-tools-ui-kit-v2-rich-editor.mjs.map +1 -1
  26. package/fesm2022/rt-tools-ui-kit-v2-select.mjs +37 -181
  27. package/fesm2022/rt-tools-ui-kit-v2-select.mjs.map +1 -1
  28. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs +5 -36
  29. package/fesm2022/rt-tools-ui-kit-v2-side-menu.mjs.map +1 -1
  30. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs +2 -3
  31. package/fesm2022/rt-tools-ui-kit-v2-tag.mjs.map +1 -1
  32. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs +2 -2
  33. package/fesm2022/rt-tools-ui-kit-v2-toolbar.mjs.map +1 -1
  34. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs +8 -13
  35. package/fesm2022/rt-tools-ui-kit-v2-tree-selector.mjs.map +1 -1
  36. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs +12 -7
  37. package/fesm2022/rt-tools-ui-kit-v2-tree.mjs.map +1 -1
  38. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs +2 -2
  39. package/fesm2022/rt-tools-ui-kit-v2-workspace.mjs.map +1 -1
  40. package/package.json +1 -1
  41. package/src/lib/components/dynamic-selector/stories/component/test-dynamic-selector-popup-frame.component.scss +6 -0
  42. package/src/lib/components/page-header/rt-page-header.component.scss +4 -2
  43. package/src/lib/components/tree/CONTEXT.md +1 -1
  44. package/src/rich-editor/lib/components/ai-chat/CONTEXT.md +1 -1
  45. package/src/styles/_breakpoints.scss +37 -4
  46. package/src/styles/_index.scss +0 -4
  47. package/styles/tokens.css +0 -8
  48. package/types/rt-tools-ui-kit-v2-autocomplete.d.ts +4 -3
  49. package/types/rt-tools-ui-kit-v2-core.d.ts +155 -1
  50. package/types/rt-tools-ui-kit-v2-dynamic-selector.d.ts +51 -269
  51. package/types/rt-tools-ui-kit-v2-multiselect.d.ts +4 -3
  52. package/types/rt-tools-ui-kit-v2-select.d.ts +12 -74
  53. package/types/rt-tools-ui-kit-v2-side-menu.d.ts +5 -20
  54. package/types/rt-tools-ui-kit-v2-tag.d.ts +2 -3
  55. package/types/rt-tools-ui-kit-v2-tree-selector.d.ts +0 -2
  56. package/types/rt-tools-ui-kit-v2-tree.d.ts +7 -9
  57. package/src/styles/_place.scss +0 -7
@@ -1 +1 @@
1
- {"version":3,"file":"rt-tools-ui-kit-v2-side-menu.mjs","names":["BEM_BLOCK","BEM_BLOCK"],"sources":["../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-favorites.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-settings.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-settings.service.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.tokens.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-href.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-icon.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-icon.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-title-parts.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/sub-item/rt-side-menu-sub-item.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/sub-item/rt-side-menu-sub-item.component.html","../../../projects/ui-kit-v2/src/lib/components/side-menu/favorites/rt-side-menu-favorites.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/favorites/rt-side-menu-favorites.component.html","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-close-timer.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-keyboard.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-resize.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-look.base.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.directives.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.component.html"],"sourcesContent":["import { IRtSideMenu } from './rt-side-menu.model';\n\n/** Только строки и числа, без повторов, в прежнем порядке. */\nexport function normalizeSideMenuFavorites(values: ReadonlyArray<unknown>): IRtSideMenu.FavoriteId[] {\n return values.reduce((kept: IRtSideMenu.FavoriteId[], value: unknown): IRtSideMenu.FavoriteId[] => {\n if ((typeof value === 'string' || typeof value === 'number') && !kept.includes(value)) {\n kept.push(value);\n }\n\n return kept;\n }, []);\n}\n\n/**\n * Перенос записи списка с места на место.\n *\n * Номер места вне списка у источника ничего не меняет — переносить нечего; у цели прижимается к\n * краю: рука, бросившая строку ниже последней, хотела поставить её последней.\n */\nexport function moveSideMenuFavorite(ids: ReadonlyArray<IRtSideMenu.FavoriteId>, from: number, to: number): IRtSideMenu.FavoriteId[] {\n const next: IRtSideMenu.FavoriteId[] = [...ids];\n\n if (from < 0 || from >= next.length) {\n return next;\n }\n\n const moved: IRtSideMenu.FavoriteId = next.splice(from, 1)[0];\n const target: number = Math.min(Math.max(to, 0), next.length);\n next.splice(target, 0, moved);\n\n return next;\n}\n\n/**\n * Перенос строки блока — места в блоке, а не в списке.\n *\n * Блок показывает только те номера, пункты которых в меню есть, поэтому место в блоке — не место в\n * списке. Видимые номера переставляются между собой и встают в те же ячейки списка, которые\n * занимали; скрытые остаются на своих местах: их пункт вернётся вместе с правом, и человек найдёт\n * его там, куда ставил. Видимый номер, которого в списке нет, пропускается: список могла поменять\n * другая вкладка, а место номера, которого нет, занять нечем.\n */\nexport function moveVisibleSideMenuFavorite(\n ids: ReadonlyArray<IRtSideMenu.FavoriteId>,\n visible: ReadonlyArray<IRtSideMenu.FavoriteId>,\n from: number,\n to: number\n): IRtSideMenu.FavoriteId[] {\n const listed: Set<IRtSideMenu.FavoriteId> = new Set(ids);\n const reordered: IRtSideMenu.FavoriteId[] = moveSideMenuFavorite(normalizeSideMenuFavorites(visible), from, to).filter(\n (id: IRtSideMenu.FavoriteId): boolean => listed.has(id)\n );\n const slots: Set<IRtSideMenu.FavoriteId> = new Set(reordered);\n let next: number = 0;\n\n return ids.map((id: IRtSideMenu.FavoriteId): IRtSideMenu.FavoriteId => {\n if (!slots.has(id)) {\n return id;\n }\n\n const placed: IRtSideMenu.FavoriteId = reordered[next];\n next += 1;\n\n return placed;\n });\n}\n\n/**\n * Может ли пункт стоять в избранном: ссылка есть — может; папка без ссылки никуда не ведёт, а\n * пункт с флагом `favoriteDisabled` приложение исключило само.\n */\nexport function isSideMenuFavoriteCandidate(item: IRtSideMenu.Item): boolean {\n return Boolean(item.link?.trim()) && !item.favoriteDisabled;\n}\n\nfunction collectLinkedItems(items: ReadonlyArray<IRtSideMenu.Item>, byId: Map<IRtSideMenu.FavoriteId, IRtSideMenu.Item>): void {\n for (const item of items) {\n if (isSideMenuFavoriteCandidate(item) && !byId.has(item.id)) {\n byId.set(item.id, item);\n }\n\n collectLinkedItems(item.submenu ?? [], byId);\n }\n}\n\n/**\n * Пункты меню, стоящие за номерами списка, в порядке списка.\n *\n * Пункт ищется в подменю пунктов полосы, вглубь папок, и берётся первый со ссылкой: избранное,\n * которое никуда не ведёт, — сломанное избранное. Номер без пункта пропускается, но из списка не\n * уходит — это решает сервис, а не меню.\n */\nexport function findSideMenuFavoriteItems(\n menu: ReadonlyArray<IRtSideMenu.Item>,\n ids: ReadonlyArray<IRtSideMenu.FavoriteId>\n): IRtSideMenu.Item[] {\n const byId: Map<IRtSideMenu.FavoriteId, IRtSideMenu.Item> = new Map();\n collectLinkedItems(\n menu.flatMap((item: IRtSideMenu.Item): IRtSideMenu.Item[] => item.submenu ?? []),\n byId\n );\n\n return ids.reduce((found: IRtSideMenu.Item[], id: IRtSideMenu.FavoriteId): IRtSideMenu.Item[] => {\n const item: IRtSideMenu.Item | undefined = byId.get(id);\n\n if (item) {\n found.push(item);\n }\n\n return found;\n }, []);\n}\n\n/**\n * Пункт полосы, чьё подменю сейчас показано, если избранное у него включено.\n *\n * Избранное у каждого пункта своё и по умолчанию выключено: включает его потребитель флагом\n * `favorites` пункта полосы. Показанный набор даёт меню — закреплённый или открытый наведением, —\n * и пустой набор не принадлежит никому: подменю без списка не показывает ничьё избранное.\n *\n * Раздел узнаётся сначала по самому набору, затем по номеру его первого пункта: приложение вправе\n * передать меню заново теми же пунктами в новых объектах — при смене языка или прав, — и набор,\n * который подменю ещё держит, перестаёт совпадать с пунктами полосы по ссылке. Номера пунктов\n * подменю единственны на всё меню — это обязанность потребителя.\n */\nexport function sideMenuFavoritesSection(menu: ReadonlyArray<IRtSideMenu.Item>, shown: IRtSideMenu.Item[]): IRtSideMenu.Item | null {\n const first: IRtSideMenu.Item | undefined = shown[0];\n\n if (!first) {\n return null;\n }\n\n const section: IRtSideMenu.Item | undefined =\n menu.find((item: IRtSideMenu.Item): boolean => item.submenu === shown) ??\n menu.find((item: IRtSideMenu.Item): boolean => !!item.submenu?.some((child: IRtSideMenu.Item): boolean => child.id === first.id));\n\n return section?.favorites ? section : null;\n}\n\n/** Место на экране, где строку отпустили, и прямоугольник панели подменю. */\nexport interface IRtSideMenuDropPoint {\n x: number;\n y: number;\n}\n\n/**\n * Строку отпустили за пределами панели. Пока её тянули, уход указателя с панели не закрывал\n * подменю, открытое наведением, — после броска он должен сработать так, будто случился сейчас.\n */\nexport function isSideMenuDropOutside(panel: Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom'>, point: IRtSideMenuDropPoint): boolean {\n return point.x < panel.left || point.x > panel.right || point.y < panel.top || point.y > panel.bottom;\n}\n","import { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Отбор пунктов подменю по подстроке подписи.\n *\n * Отбор спускается внутрь папок на любую глубину: потребитель кладёт в папки то, что человек и ищет\n * по имени, и по одному верхнему уровню такой пункт не нашёлся бы.\n *\n * В выдаче стоят только строки, содержащие запрос. Папка остаётся путём к совпавшим детям, а её\n * состав отбирается тем же правилом: иначе одно совпадение по имени папки вытаскивало бы весь её\n * состав. Папка, совпавшая только по имени, остаётся одной строкой без детей.\n *\n * Отобранная папка — новый объект: правка на месте переписала бы набор потребителя, и стёртый\n * запрос вернул бы урезанное меню.\n */\nexport function filterSideMenuItems(items: ReadonlyArray<IRtSideMenu.Item>, query: string): IRtSideMenu.Item[] {\n const needle: string = query.trim().toLowerCase();\n\n if (needle === '') {\n return [...items];\n }\n\n return items.reduce((kept: IRtSideMenu.Item[], item: IRtSideMenu.Item): IRtSideMenu.Item[] => {\n const isFolder: boolean = Boolean(item.submenu?.length);\n const inside: IRtSideMenu.Item[] = isFolder ? filterSideMenuItems(item.submenu ?? [], query) : [];\n\n if (inside.length) {\n kept.push({ ...item, submenu: inside });\n\n return kept;\n }\n\n if (item.name?.toLowerCase().includes(needle)) {\n kept.push(isFolder ? { ...item, submenu: [] } : item);\n }\n\n return kept;\n }, []);\n}\n\n/**\n * Папки отобранного списка — те, что должны стоять раскрытыми. Возвращаются номера, а не пункты:\n * отобранный список пересобирается на каждую букву запроса, и ссылка на пункт этого не переживает.\n */\nexport function sideMenuIdsToExpand(items: ReadonlyArray<IRtSideMenu.Item>): Array<string | number> {\n return items.reduce((ids: Array<string | number>, item: IRtSideMenu.Item): Array<string | number> => {\n if (item.submenu?.length) {\n ids.push(item.id, ...sideMenuIdsToExpand(item.submenu));\n }\n\n return ids;\n }, []);\n}\n\n/**\n * Пределы ширины подменю в пикселях. Уже нижнего не помещается ни одна подпись пункта, шире\n * верхнего подменю закрывает содержимое страницы.\n */\nexport const RT_SIDE_MENU_WIDTH_MIN: number = 120;\nexport const RT_SIDE_MENU_WIDTH_MAX: number = 480;\n\n/** Шаг ширины с клавиатуры: стрелку держат нажатой, и крупный шаг проскакивал бы подпись целиком. */\nexport const RT_SIDE_MENU_WIDTH_STEP: number = 16;\n\n/** Приведение ширины к пределам. */\nexport function clampSideMenuWidth(width: number): number {\n return Math.min(RT_SIDE_MENU_WIDTH_MAX, Math.max(RT_SIDE_MENU_WIDTH_MIN, width));\n}\n\n/**\n * Резка подписи на совпавшие и несовпавшие куски.\n *\n * Отмечаются все вхождения: отмеченное одно первое читается как «второго нет». Сравнение без учёта\n * регистра, а куски режутся из оригинала — приведение к нижнему регистру переписало бы названия.\n */\nexport function splitSideMenuTitle(name: string, query: string): IRtSideMenu.TitlePart[] {\n if (name === '') {\n return [];\n }\n\n const needle: string = query.trim().toLowerCase();\n\n if (needle === '') {\n return [{ text: name, matched: false }];\n }\n\n const haystack: string = name.toLowerCase();\n const parts: IRtSideMenu.TitlePart[] = [];\n let from: number = 0;\n\n for (let at: number = haystack.indexOf(needle, from); at !== -1; at = haystack.indexOf(needle, from)) {\n if (at > from) {\n parts.push({ text: name.slice(from, at), matched: false });\n }\n\n parts.push({ text: name.slice(at, at + needle.length), matched: true });\n from = at + needle.length;\n }\n\n if (from < name.length) {\n parts.push({ text: name.slice(from), matched: false });\n }\n\n return parts;\n}\n\n/**\n * Резка подписи по каждому слову запроса. Приложение делит поиск по пробелам и ищет каждое слово\n * отдельно, поэтому и отмечено каждое. Слово внутри уже отмеченного куска не режет его второй раз.\n */\nexport function splitTitleByWords(name: string, query: string): IRtSideMenu.TitlePart[] {\n const words: string[] = query.split(/\\s+/).filter((word: string): boolean => word !== '');\n return words.reduce(\n (parts: IRtSideMenu.TitlePart[], word: string): IRtSideMenu.TitlePart[] =>\n parts.flatMap((part: IRtSideMenu.TitlePart): IRtSideMenu.TitlePart[] =>\n part.matched ? [part] : splitSideMenuTitle(part.text, word)\n ),\n splitSideMenuTitle(name, '')\n );\n}\n\n/**\n * Пункты подменю в том порядке, в каком они стоят на экране. Внутрь папки список спускается, только\n * если та раскрыта: ходьба стрелками идёт по видимому, а не по всему набору.\n */\nexport function walkSideMenuItems(items: ReadonlyArray<IRtSideMenu.Item>, expandedIds: ReadonlyArray<string | number>): IRtSideMenu.Item[] {\n return items.reduce((walk: IRtSideMenu.Item[], item: IRtSideMenu.Item): IRtSideMenu.Item[] => {\n walk.push(item);\n\n if (item.submenu?.length && expandedIds.includes(item.id)) {\n walk.push(...walkSideMenuItems(item.submenu, expandedIds));\n }\n\n return walk;\n }, []);\n}\n\n/**\n * Куда уходит подсветка на шаг стрелкой. Считается по номеру пункта: видимый список пересобирается\n * на каждую букву запроса. Без подсветки вниз берёт первый пункт, вверх — последний. У краёв ходьба\n * останавливается: заворот уводил бы взгляд через всю панель.\n */\nexport function stepSideMenuHighlight(\n walk: ReadonlyArray<IRtSideMenu.Item>,\n highlightedId: string | number | null,\n step: number\n): string | number | null {\n if (walk.length === 0) {\n return null;\n }\n\n const at: number = walk.findIndex((item: IRtSideMenu.Item): boolean => item.id === highlightedId);\n\n if (at === -1) {\n return step > 0 ? walk[0].id : walk[walk.length - 1].id;\n }\n\n return walk[Math.min(walk.length - 1, Math.max(0, at + step))].id;\n}\n\n/** Чем вешается слушатель. У кита это `Renderer2.listen`, у проверки — своя функция. */\nexport type TRtPointerListen = (target: HTMLElement, event: string, handler: (event: PointerEvent) => void) => () => void;\n\n/** Что тяга сообщает меню: новую ширину по ходу и конец — отпусканием или отнятием указателя. */\nexport interface IRtSubMenuWidthDragHooks {\n readonly onWidth: (width: number) => void;\n readonly onEnd: () => void;\n}\n\n/**\n * Тяга ширины подменю указательными событиями: мышиных палец и перо не дают. Слушатели висят на\n * ручке, а держит их захват указателя — слушатель на документе терял бы движение над чужим кадром.\n * Указатель, отнятый средой, кончает тягу так же, как отпускание.\n *\n * Возвращает снятие слушателей. Пустое значение — тянуть нечем.\n */\nexport function startSideMenuWidthDrag(\n event: PointerEvent,\n startWidth: number,\n listen: TRtPointerListen,\n hooks: IRtSubMenuWidthDragHooks\n): (() => void) | null {\n const handle: EventTarget | null = event.currentTarget;\n\n // Тянет только основная кнопка: отпускание правой над меню среды ручке не приходит.\n if (!(handle instanceof HTMLElement) || event.button !== 0) {\n return null;\n }\n\n const pointerId: number = event.pointerId;\n const startX: number = event.clientX;\n\n // Захвата нет у среды, где идут проверки.\n if (typeof handle.setPointerCapture === 'function') {\n handle.setPointerCapture(pointerId);\n }\n\n const stopMove: () => void = listen(handle, 'pointermove', (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId === pointerId) {\n hooks.onWidth(clampSideMenuWidth(startWidth + moveEvent.clientX - startX));\n }\n });\n const stopUp: () => void = listen(handle, 'pointerup', (): void => hooks.onEnd());\n const stopCancel: () => void = listen(handle, 'pointercancel', (): void => hooks.onEnd());\n // Захват снимается и без отпускания — ручку убрали из разметки; тогда конец тяги — сама потеря.\n const stopLost: () => void = listen(handle, 'lostpointercapture', (): void => hooks.onEnd());\n\n return (): void => {\n stopMove();\n stopUp();\n stopCancel();\n stopLost();\n\n if (typeof handle.releasePointerCapture === 'function' && handle.hasPointerCapture(pointerId)) {\n handle.releasePointerCapture(pointerId);\n }\n };\n}\n\n/**\n * Ширина, которой панель нарисована. Панели нет или раскладка не посчитана — нижний предел: с нуля\n * тяга уводила бы ширину в отрицательные числа.\n */\nexport function drawnSideMenuWidth(panel: HTMLElement | null): number {\n const width: number = panel?.getBoundingClientRect().width ?? 0;\n\n return width > 0 ? width : RT_SIDE_MENU_WIDTH_MIN;\n}\n\n/**\n * Число, которое уходит наружу по концу тяги: большее из натянутого и нарисованного. Нижний предел\n * держит оформление, и панель не бывает уже его, даже если рука ушла левее.\n */\nexport function reportedSideMenuWidth(dragged: number, panel: HTMLElement | null): number {\n return Math.max(dragged, drawnSideMenuWidth(panel));\n}\n\n/**\n * Новая ширина по нажатой клавише. Пустое значение — клавиша не о ширине, и умолчание не отменяется:\n * иначе ручка съедала бы табуляцию. Влево значит уже при любом направлении письма: ручка стоит у\n * правого края панели.\n */\nexport function sideMenuWidthByKey(key: string, width: number): number | null {\n switch (key) {\n case 'ArrowRight':\n return clampSideMenuWidth(width + RT_SIDE_MENU_WIDTH_STEP);\n case 'ArrowLeft':\n return clampSideMenuWidth(width - RT_SIDE_MENU_WIDTH_STEP);\n case 'Home':\n return RT_SIDE_MENU_WIDTH_MIN;\n case 'End':\n return RT_SIDE_MENU_WIDTH_MAX;\n default:\n return null;\n }\n}\n\n/**\n * Что показывает закреплённое подменю: выбранный человеком раздел, а пока выбора нет — раздел\n * активного адреса. Выбор впереди активности: иначе до соседнего раздела не добраться.\n */\nexport function pinnedSideMenuItems(\n picked: IRtSideMenu.Item[] | null | undefined,\n activeIds: ReadonlyArray<string | number>,\n items: ReadonlyArray<IRtSideMenu.Item>\n): IRtSideMenu.Item[] {\n if (picked?.length) {\n return picked;\n }\n\n return items.find((item: IRtSideMenu.Item): boolean => activeIds.includes(item.id) && !!item.submenu?.length)?.submenu ?? [];\n}\n\n/**\n * Нажатие, которое браузер открывает сам по адресу ссылки: не левой кнопкой или с клавишей —\n * новая вкладка, окно, загрузка. Строка подменю такое нажатие не перехватывает.\n */\nexport function sideMenuPressOpensInBrowser(event: MouseEvent): boolean {\n const withKey: boolean = [event.ctrlKey, event.metaKey, event.shiftKey, event.altKey].some(Boolean);\n\n return event.button !== 0 || withKey;\n}\n","import { isRecord } from '@rt-tools/utils';\n\nimport { normalizeSideMenuFavorites } from './rt-side-menu-favorites.logic';\nimport { clampSideMenuWidth } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Номер меню, которому приложение своего не задало. */\nexport const RT_SIDE_MENU_DEFAULT_ID: string = 'main';\n\n/**\n * Номер меню, под которым лежат его настройки. Пустой — тот же, что не заданный: атрибут без\n * значения даёт пустую строку, и настройки легли бы под ключ, которого никто не называл.\n */\nexport function normalizeSideMenuId(menuId: string | null | undefined): string {\n return menuId?.trim() || RT_SIDE_MENU_DEFAULT_ID;\n}\n\n/** Запись хранилища как есть: номер меню — и что под ним лежит, в том числе сломанное. */\nexport type TRtSideMenuSettingsRecord = Record<string, unknown>;\n\nfunction isSubMenuMode(value: unknown): value is IRtSideMenu.SubMenuMode {\n return value === 'hover' || value === 'pinned';\n}\n\n/**\n * Настройки одного меню из того, что лежит под его номером. Недопустимое поле читается как\n * отсутствие значения, соседние поля остаются: сломанная ширина не стирает режим. Незнакомые поля\n * не удаляются. Ширина приводится к пределам подменю, списки избранного — к номерам без повторов.\n */\nexport function normalizeSideMenuSettings(value: unknown): IRtSideMenu.Settings {\n if (!isRecord(value)) {\n return {};\n }\n\n const settings: IRtSideMenu.Settings = { ...value };\n const width: unknown = value['subMenuWidth'];\n const favorites: unknown = value['favorites'];\n const collapsed: unknown = value['favoritesCollapsed'];\n\n if (!isSubMenuMode(value['subMenuMode'])) {\n delete settings.subMenuMode;\n }\n\n if (typeof width === 'number' && Number.isFinite(width)) {\n settings.subMenuWidth = clampSideMenuWidth(width);\n } else {\n delete settings.subMenuWidth;\n }\n\n // Список — только строки и числа без повторов; не массив читается отсутствием списка.\n if (Array.isArray(favorites)) {\n settings.favorites = normalizeSideMenuFavorites(favorites);\n } else {\n delete settings.favorites;\n }\n\n if (Array.isArray(collapsed)) {\n settings.favoritesCollapsed = normalizeSideMenuFavorites(collapsed);\n } else {\n delete settings.favoritesCollapsed;\n }\n\n return settings;\n}\n\n/** Запись хранилища как объект. Не JSON, не объект, пустое хранилище — одинаково пустая запись. */\nexport function parseSideMenuSettingsRecord(raw: string | null): TRtSideMenuSettingsRecord {\n if (raw === null) {\n return {};\n }\n\n try {\n const value: unknown = JSON.parse(raw);\n\n return isRecord(value) ? value : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Настройки всех меню из записи. Меню, чьё значение не объект, пропускается: сломанный угол одного\n * меню не стирает соседние.\n */\nexport function readSideMenuSettings(record: TRtSideMenuSettingsRecord): Record<string, IRtSideMenu.Settings> {\n return Object.fromEntries(\n Object.entries(record)\n .filter(([, settings]: [string, unknown]): boolean => isRecord(settings))\n .map(([menuId, settings]: [string, unknown]): [string, IRtSideMenu.Settings] => [menuId, normalizeSideMenuSettings(settings)])\n );\n}\n\n/**\n * Запись с правкой полей одного меню. Остальные меню и поля уходят такими, какими лежали, даже\n * сломанные: кит правит только то, о чём его попросили.\n */\nexport function patchSideMenuSettings(\n record: TRtSideMenuSettingsRecord,\n menuId: string,\n patch: Partial<IRtSideMenu.Settings>\n): TRtSideMenuSettingsRecord {\n const current: unknown = record[menuId];\n\n return { ...record, [menuId]: { ...(isRecord(current) ? current : {}), ...patch } };\n}\n\n/** Запись без настроек одного меню. */\nexport function omitSideMenuSettings(record: TRtSideMenuSettingsRecord, menuId: string): TRtSideMenuSettingsRecord {\n return Object.fromEntries(Object.entries(record).filter(([id]: [string, unknown]): boolean => id !== menuId));\n}\n","import {\n computed,\n DestroyRef,\n EnvironmentProviders,\n inject,\n Injectable,\n InjectionToken,\n makeEnvironmentProviders,\n Signal,\n signal,\n WritableSignal,\n} from '@angular/core';\n\nimport { LOCAL_STORAGE, PlatformService, WINDOW } from '@rt-tools/core';\n\nimport { ERtStorageKeys } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { moveVisibleSideMenuFavorite, normalizeSideMenuFavorites } from './rt-side-menu-favorites.logic';\nimport {\n normalizeSideMenuId,\n normalizeSideMenuSettings,\n omitSideMenuSettings,\n parseSideMenuSettingsRecord,\n patchSideMenuSettings,\n readSideMenuSettings,\n TRtSideMenuSettingsRecord,\n} from './rt-side-menu-settings.logic';\nimport { clampSideMenuWidth } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Значки кнопок строки избранного: «убрать» и ручка перетаскивания. */\nexport interface IRtSideMenuFavoritesIcons {\n readonly remove: IRtIcon.Name;\n readonly drag: IRtIcon.Name;\n}\n\nexport interface IRtSideMenuSettingsConfig {\n /** Свой ключ хранилища, если приложению нужен не общий ключ кита. */\n readonly storageKey?: string;\n /** Свои значки кнопок избранного; не названный берётся из набора кита. */\n readonly icons?: Partial<IRtSideMenuFavoritesIcons>;\n}\n\n/** Корзина и вертикальная двойная стрелка — те же знаки, что у первого кита. */\nconst DEFAULT_FAVORITES_ICONS: IRtSideMenuFavoritesIcons = { remove: 'trash', drag: 'arrows-v' };\n\nexport const RT_SIDE_MENU_SETTINGS_CONFIG: InjectionToken<IRtSideMenuSettingsConfig> = new InjectionToken<IRtSideMenuSettingsConfig>(\n 'RT_SIDE_MENU_SETTINGS_CONFIG'\n);\n\n/** Сигнал на номер меню один: повторный вызов отдаёт тот же. */\nfunction cached<T>(cache: Map<string, Signal<T>>, menuId: string, read: (menuId: string) => T): Signal<T> {\n const id: string = normalizeSideMenuId(menuId);\n let value: Signal<T> | undefined = cache.get(id);\n\n if (!value) {\n value = computed((): T => read(id));\n cache.set(id, value);\n }\n\n return value;\n}\n\n/**\n * Настройки боковых меню в хранилище браузера: режим и ширина подменю, избранное и свёрнутые блоки\n * избранного под номером каждого меню.\n *\n * Перед каждой записью ключ читается заново: соседняя вкладка или приложение могли записать своё,\n * и запись из копии затёрла бы их. Правится одно поле одного меню, остальное уходит как лежало.\n * Изменения из других вкладок приходят событием `storage` и обновляют сигналы.\n *\n * Хранилище берётся токеном `@rt-tools/core` напрямую, а не общим сервисом хранилища: сервису\n * нужно знать, что запись не удалась, и держать правду в памяти до перезагрузки. Без хранилища, вне\n * браузера, закрытое или полное — настройки живут в памяти, и ни чтение, ни запись не бросают.\n *\n * Сервис ставится провайдером окружения — `provideRtSideMenuSettings()`, — а меню берёт его\n * необязательно: не поставлен — меню ничего не хранит, режим и ширина приходят входами.\n */\n@Injectable()\nexport class RtSideMenuSettingsService {\n readonly #config: IRtSideMenuSettingsConfig = inject(RT_SIDE_MENU_SETTINGS_CONFIG, { optional: true }) ?? {};\n readonly #storage: Storage | null = inject(LOCAL_STORAGE, { optional: true }) ?? null;\n readonly #key: string = this.#config.storageKey?.trim() || ERtStorageKeys.SideMenu;\n readonly #menus: Map<string, Signal<IRtSideMenu.Settings>> = new Map();\n readonly #modes: Map<string, Signal<IRtSideMenu.SubMenuMode>> = new Map();\n readonly #widths: Map<string, Signal<number | null>> = new Map();\n readonly #favorites: Map<string, Signal<ReadonlyArray<IRtSideMenu.FavoriteId>>> = new Map();\n readonly #collapsed: Map<string, Signal<ReadonlyArray<IRtSideMenu.Item['id']>>> = new Map();\n /** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */\n #record: TRtSideMenuSettingsRecord = this.#load() ?? {};\n /** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */\n #unsaved: boolean = false;\n readonly #settings: WritableSignal<Readonly<Record<string, IRtSideMenu.Settings>>> = signal(readSideMenuSettings(this.#record));\n\n /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */\n public readonly favoritesIcons: IRtSideMenuFavoritesIcons = { ...DEFAULT_FAVORITES_ICONS, ...this.#config.icons };\n\n /** Номера меню, чьи настройки лежат в хранилище. */\n public readonly menuIds: Signal<string[]> = computed((): string[] => Object.keys(this.#settings()));\n\n constructor() {\n if (!inject(PlatformService).isPlatformBrowser) {\n return;\n }\n\n const view: Window = inject(WINDOW);\n const onStorage: (event: StorageEvent) => void = (event: StorageEvent): void => {\n if (event.key === this.#key || event.key === null) {\n this.#unsaved = false;\n this.#apply(this.#fresh());\n }\n };\n\n view.addEventListener('storage', onStorage);\n inject(DestroyRef).onDestroy((): void => view.removeEventListener('storage', onStorage));\n }\n\n /** Настройки одного меню целиком, с незнакомыми полями; не сохранены — пустой объект. */\n public settings(menuId: string): Signal<IRtSideMenu.Settings> {\n return cached(this.#menus, menuId, (id: string): IRtSideMenu.Settings => this.#settings()[id] ?? {});\n }\n\n public subMenuMode(menuId: string): Signal<IRtSideMenu.SubMenuMode> {\n return cached(this.#modes, menuId, (id: string): IRtSideMenu.SubMenuMode => this.#settings()[id]?.subMenuMode ?? 'hover');\n }\n\n public subMenuWidth(menuId: string): Signal<number | null> {\n return cached(this.#widths, menuId, (id: string): number | null => this.#settings()[id]?.subMenuWidth ?? null);\n }\n\n /** Избранное меню в порядке списка; пусто — пустой список. */\n public favoriteIds(menuId: string): Signal<ReadonlyArray<IRtSideMenu.FavoriteId>> {\n return cached(\n this.#favorites,\n menuId,\n (id: string): ReadonlyArray<IRtSideMenu.FavoriteId> => this.#settings()[id]?.favorites ?? []\n );\n }\n\n /** Пункты полосы, чей блок избранного свёрнут; ничего не свёрнуто — пустой список. */\n public favoritesCollapsed(menuId: string): Signal<ReadonlyArray<IRtSideMenu.Item['id']>> {\n return cached(\n this.#collapsed,\n menuId,\n (id: string): ReadonlyArray<IRtSideMenu.Item['id']> => this.#settings()[id]?.favoritesCollapsed ?? []\n );\n }\n\n /** Сворачивает или разворачивает блок одного раздела; остальные разделы остаются как были. */\n public setFavoritesCollapsed(menuId: string, sectionId: IRtSideMenu.Item['id'], collapsed: boolean): void {\n const current: ReadonlyArray<IRtSideMenu.Item['id']> = this.#freshSettings(menuId).favoritesCollapsed ?? [];\n const others: Array<IRtSideMenu.Item['id']> = current.filter((id: IRtSideMenu.Item['id']): boolean => id !== sectionId);\n\n this.#update(menuId, { favoritesCollapsed: collapsed ? [...others, sectionId] : others });\n }\n\n /** Звезда: нет в списке — встаёт последним, есть — уходит. */\n public toggleFavorite(menuId: string, id: IRtSideMenu.FavoriteId): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n ids.includes(id) ? ids.filter((kept: IRtSideMenu.FavoriteId): boolean => kept !== id) : [...ids, id]\n );\n }\n\n public removeFavorite(menuId: string, id: IRtSideMenu.FavoriteId): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n ids.filter((kept: IRtSideMenu.FavoriteId): boolean => kept !== id)\n );\n }\n\n /** Перенос строки блока: места в блоке, скрытые номера остаются на своих. */\n public moveVisibleSideMenuFavorite(menuId: string, visibleIds: ReadonlyArray<IRtSideMenu.FavoriteId>, from: number, to: number): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n moveVisibleSideMenuFavorite(ids, visibleIds, from, to)\n );\n }\n\n /** Список целиком: приложение заводит избранное само, например по умолчанию для роли. */\n public setFavorites(menuId: string, ids: ReadonlyArray<IRtSideMenu.FavoriteId>): void {\n this.#updateFavorites(menuId, (): IRtSideMenu.FavoriteId[] => normalizeSideMenuFavorites(ids));\n }\n\n public setSubMenuMode(menuId: string, mode: IRtSideMenu.SubMenuMode): void {\n this.#update(menuId, { subMenuMode: mode });\n }\n\n public setSubMenuWidth(menuId: string, width: number): void {\n this.#update(menuId, { subMenuWidth: clampSideMenuWidth(width) });\n }\n\n /** Настройки меню уходят только по вызову приложения: кит сам ничего не удаляет. */\n public deleteSettings(menuId: string): void {\n this.#commit(omitSideMenuSettings(this.#fresh(), normalizeSideMenuId(menuId)));\n }\n\n /** Прочитать хранилище → поправить поля одного меню → записать. */\n #update(menuId: string, patch: Partial<IRtSideMenu.Settings>): void {\n const record: TRtSideMenuSettingsRecord = this.#fresh();\n const id: string = normalizeSideMenuId(menuId);\n\n this.#commit(patchSideMenuSettings(record, id, { ...normalizeSideMenuSettings(record[id]), ...patch }));\n }\n\n /** Список правится по свежей записи хранилища: соседняя вкладка могла его поменять. */\n #updateFavorites(menuId: string, next: (ids: IRtSideMenu.FavoriteId[]) => IRtSideMenu.FavoriteId[]): void {\n this.#update(menuId, { favorites: next([...(this.#freshSettings(menuId).favorites ?? [])]) });\n }\n\n #freshSettings(menuId: string): IRtSideMenu.Settings {\n return normalizeSideMenuSettings(this.#fresh()[normalizeSideMenuId(menuId)]);\n }\n\n #commit(record: TRtSideMenuSettingsRecord): void {\n this.#apply(record);\n\n try {\n this.#storage?.setItem(this.#key, JSON.stringify(record));\n this.#unsaved = false;\n } catch {\n // хранилище закрыто или полно — настройки живут в памяти до перезагрузки\n this.#unsaved = true;\n }\n }\n\n #apply(record: TRtSideMenuSettingsRecord): void {\n this.#record = record;\n this.#settings.set(readSideMenuSettings(record));\n }\n\n /** Запись из хранилища, а если оно недоступно или не приняло прошлую запись — последняя известная. */\n #fresh(): TRtSideMenuSettingsRecord {\n return this.#unsaved ? this.#record : (this.#load() ?? this.#record);\n }\n\n /** Пусто — хранилища нет или оно не отвечает. Сломанная запись читается пустой, но читается. */\n #load(): TRtSideMenuSettingsRecord | null {\n if (!this.#storage) {\n return null;\n }\n\n try {\n return parseSideMenuSettingsRecord(this.#storage.getItem(this.#key));\n } catch {\n return null;\n }\n }\n}\n\n/**\n * Сервис настроек бокового меню для приложения.\n *\n * ```ts\n * bootstrapApplication(App, { providers: [provideRtSideMenuSettings()] });\n * ```\n */\nexport function provideRtSideMenuSettings(config: IRtSideMenuSettingsConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: RT_SIDE_MENU_SETTINGS_CONFIG, useValue: config }, RtSideMenuSettingsService]);\n}\n","import { InjectionToken, Signal, TemplateRef } from '@angular/core';\n\nimport { IRtSideMenuIconContext } from './rt-side-menu.directives';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Меню, в котором стоит подпункт. Токен вместо самого компонента: меню объявляет подпункт в своих\n * импортах, и прямая ссылка замкнула бы круг «меню → подпункт → меню».\n *\n * Активность и раскрытость — два значения: активным остаётся адрес, на котором человек стоит, а\n * раскрытой — всякая папка, в которой нашлось совпадение.\n */\nexport interface IRtSideMenuHost {\n readonly activeMenuIds: Signal<ReadonlyArray<string | number>>;\n readonly expandedMenuIds: Signal<ReadonlyArray<string | number>>;\n readonly highlightedMenuId: Signal<string | number | null>;\n readonly subMenuQuery: Signal<string>;\n /** Номер, под которым меню хранит настройки, и пункты полосы: по ним избранное находит свой раздел. */\n readonly menuId: Signal<string>;\n readonly menuItems: Signal<ReadonlyArray<IRtSideMenu.Item>>;\n /** Набор, который подменю наполняет сейчас, до отбора поиском. */\n readonly shownSubMenu: Signal<IRtSideMenu.Item[]>;\n readonly favoritesCount: Signal<IRtSideMenu.FavoritesCount>;\n /** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */\n readonly isFavoritesSearchShown: Signal<boolean>;\n /** Свой значок пунктов, чьё имя кит не рисует: шаблон `rtSideMenuIcon` меню. */\n readonly ownIconTpl: Signal<TemplateRef<IRtSideMenuIconContext> | undefined>;\n /** Подсказки строк подменю: выключено — их нет ни у подписей, ни у кнопок строк. */\n readonly subMenuTooltipsShown: Signal<boolean>;\n /** Залитые значки строк и папок подменю. */\n readonly subItemIconFill: Signal<boolean>;\n /** Раскрыть или свернуть папку нажатием её заголовка. */\n toggleFolder(item: IRtSideMenu.Item): void;\n /** Уход указателя с панели: подменю, открытое наведением, закрывается. */\n toggleSubMenu(): void;\n /** Строку избранного тянут: подменю, открытое наведением, не закрывается уходом указателя. */\n holdSubMenu(held: boolean): void;\n}\n\nexport const RT_SIDE_MENU: InjectionToken<IRtSideMenuHost> = new InjectionToken<IRtSideMenuHost>('RT_SIDE_MENU');\n","import { inject, Pipe, PipeTransform } from '@angular/core';\nimport { ActivatedRoute, Router, UrlTree } from '@angular/router';\n\n/**\n * Адрес пункта так, как его строит ссылка роутера: относительно маршрута, на котором стоит меню.\n * Им строка подменю и переходит, и подписывает `href` — для средней кнопки и копирования ссылки.\n */\nexport function sideMenuLinkTree(router: Router, route: ActivatedRoute | null, link: string): UrlTree {\n return router.createUrlTree([link], { relativeTo: route ?? undefined });\n}\n\n/** `href` строки подменю. Переход строка делает сама: ссылка роутера не дала бы потребителю его взять. */\n@Pipe({\n name: 'rtSideMenuHref',\n})\nexport class RtSideMenuHrefPipe implements PipeTransform {\n readonly #router: Router = inject(Router);\n readonly #route: ActivatedRoute | null = inject(ActivatedRoute, { optional: true });\n\n public transform(link: string | undefined): string | null {\n return link ? this.#router.serializeUrl(sideMenuLinkTree(this.#router, this.#route, link)) : null;\n }\n}\n","import { isKitIconName, materialPairOf } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Значок пункта по его `icon`: имя кита как есть, имя Material первого кита — его парой из перечня\n * кита, как у `rt-menu-item`. `null` — кит такой значок не рисует: пункт берёт свой шаблон меню или\n * стоит без значка.\n */\nexport function sideMenuIconName(icon: string | undefined): IRtIcon.Name | null {\n if (!icon) {\n return null;\n }\n if (isKitIconName(icon)) {\n return icon;\n }\n return materialPairOf(icon);\n}\n\n/**\n * Имена значков меню, о которых оно должно предупредить: ни имени кита, ни пары в перечне. И значок\n * пункта, и значок кнопки строки закрывает свой шаблон меню.\n * Без предупреждения такой значок пуст, и пропуск не заметен. Обходит пункты на любую глубину;\n * каждое имя — один раз.\n */\nexport function unpairedSideMenuIcons(items: ReadonlyArray<IRtSideMenu.Item>, hasOwnIcon: boolean): string[] {\n const found: Set<string> = new Set<string>();\n const walk: (list: ReadonlyArray<IRtSideMenu.Item>) => void = (list: ReadonlyArray<IRtSideMenu.Item>): void => {\n for (const item of list) {\n if (!hasOwnIcon && item.icon && sideMenuIconName(item.icon) === null) {\n found.add(item.icon);\n }\n if (!hasOwnIcon && item.iconButton?.icon && sideMenuIconName(item.iconButton.icon) === null) {\n found.add(item.iconButton.icon);\n }\n walk(item.submenu ?? []);\n }\n };\n walk(items);\n\n return [...found];\n}\n\n/**\n * Предупреждение разработчику приложения об именах из `unpairedSideMenuIcons`: имя без значка кита и\n * без пары рисует пункт без значка, и пропуск без предупреждения не заметен.\n */\nexport function warnUnpairedSideMenuIcons(menuId: string, items: ReadonlyArray<IRtSideMenu.Item>, hasOwnIcon: boolean): void {\n const unpaired: string[] = unpairedSideMenuIcons(items, hasOwnIcon);\n\n if (unpaired.length) {\n const names: string = unpaired.map((name: string): string => `«${name}»`).join(', ');\n // eslint-disable-next-line no-console -- предупреждение разработчику приложения: другого канала у кита нет\n console.warn(\n `rt-side-menu «${menuId}»: значков ${names} нет ни в наборе кита, ни в перечне имён Material. ` +\n 'Задайте имя кита или свой значок через <ng-template rtSideMenuIcon>.'\n );\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { sideMenuIconName } from './rt-side-menu-icon.logic';\n\n/** Значок пункта, который рисует кит: имя кита или пара имени Material; `null` — такого нет. */\n@Pipe({\n name: 'rtSideMenuIcon',\n})\nexport class RtSideMenuIconPipe implements PipeTransform {\n public transform(icon: string | undefined): IRtIcon.Name | null {\n return sideMenuIconName(icon);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\nimport { splitSideMenuTitle } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Подпись пункта кусками: отмечено ровно то, чем она совпала с запросом поиска. */\n@Pipe({\n name: 'rtSideMenuTitleParts',\n})\nexport class RtSideMenuTitlePartsPipe implements PipeTransform {\n public transform(name: string | undefined, query: string): IRtSideMenu.TitlePart[] {\n return splitSideMenuTitle(name ?? '', query);\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ErrorHandler,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n output,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { sideMenuFavoritesSection } from '../rt-side-menu-favorites.logic';\nimport { RtSideMenuSettingsService } from '../rt-side-menu-settings.service';\nimport { RtSideMenuHrefPipe, sideMenuLinkTree } from '../rt-side-menu-href.pipe';\nimport { RtSideMenuIconPipe } from '../rt-side-menu-icon.pipe';\nimport { RtSideMenuTitlePartsPipe } from '../rt-side-menu-title-parts.pipe';\nimport { sideMenuPressOpensInBrowser } from '../rt-side-menu.logic';\nimport { IRtSideMenu } from '../rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from '../rt-side-menu.tokens';\n\nconst BEM_BLOCK: string = 'rt-side-menu-sub-item';\n/** Строка блока избранного и кнопка избранного в ней: фокус уходит на соседнюю до удаления строки. */\nconst FAVORITE_ROW: string = '.rt-side-menu-favorites__row';\nconst FAVORITE_BUTTON: string = '.rt-side-menu-sub-item__favorite button';\n\n/**\n * Строка подменю: пункт со ссылкой или папка со своими пунктами. Раскрытость, подсветку и запрос\n * строка берёт у меню — своего состояния у неё нет.\n *\n * В разделе, где приложение включило избранное, у пункта со ссылкой стоит звезда; в строке блока\n * избранного — кнопка «убрать» и слот `[rtSideMenuSubItemAction]`, куда блок кладёт ручку.\n */\n@Component({\n selector: 'rt-side-menu-sub-item',\n host: { class: BEM_BLOCK },\n templateUrl: './rt-side-menu-sub-item.component.html',\n styleUrls: ['./rt-side-menu-sub-item.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtExpansionPanelComponent,\n RtExpansionPanelContentDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtTooltipDirective,\n RtSideMenuTitlePartsPipe,\n RtSideMenuIconPipe,\n RtSideMenuHrefPipe,\n ],\n})\nexport class RtSideMenuSubItemComponent {\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n readonly #router: Router = inject(Router);\n readonly #route: ActivatedRoute | null = inject(ActivatedRoute, { optional: true });\n readonly #errors: ErrorHandler = inject(ErrorHandler);\n\n protected readonly menuRef: IRtSideMenuHost = inject(RT_SIDE_MENU);\n protected readonly narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */\n protected readonly favoritesIcons: RtSideMenuSettingsService['favoritesIcons'] | null = this.#settings?.favoritesIcons ?? null;\n protected readonly addLabel: Signal<string> = rtKitLabel('sideMenuFavoriteAdd');\n protected readonly removeLabel: Signal<string> = rtKitLabel('sideMenuFavoriteRemove');\n /** Избранное включено у раздела, чьё подменю показано, и приложение поставило настройки меню. */\n protected readonly favoritesOn: Signal<boolean> = computed(\n (): boolean => !!this.#settings && sideMenuFavoritesSection(this.menuRef.menuItems(), this.menuRef.shownSubMenu()) !== null\n );\n protected readonly favoriteIds: Signal<ReadonlyArray<IRtSideMenu.FavoriteId>> = computed(\n (): ReadonlyArray<IRtSideMenu.FavoriteId> => this.#settings?.favoriteIds(this.menuRef.menuId())() ?? []\n );\n\n public readonly item: InputSignal<IRtSideMenu.Item> = input.required<IRtSideMenu.Item>();\n /** Строка стоит в блоке избранного: вместо звезды — «убрать», без номера и подсветки клавиатуры. */\n public readonly inFavorites: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n\n /**\n * Нажатие строки уходит потребителю раньше перехода. Потребитель, взявший переход на себя,\n * отменяет умолчание события — тогда строка не переходит. Нажатие с клавишей или не левой\n * кнопкой остаётся браузеру: он открывает адрес строки сам.\n */\n public onClickSubMenu(item: IRtSideMenu.Item, event: MouseEvent): void {\n this.clickSubMenuAction.emit({ item, event });\n\n if (event.defaultPrevented || !item.link || sideMenuPressOpensInBrowser(event)) {\n return;\n }\n event.preventDefault();\n // Отказ перехода уходит обработчику ошибок приложения, как у ссылки роутера.\n this.#router\n .navigateByUrl(sideMenuLinkTree(this.#router, this.#route, item.link))\n .catch((error: unknown): void => this.#errors.handleError(error));\n }\n\n /** Кнопка потребителя в строке: ни перехода по ссылке строки, ни закрытия подменю. */\n public onClickSubMenuAdditional(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.clickSubMenuAdditionalAction.emit({ data: item.iconButton?.data, event });\n }\n\n /** Звезда переключает избранное и больше ничего: ни перехода, ни закрытия подменю. */\n public onToggleFavorite(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.#settings?.toggleFavorite(this.menuRef.menuId(), item.id);\n }\n\n /**\n * «Убрать» в строке блока. Фокус уходит на такую же кнопку соседней строки до того, как строка\n * исчезнет: иначе он падал бы на страницу, и человек с клавиатуры терял бы место.\n */\n public onRemoveFavorite(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n\n const row: Element | null = event.target instanceof Element ? event.target.closest(FAVORITE_ROW) : null;\n const neighbour: Element | null | undefined = row?.nextElementSibling ?? row?.previousElementSibling;\n\n neighbour?.querySelector<HTMLElement>(FAVORITE_BUTTON)?.focus();\n this.#settings?.removeFavorite(this.menuRef.menuId(), item.id);\n }\n\n public onToggleFolder(item: IRtSideMenu.Item): void {\n this.menuRef.toggleFolder(item);\n }\n}\n","<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 Номер пункта на самой строке — признак для правил приложения: по нему оно находит строку,\n например ту, что открывает страницу раздела. Стоит и у строки блока избранного.\n Строка блока избранного без номера: по номеру клавиатура нажимает строку списка. И без\n родного перетаскивания ссылки: нажатие на ручку внутри строки браузер забирал себе,\n и перетаскивание CDK не начиналось.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\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 <!-- Кусок без отметки тоже узел: голая интерполяция разрывала бы подпись пробелами. -->\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 Строка блока несёт «убрать», а не звезду: всё в блоке уже в избранном. Звезда —\n у пункта со своим адресом в разделе с избранным: полая — не в избранном,\n закрашенная цветом темы — в нём. Имя кнопки называет следующее нажатие.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\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 [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 <!-- Действия, которые строке передаёт владелец: блок избранного кладёт сюда ручку. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- Кнопка потребителя видна всегда и стоит последней: кнопки избранного встают перед ней. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- Имя без значка кита берёт свой шаблон меню, как значок строки. Без шаблона кнопка пуста и\n незнакомого имени значку не передаёт: значка с таким именем нет, и ждать его некому. -->\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 [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\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 Папка — раскрывающаяся панель кита. Рисуется и с пустым составом: отбор оставляет так\n ту, что совпала подписью. Шеврона у такой папки нет — раскрывать нечего. Раскрытие\n держит меню: панель сообщает о нажатии, меню переключает папку. Кнопке заголовка\n отдан номер пункта — по нему клавиатура меню нажимает строку.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"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 <!-- Значок строки: имя кита или пара имени Material, а имя, которого кит не рисует, — свой шаблон меню. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\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","import { CdkDrag, CdkDragDrop, CdkDragEnd, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n Injector,\n linkedSignal,\n output,\n OutputEmitterRef,\n Signal,\n signal,\n viewChildren,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { sideMenuFavoritesSection, findSideMenuFavoriteItems, isSideMenuDropOutside } from '../rt-side-menu-favorites.logic';\nimport { RtSideMenuSettingsService } from '../rt-side-menu-settings.service';\nimport { filterSideMenuItems } from '../rt-side-menu.logic';\nimport { IRtSideMenu } from '../rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from '../rt-side-menu.tokens';\nimport { RtSideMenuSubItemComponent } from '../sub-item/rt-side-menu-sub-item.component';\n\nconst BEM_BLOCK: string = 'rt-side-menu-favorites';\n/** Панель подменю: уход указателя с неё закрывает подменю, открытое наведением. */\nconst SUB_MENU_PANEL: string = '.rt-side-menu__panel';\n\n/**\n * Блок избранного вверху подменю: выбранные человеком разделы в порядке его списка. Перенос блока\n * первого кита на части второго, без Material: блок — раскрывающаяся панель кита, как папка\n * подменю, строки тянутся за ручку перетаскиванием CDK.\n *\n * Строки собираются из пунктов самого меню, а не из хранилища: подпись следует языку пунктов, адрес\n * — объявлению. Номер без пункта в меню не показывается, но из списка не уходит. Строку рисует тот же\n * подпункт, что и в списке.\n *\n * Избранное у каждого пункта полосы своё и по умолчанию выключено: блок стоит только в подменю пункта\n * с флагом `favorites`. Без настроек меню (`provideRtSideMenuSettings()`) блока нет вовсе. Показать\n * нечего — блок места не занимает.\n */\n@Component({\n selector: 'rt-side-menu-favorites',\n host: { class: BEM_BLOCK },\n templateUrl: './rt-side-menu-favorites.component.html',\n styleUrls: ['./rt-side-menu-favorites.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n CdkDrag,\n CdkDragHandle,\n CdkDropList,\n\n // directives\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtExpansionPanelContentDirective,\n\n // components\n RtExpansionPanelComponent,\n RtIconButtonComponent,\n RtIconComponent,\n RtSideMenuSubItemComponent,\n ],\n})\nexport class RtSideMenuFavoritesComponent {\n readonly #menu: IRtSideMenuHost = inject(RT_SIDE_MENU);\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n readonly #injector: Injector = inject(Injector);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef);\n /** Строку тянут: подменю держится открытым, пока её не бросят. */\n readonly #dragging: WritableSignal<boolean> = signal(false);\n readonly #section: Signal<IRtSideMenu.Item | null> = computed((): IRtSideMenu.Item | null =>\n this.#settings ? sideMenuFavoritesSection(this.#menu.menuItems(), this.#menu.shownSubMenu()) : null\n );\n\n /** В поиске что-то набрано: блок показывает совпавшие строки раскрытым, сохранённое не трогая. */\n protected readonly searching: Signal<boolean> = computed((): boolean => this.#menu.subMenuQuery().trim() !== '');\n protected readonly rows: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] => {\n const section: IRtSideMenu.Item | null = this.#section();\n\n if (!this.#settings || !section || (this.searching() && !this.#menu.isFavoritesSearchShown())) {\n return [];\n }\n\n const found: IRtSideMenu.Item[] = findSideMenuFavoriteItems([section], this.#settings.favoriteIds(this.#menu.menuId())());\n\n return filterSideMenuItems(found, this.#menu.subMenuQuery());\n });\n /**\n * Блок раздела свёрнут: заголовок и черта остаются, строк нет. Состояние своё у каждого раздела и\n * лежит в настройках меню. Поиск его не меняет: на время поиска блок стоит раскрытым.\n */\n protected readonly collapsed: Signal<boolean> = computed((): boolean => {\n const section: IRtSideMenu.Item | null = this.#section();\n\n return !!section && !!this.#settings?.favoritesCollapsed(this.#menu.menuId())().includes(section.id);\n });\n /**\n * Раскрытие панели блока. Начинается заново с сохранённого выбора при каждой его смене и при\n * входе в поиск и выходе из него: свёрнутый в поиске блок — выбор на время поиска, он не\n * сохраняется и с концом поиска возвращается к сохранённому.\n */\n protected readonly open: WritableSignal<boolean> = linkedSignal({\n source: (): { collapsed: boolean; searching: boolean } => ({ collapsed: this.collapsed(), searching: this.searching() }),\n computation: (state: { collapsed: boolean; searching: boolean }): boolean => !state.collapsed || state.searching,\n });\n /** Заголовок показывает число строк: всегда, у свёрнутого блока или никогда — по входу меню. */\n protected readonly countShown: Signal<boolean> = computed((): boolean => {\n const mode: IRtSideMenu.FavoritesCount = this.#menu.favoritesCount();\n\n return mode === 'always' || (mode === 'collapsed' && !this.open());\n });\n /** Ручки строк по порядку: стрелка возвращает фокус на ручку переставленной строки. */\n protected readonly handles: Signal<ReadonlyArray<ElementRef<HTMLElement>>> = viewChildren<string, ElementRef<HTMLElement>>('handle', {\n read: ElementRef,\n });\n protected readonly dragIcon: IRtIcon.Name = this.#settings?.favoritesIcons.drag ?? 'arrows-v';\n /** Узкий экран: подсказка у ручки не показывается — наводиться там нечем. */\n protected readonly narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n protected readonly titleLabel: Signal<string> = rtKitLabel('sideMenuFavorites');\n protected readonly dragLabel: Signal<string> = rtKitLabel('sideMenuFavoriteDrag');\n protected readonly expandLabel: Signal<string> = rtKitLabel('sideMenuFavoritesExpand');\n protected readonly collapseLabel: Signal<string> = rtKitLabel('sideMenuFavoritesCollapse');\n\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n\n constructor() {\n // Блок уничтожили посреди тяги — указатель увёл подменю на другой раздел. CDK закрывает тягу\n // без события конца, и удержание осталось бы навсегда.\n inject(DestroyRef).onDestroy((): void => this.#release());\n }\n\n /** Заголовок нажат: выбор ложится в настройки меню. Во время поиска он не сохраняется. */\n public onToggle(expanded: boolean): void {\n const section: IRtSideMenu.Item | null = this.#section();\n\n if (this.#settings && section && !this.searching()) {\n this.#settings.setFavoritesCollapsed(this.#menu.menuId(), section.id, !expanded);\n }\n }\n\n /** Строка в руке: подменю, открытое наведением, не закрывается, когда рука выходит за панель. */\n public onDragStart(): void {\n this.#dragging.set(true);\n this.#menu.holdSubMenu(true);\n }\n\n /** Строка брошена за панелью: уход указателя, пропущенный за время тяги, срабатывает сейчас. */\n public onDragEnd(event: CdkDragEnd): void {\n this.#release();\n\n const panel: Element | null = this.#host.nativeElement.closest(SUB_MENU_PANEL);\n\n if (panel && isSideMenuDropOutside(panel.getBoundingClientRect(), event.dropPoint)) {\n this.#menu.toggleSubMenu();\n }\n }\n\n /** Брошенная вне блока строка ничего не меняет: убирает из избранного кнопка, а не перетаскивание. */\n public onDrop(event: CdkDragDrop<IRtSideMenu.Item[]>): void {\n if (event.isPointerOverContainer) {\n this.#move(event.previousIndex, event.currentIndex);\n }\n }\n\n /** Стрелка на ручке переставляет строку на соседнее место, и фокус едет вместе с ручкой. */\n public onHandleKey(event: Event, index: number, step: number): void {\n // Стрелки не уходят в меню: там они ведут подсветку по списку раздела.\n event.preventDefault();\n event.stopPropagation();\n\n const target: number = index + step;\n\n if (target < 0 || target >= this.rows().length) {\n return;\n }\n\n this.#move(index, target);\n afterNextRender((): void => this.handles()[target]?.nativeElement.querySelector<HTMLElement>('button')?.focus(), {\n injector: this.#injector,\n });\n }\n\n /** Нажатие ручки — не переход по строке: она стоит внутри ссылки пункта. */\n public onHandleClick(event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n }\n\n #release(): void {\n if (!this.#dragging()) {\n return;\n }\n\n this.#dragging.set(false);\n this.#menu.holdSubMenu(false);\n }\n\n /** Место в блоке переводится в место списка по свежей записи хранилища: скрытые номера остаются на своих. */\n #move(from: number, to: number): void {\n if (!this.#settings || from === to) {\n return;\n }\n\n const visible: IRtSideMenu.FavoriteId[] = this.rows().map((item: IRtSideMenu.Item): IRtSideMenu.FavoriteId => item.id);\n this.#settings.moveVisibleSideMenuFavorite(this.#menu.menuId(), visible, from, to);\n }\n}\n","<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n Блок — раскрывающаяся панель кита, как папка подменю, со звездой вместо значка папки. Имя\n кнопки заголовка называет следующее нажатие; число строк — по входу меню.\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 Ручка стоит внутри строки, последней перед кнопкой потребителя. Строку тянут\n только за ручку: остальная строка — ссылка. С клавиатуры строку переставляют\n стрелки на ручке: перетаскивание CDK клавиш не знает.\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 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","import { DestroyRef } from '@angular/core';\n\n/**\n * Отложенное закрытие подменю, открытого наведением. Уход указателя ставит закрытие, возврат на\n * панель или наведение на пункт с подменю его снимают. Нулевая задержка закрывает сразу, как\n * раньше: без входа задержки меню ведёт себя прежним образом.\n */\nexport class RtSideMenuCloseTimer {\n #handle: ReturnType<typeof setTimeout> | null = null;\n\n constructor(destroyRef: DestroyRef) {\n destroyRef.onDestroy((): void => this.cancel());\n }\n\n /** Закрыть через `delay` миллисекунд; поставленное раньше закрытие заменяется новым. */\n public run(delay: number, close: () => void): void {\n this.cancel();\n if (delay <= 0) {\n close();\n\n return;\n }\n this.#handle = setTimeout((): void => {\n this.#handle = null;\n close();\n }, delay);\n }\n\n public cancel(): void {\n if (this.#handle !== null) {\n clearTimeout(this.#handle);\n this.#handle = null;\n }\n }\n}\n","import { signal, Signal, WritableSignal } from '@angular/core';\n\nimport { stepSideMenuHighlight, walkSideMenuItems } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Что клавиатура просит у меню: открыть пункт и стереть запрос. Сама она ни того, ни другого не умеет. */\nexport interface IRtSubMenuKeyboardHooks {\n open: (item: IRtSideMenu.Item) => void;\n clearQuery: () => void;\n}\n\n/**\n * Ходьба по подменю с клавиатуры. Проверяется вызовом, без подъёма компонента.\n *\n * Клавиши слушает поле поиска: фокус из поля не уходит, и между стрелками работает набор текста.\n * Съедаются только клавиши ходьбы — на остальные ответ отрицательный, иначе поле перестанет\n * принимать буквы. Подсветка держится по номеру пункта: видимый список пересобирается на каждую\n * букву запроса.\n */\nexport class RtSubMenuKeyboard {\n readonly #highlightedId: WritableSignal<string | number | null> = signal(null);\n /**\n * Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от\n * активного адреса, и свернуть такую папку можно, только помня об этом отдельно.\n */\n readonly #openedIds: WritableSignal<Array<string | number>> = signal([]);\n readonly #closedIds: WritableSignal<Array<string | number>> = signal([]);\n readonly #hooks: IRtSubMenuKeyboardHooks;\n\n public readonly highlightedId: Signal<string | number | null> = this.#highlightedId.asReadonly();\n public readonly openedIds: Signal<Array<string | number>> = this.#openedIds.asReadonly();\n public readonly closedIds: Signal<Array<string | number>> = this.#closedIds.asReadonly();\n\n constructor(hooks: IRtSubMenuKeyboardHooks) {\n this.#hooks = hooks;\n }\n\n /** Всё, что клавиатура успела наделать, снимается: набран новый запрос или подменю закрылось. */\n public reset(): void {\n this.#highlightedId.set(null);\n this.#openedIds.set([]);\n this.#closedIds.set([]);\n }\n\n /** Нажата клавиша в поле поиска. Отвечает, съедена ли она: только съеденная отменяет умолчание. */\n public press(key: string, items: ReadonlyArray<IRtSideMenu.Item>, expandedIds: ReadonlyArray<string | number>): boolean {\n const walk: IRtSideMenu.Item[] = walkSideMenuItems(items, expandedIds);\n const current: IRtSideMenu.Item | null = walk.find((item: IRtSideMenu.Item): boolean => item.id === this.#highlightedId()) ?? null;\n\n switch (key) {\n case 'ArrowDown':\n case 'ArrowUp':\n this.#highlightedId.set(stepSideMenuHighlight(walk, this.#highlightedId(), key === 'ArrowDown' ? 1 : -1));\n\n return true;\n case 'ArrowRight':\n return this.setFolderOpen(current, true);\n case 'ArrowLeft':\n return this.setFolderOpen(current, false);\n case 'Enter':\n return this.#enter(current);\n case 'Escape':\n this.reset();\n this.#hooks.clearQuery();\n\n return true;\n default:\n return false;\n }\n }\n\n /** Enter открывает подсвеченное: у пункта со ссылкой это переход, у папки — раскрытие. */\n #enter(item: IRtSideMenu.Item | null): boolean {\n if (item === null) {\n return false;\n }\n\n if (item.link) {\n this.#hooks.open(item);\n\n return true;\n }\n\n return this.setFolderOpen(item, true);\n }\n\n /** Раскрыть или свернуть папку: стрелкой, Enter или нажатием её заголовка. */\n public setFolderOpen(item: IRtSideMenu.Item | null, open: boolean): boolean {\n if (item === null || !item.submenu?.length) {\n return false;\n }\n\n const without: (ids: Array<string | number>) => Array<string | number> = (ids: Array<string | number>): Array<string | number> =>\n ids.filter((id: string | number): boolean => id !== item.id);\n\n this.#openedIds.update((ids: Array<string | number>): Array<string | number> => (open ? [...without(ids), item.id] : without(ids)));\n this.#closedIds.update((ids: Array<string | number>): Array<string | number> => (open ? without(ids) : [...without(ids), item.id]));\n\n return true;\n }\n}\n","import { computed, DestroyRef, signal, Signal, WritableSignal } from '@angular/core';\n\nimport {\n clampSideMenuWidth,\n reportedSideMenuWidth,\n sideMenuWidthByKey,\n startSideMenuWidthDrag,\n TRtPointerListen,\n} from './rt-side-menu.logic';\n\n/** Что тяга просит у меню. Сама она ни о ширине не помнит, ни наружу говорить не умеет. */\nexport interface IRtSideMenuResizeHooks {\n /** Ширина, о которой меню просит по концу тяги или по нажатой клавише. */\n askWidth: (width: number) => void;\n /** Начало и конец тяги для потребителя: по ним он накрывает чужие кадры и снимает накрытие. */\n started: () => void;\n ended: () => void;\n /** Панель подменю — по ней замеряется нарисованная ширина. Пустая, пока панели нет. */\n panel: () => HTMLElement | null;\n /** Ширина, названная потребителем или настройками. Пустая — ширину ставит оформление. */\n namedWidth: () => number | null;\n}\n\n/**\n * Тяга ширины закреплённого подменю: натянутая ширина и снятие слушателей. Механика событий —\n * `startSideMenuWidthDrag` в логике рядом, здесь держится только то, что живёт дольше одного события.\n */\nexport class RtSideMenuResize {\n /** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */\n readonly #draggedWidth: WritableSignal<number | null> = signal(null);\n readonly #listen: TRtPointerListen;\n readonly #hooks: IRtSideMenuResizeHooks;\n\n #stop: (() => void) | null = null;\n\n public readonly draggedWidth: Signal<number | null> = this.#draggedWidth.asReadonly();\n\n /**\n * Ширина для диктора: только то, что кит знает сам. Не названо ничего и ничего не тянули — числа\n * нет: выдуманное назвало бы ширину, которой панель не нарисована.\n */\n public readonly valueNow: Signal<number | null> = computed((): number | null => {\n const width: number | null = this.#draggedWidth() ?? this.#hooks.namedWidth();\n\n return width === null ? null : clampSideMenuWidth(width);\n });\n\n /**\n * Меню, разрушенное посреди тяги, кончает её здесь же: иначе накрытие чужих кадров, снимаемое по\n * концу, осталось бы стоять.\n */\n constructor(listen: TRtPointerListen, hooks: IRtSideMenuResizeHooks, destroyRef?: DestroyRef) {\n this.#listen = listen;\n this.#hooks = hooks;\n destroyRef?.onDestroy((): void => this.finish());\n }\n\n /** Тяга идёт. Второе нажатие при начатой тяге ничего не начинает: указатель уже захвачен. */\n public isRunning(): boolean {\n return this.#stop !== null;\n }\n\n /**\n * Нажата клавиша на ручке. Просьба о ширине уходит сразу: жеста нет, и держать её не до чего.\n * Отрицательный ответ — клавиша не о ширине, и умолчание у неё не отменяется.\n */\n public pressKey(key: string, from: number): boolean {\n const width: number | null = sideMenuWidthByKey(key, from);\n\n if (width === null) {\n return false;\n }\n\n this.#hooks.askWidth(width);\n\n return true;\n }\n\n public start(event: PointerEvent, startWidth: number): void {\n if (this.isRunning()) {\n return;\n }\n\n const stop: (() => void) | null = startSideMenuWidthDrag(event, startWidth, this.#listen, {\n onWidth: (width: number): void => this.#draggedWidth.set(width),\n onEnd: (): void => this.finish(),\n });\n\n if (stop !== null) {\n this.#stop = stop;\n this.#hooks.started();\n }\n }\n\n /** Конец тяги. Наружу уходит то число, которым панель нарисована: нижний предел держит оформление. */\n public finish(): void {\n if (!this.isRunning()) {\n return;\n }\n\n // Замер идёт до сброса натянутой ширины: сбросив её, панель перерисуют, и мерить будет нечего.\n const width: number | null = this.#draggedWidth();\n const drawn: number | null = width === null ? null : reportedSideMenuWidth(width, this.#hooks.panel());\n\n this.#stop?.();\n this.#stop = null;\n this.#draggedWidth.set(null);\n\n if (drawn !== null) {\n this.#hooks.askWidth(drawn);\n }\n\n this.#hooks.ended();\n }\n}\n","import { booleanAttribute, Directive, input, InputSignal, InputSignalWithTransform } from '@angular/core';\n\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\n\n/**\n * Входы вида бокового меню: подписи и заливка значков, поле поиска, подсказка прокрутки панели. Ни\n * один не меняет поведения — по ним приложение даёт меню вид первого кита, а без них меню рисуется\n * как раньше. Вынесены из класса меню, чтобы тот оставался в пределе длины файла.\n */\n@Directive()\nexport abstract class RtSideMenuLookBase {\n /** Подсказки строк подменю — у подписей и у кнопок строк. Доступные имена остаются. */\n public readonly subMenuTooltipsShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /**\n * Подписи под значками полосы. Выключено — имя пункта уходит в подсказку справа от значка, а\n * подсказка подчиняется `subMenuTooltipsShown`; доступное имя пункт держит всегда.\n */\n public readonly railTitlesShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Залитые значки полосы — залитый рисунок материального набора и заливка шрифта лигатуры. */\n public readonly railIconFill: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Залитые значки строк и папок подменю — так же, как `railIconFill` у полосы. */\n public readonly subItemIconFill: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Подсказка прокрутки внизу панели подменю, как у полосы: длинный список не обрывается молча. */\n public readonly panelScrollHintShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Размер поля поиска подменю — ступень `rt-input`. */\n public readonly searchSize: InputSignal<IRtInput.Size> = input<IRtInput.Size>('sm');\n /** Вид поля поиска подменю — вид `rt-input`. */\n public readonly searchAppearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n}\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Шапка бокового меню: над полосой пунктов. */\n@Directive({\n selector: '[rtSideMenuHeader]',\n})\nexport class RtSideMenuHeaderDirective {}\n\n/** Подвал бокового меню: под полосой пунктов. */\n@Directive({\n selector: '[rtSideMenuFooter]',\n})\nexport class RtSideMenuFooterDirective {}\n\n/**\n * Контекст своего значка: пункт, имя, которое надо нарисовать, и место. Шаблон один на меню и рисует\n * и значок строки, и кнопку строки, поэтому рисунок выбирают по `icon`, а не по `item.icon`: у кнопки\n * это имя её значка.\n */\nexport interface IRtSideMenuIconContext {\n $implicit: IRtSideMenu.Item;\n /** Имя значка этого места: значка строки или кнопки строки. */\n icon: string;\n /** Место: значок строки или кнопка строки. */\n slot: 'icon' | 'iconButton';\n}\n\n/**\n * Помечает `<ng-template>` своего значка пунктов меню — для имён, которых нет ни в наборе кита, ни\n * в перечне имён Material. Шаблон один на меню и рисует и значок строки, и значок кнопки строки; кит\n * ставит его на место значка размером значка и цветом строки. Рисунок выбирают по `icon` контекста.\n * Значок, который кит рисует сам, шаблон не перебивает.\n *\n * ```html\n * <rt-side-menu [menuItems]=\"items\">\n * <ng-template rtSideMenuIcon let-icon=\"icon\"><img [src]=\"'/icons/' + icon + '.svg'\" alt=\"\" /></ng-template>\n * </rt-side-menu>\n * ```\n */\n@Directive({\n selector: 'ng-template[rtSideMenuIcon]',\n})\nexport class RtSideMenuIconDirective {\n public readonly templateRef: TemplateRef<IRtSideMenuIconContext> = inject<TemplateRef<IRtSideMenuIconContext>>(TemplateRef);\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате; убрать его нечем, подпись задаёт каркас\n public static ngTemplateContextGuard(_directive: RtSideMenuIconDirective, context: unknown): context is IRtSideMenuIconContext {\n return true;\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n ElementRef,\n effect,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n isDevMode,\n numberAttribute,\n output,\n OutputEmitterRef,\n Renderer2,\n Signal,\n signal,\n TemplateRef,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { RouterLink } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport {\n RtScrollAreaComponent,\n RtScrollAreaContentDirective,\n RtScrollAreaFooterDirective,\n RtScrollAreaHeaderDirective,\n} from '@rt-tools/ui-kit-v2/scroll-area';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtSideMenuFavoritesComponent } from './favorites/rt-side-menu-favorites.component';\nimport { RtSideMenuCloseTimer } from './rt-side-menu-close-timer';\nimport { RtSubMenuKeyboard } from './rt-side-menu-keyboard';\nimport { RtSideMenuResize } from './rt-side-menu-resize';\nimport { normalizeSideMenuId, RT_SIDE_MENU_DEFAULT_ID } from './rt-side-menu-settings.logic';\nimport { RtSideMenuSettingsService } from './rt-side-menu-settings.service';\nimport { warnUnpairedSideMenuIcons } from './rt-side-menu-icon.logic';\nimport { RtSideMenuIconPipe } from './rt-side-menu-icon.pipe';\nimport { RtSideMenuLookBase } from './rt-side-menu-look.base';\nimport {\n IRtSideMenuIconContext,\n RtSideMenuFooterDirective,\n RtSideMenuHeaderDirective,\n RtSideMenuIconDirective,\n} from './rt-side-menu.directives';\nimport {\n clampSideMenuWidth,\n drawnSideMenuWidth,\n filterSideMenuItems,\n pinnedSideMenuItems,\n RT_SIDE_MENU_WIDTH_MAX,\n RT_SIDE_MENU_WIDTH_MIN,\n sideMenuIdsToExpand,\n} from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from './rt-side-menu.tokens';\nimport { RtSideMenuSubItemComponent } from './sub-item/rt-side-menu-sub-item.component';\n\nconst BEM_BLOCK: string = 'rt-side-menu';\n\n/**\n * Боковая навигация: полоса пунктов и подменю раздела рядом. Перенос бокового меню первого кита на\n * части второго; `rt-menu` — другое семейство, выпадающее меню действий.\n */\n@Component({\n selector: 'rt-side-menu',\n host: {\n class: BEM_BLOCK,\n // Раскладка хоста меняется только у закреплённого режима: панель встаёт в поток рядом с полосой.\n '[class.rt-side-menu--pinned]': 'isPinned()',\n // Натянутая ширина подменю приходит своим свойством: панель берёт наибольшее из него и ширины оформления.\n '[style.--rt-side-menu-panel-dragged-width]': 'panelWidthStyle()',\n // Кнопки избранного, ждущие наведения, в покое ширины не занимают.\n '[class.rt-side-menu--favorite-actions-none]': \"favoriteActionsReserve() === 'none'\",\n },\n templateUrl: './rt-side-menu.component.html',\n styleUrls: ['./rt-side-menu.component.scss'],\n providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n ReactiveFormsModule,\n RouterLink,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtInputComponent,\n RtScrollAreaComponent,\n RtScrollAreaContentDirective,\n RtScrollAreaFooterDirective,\n RtScrollAreaHeaderDirective,\n RtTooltipDirective,\n RtSideMenuFavoritesComponent,\n RtSideMenuIconPipe,\n RtSideMenuSubItemComponent,\n ],\n})\nexport class RtSideMenuComponent extends RtSideMenuLookBase implements IRtSideMenuHost {\n readonly #breakpoints: BreakpointsService = inject(BreakpointsService);\n readonly #renderer: Renderer2 = inject(Renderer2);\n /** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n\n /** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */\n readonly #resize: RtSideMenuResize = new RtSideMenuResize(\n (target: HTMLElement, name: string, handler: (event: PointerEvent) => void): (() => void) =>\n this.#renderer.listen(target, name, handler),\n {\n askWidth: (width: number): void => {\n this.#settings?.setSubMenuWidth(this.menuId(), width);\n this.subMenuWidthChange.emit(width);\n },\n started: (): void => this.subMenuResizeStart.emit(),\n ended: (): void => this.subMenuResizeEnd.emit(),\n panel: (): HTMLElement | null => this.panelRef()?.nativeElement ?? null,\n namedWidth: (): number | null => this.#width(),\n },\n inject(DestroyRef)\n );\n /** Закрытие подменю, отложенное входом `subMenuCloseDelay`. */\n readonly #closeTimer: RtSideMenuCloseTimer = new RtSideMenuCloseTimer(inject(DestroyRef));\n\n /** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */\n readonly #keyboard: RtSubMenuKeyboard = new RtSubMenuKeyboard({\n open: (item: IRtSideMenu.Item): void => this.#pressRow(item),\n clearQuery: (): void => this.searchControl.setValue(''),\n });\n\n /** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */\n readonly #hoverOpened: WritableSignal<boolean> = signal(false);\n /** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */\n readonly #searchHeld: WritableSignal<boolean> = signal(false);\n /** Строку избранного тянут: уход указателя за панель подменю не закрывает. */\n readonly #dragHeld: WritableSignal<boolean> = signal(false);\n /**\n * Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки\n * закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.\n */\n readonly #mode: Signal<IRtSideMenu.SubMenuMode> = computed(\n (): IRtSideMenu.SubMenuMode =>\n this.subMenuMode() ?? (this.pinShown() ? this.#settings?.subMenuMode(this.menuId())() : undefined) ?? 'hover'\n );\n readonly #width: Signal<number | null> = computed(\n (): number | null => this.subMenuWidth() ?? this.#settings?.subMenuWidth(this.menuId())() ?? null\n );\n readonly #pinnedSubMenu: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n this.isPinned() ? pinnedSideMenuItems(this.selectedSubMenu(), this.activeMenuIds(), this.menuItems()) : []\n );\n\n protected readonly searchControl: FormControl<string | null> = new FormControl<string | null>('');\n protected readonly panelRef: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('panel');\n protected readonly headerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtSideMenuHeaderDirective, { read: TemplateRef });\n protected readonly footerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtSideMenuFooterDirective, { read: TemplateRef });\n\n protected readonly searchLabel: Signal<string> = rtKitLabel('sideMenuSearch');\n protected readonly pinLabel: Signal<string> = rtKitLabel('sideMenuPin');\n protected readonly unpinLabel: Signal<string> = rtKitLabel('sideMenuUnpin');\n protected readonly nothingFoundLabel: Signal<string> = rtKitLabel('sideMenuNothingFound');\n protected readonly resizeLabel: Signal<string> = rtKitLabel('sideMenuResize');\n protected readonly backLabel: Signal<string> = rtKitLabel('sideMenuBack');\n\n /** Экран узкий: замер кита, и другого источника у этого признака нет. */\n protected readonly narrow: Signal<boolean> = this.#breakpoints.narrow;\n protected readonly resizeValueNow: Signal<number | null> = this.#resize.valueNow;\n protected readonly resizeValueMin: number = RT_SIDE_MENU_WIDTH_MIN;\n protected readonly resizeValueMax: number = RT_SIDE_MENU_WIDTH_MAX;\n\n /** Натянутая ширина панели. Своего выбора нет — свойство не ставится, и ширину берёт оформление. */\n protected readonly panelWidthStyle: Signal<string | null> = computed((): string | null => {\n const width: number | null = this.#resize.draggedWidth() ?? this.#width();\n\n return width === null ? null : `${clampSideMenuWidth(width)}px`;\n });\n\n /** Закрепление действует только на широком экране: на узком подменю занимает экран целиком. */\n protected readonly isPinned: Signal<boolean> = computed((): boolean => this.#mode() === 'pinned' && !this.narrow());\n /** Подменю держится набором запроса: уходом указателя не закрывается. */\n protected readonly isSearchHeld: Signal<boolean> = computed((): boolean => this.#searchHeld() && !this.isPinned());\n /** Закреплённое подменю открыто, пока ему есть что показать: указатель на это не влияет. */\n protected readonly subMenuOpened: Signal<boolean> = computed((): boolean =>\n this.isPinned() ? this.#pinnedSubMenu().length > 0 : this.#hoverOpened()\n );\n protected readonly visibleSubMenuItems: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n filterSideMenuItems(this.shownSubMenu(), this.subMenuQuery())\n );\n protected readonly isNothingFound: Signal<boolean> = computed(\n (): boolean => this.subMenuQuery().trim() !== '' && this.visibleSubMenuItems().length === 0\n );\n /** Пункт полосы подсвечен активным: выбранный человеком, а без выбора — пункт текущего адреса. */\n protected readonly activeRailIds: Signal<ReadonlyArray<string | number>> = computed((): ReadonlyArray<string | number> => {\n const selected: IRtSideMenu.Item | null = this.selectedItem();\n\n return selected ? [selected.id] : this.activeMenuIds();\n });\n\n /** Набор, который подменю наполняет сейчас, до отбора поиском. */\n public readonly shownSubMenu: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n this.isPinned() ? this.#pinnedSubMenu() : (this.selectedSubMenu() ?? [])\n );\n public readonly selectedItem: WritableSignal<IRtSideMenu.Item | null> = signal(null);\n public readonly selectedSubMenu: WritableSignal<IRtSideMenu.Item[] | null> = signal(null);\n /** Что набрано в поиске: строка берёт запрос отсюда, чтобы отметить совпавшее. */\n public readonly subMenuQuery: WritableSignal<string> = signal('');\n public readonly highlightedMenuId: Signal<string | number | null> = this.#keyboard.highlightedId;\n\n /**\n * Какие папки раскрыты. Пустой запрос оставляет раздел текущего адреса; непустой добавляет\n * папки с совпадением — иначе найденное лежит за закрытым заголовком.\n */\n public readonly expandedMenuIds: Signal<ReadonlyArray<string | number>> = computed((): ReadonlyArray<string | number> => {\n const found: Array<string | number> = this.subMenuQuery().trim() === '' ? [] : sideMenuIdsToExpand(this.visibleSubMenuItems());\n const closed: ReadonlyArray<string | number> = this.#keyboard.closedIds();\n\n return [...this.activeMenuIds(), ...found, ...this.#keyboard.openedIds()].filter(\n (id: string | number): boolean => !closed.includes(id)\n );\n });\n\n public readonly activeMenuIds: InputSignal<ReadonlyArray<string | number>> = input.required<ReadonlyArray<string | number>>();\n public readonly menuItems: InputSignal<ReadonlyArray<IRtSideMenu.Item>> = input<ReadonlyArray<IRtSideMenu.Item>>([]);\n /** Режим подменю от приложения. Задан — важнее сохранённого; нет ни того, ни другого — наведение. */\n public readonly subMenuMode: InputSignal<IRtSideMenu.SubMenuMode | undefined> = input<IRtSideMenu.SubMenuMode | undefined>(undefined);\n /** Ширина закреплённого подменю в пикселях. Пусто — сохранённая, её нет — оформление. */\n public readonly subMenuWidth: InputSignal<number | null | undefined> = input<number | null | undefined>(undefined);\n /** Номер, под которым меню хранит свои настройки: у двух меню приложения — два номера. */\n public readonly menuId: InputSignalWithTransform<string, string | null | undefined> = input<string, string | null | undefined>(\n RT_SIDE_MENU_DEFAULT_ID,\n { transform: normalizeSideMenuId }\n );\n\n /** Число строк в заголовке блока избранного: всегда, у свёрнутого блока или никогда. */\n public readonly favoritesCount: InputSignal<IRtSideMenu.FavoritesCount> = input<IRtSideMenu.FavoritesCount>('collapsed');\n /** Место под кнопки избранного, ждущие наведения: держать всегда или отдавать подписи. */\n public readonly favoriteActionsReserve: InputSignal<IRtSideMenu.FavoriteActionsReserve> =\n input<IRtSideMenu.FavoriteActionsReserve>('none');\n /** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */\n public readonly isFavoritesSearchShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Кнопка закрепления в шапке подменю; выключено — подменю только всплывает, если режим не задан входом. */\n public readonly pinShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /**\n * Задержка закрытия подменю, открытого наведением, в миллисекундах: уход указателя закрывает его\n * не сразу, а возврат на панель держит открытым. Ноль — закрытие сразу, как раньше.\n */\n public readonly subMenuCloseDelay: InputSignalWithTransform<number, unknown> = input<number, unknown>(0, {\n transform: (value: unknown): number => numberAttribute(value, 0),\n });\n\n public readonly subMenuModeChange: OutputEmitterRef<IRtSideMenu.SubMenuMode> = output<IRtSideMenu.SubMenuMode>();\n public readonly subMenuWidthChange: OutputEmitterRef<number> = output<number>();\n /** Начало и конец тяги: по ним потребитель накрывает чужие кадры и снимает накрытие. */\n public readonly subMenuResizeStart: OutputEmitterRef<void> = output<void>();\n public readonly subMenuResizeEnd: OutputEmitterRef<void> = output<void>();\n public readonly closeMobileMenuAction: OutputEmitterRef<void> = output<void>();\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n /** Свой значок пунктов, чьё имя кит не рисует; подпункты читают его через токен меню. */\n public readonly ownIconTpl: Signal<TemplateRef<IRtSideMenuIconContext> | undefined> = contentChild(RtSideMenuIconDirective, {\n read: TemplateRef,\n });\n\n constructor() {\n super();\n this.searchControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((query: string | null): void => {\n this.subMenuQuery.set(query ?? '');\n this.onSearchHold();\n // Видимый список пересобрался: подсветка и раскрытое стрелками к нему больше не относятся.\n this.#keyboard.reset();\n });\n\n // Имя без значка кита и без пары рисует пункт без значка, и пропуск без предупреждения не заметен.\n if (isDevMode()) {\n effect((): void => warnUnpairedSideMenuIcons(this.menuId(), this.menuItems(), this.ownIconTpl() !== undefined));\n }\n }\n\n public onClickMenu(item: IRtSideMenu.Item): void {\n if (this.isPinned()) {\n this.#pickPinnedSubMenu(item);\n this.closeMobileMenu();\n\n return;\n }\n\n this.selectedItem.set(item);\n\n if (item.submenu) {\n this.selectedSubMenu.set(item.submenu);\n this.#hoverOpened.set(true);\n } else if (this.selectedSubMenu()) {\n this.closeSubMenu();\n } else {\n // У пункта нет подменю, и открытого подменю тоже нет — закрывать нечего.\n }\n\n if (item.link) {\n this.closeMobileMenu();\n }\n }\n\n public onClickSubMenu({ item, event }: { item: IRtSideMenu.Item; event: MouseEvent }): void {\n if (!item.link) {\n return;\n }\n\n this.clickSubMenuAction.emit({ item, event });\n\n if (!this.isPinned()) {\n this.closeSubMenu();\n }\n\n this.closeMobileMenu();\n }\n\n public onClickSubMenuAdditional(payload: { data: IRtSideMenu.ItemData | undefined; event: MouseEvent }): void {\n this.clickSubMenuAdditionalAction.emit(payload);\n }\n\n public onBackToMainMenu(): void {\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n }\n\n /**\n * Наведение на пункт полосы открывает его подменю сразу; уход указателя с панели — без пункта —\n * и пункт без подменю закрывают его через задержку.\n */\n public toggleSubMenu(item?: IRtSideMenu.Item): void {\n if (this.isPinned() || (item === undefined && this.#isHeld())) {\n return;\n }\n\n if (item?.submenu) {\n this.#closeTimer.cancel();\n this.selectedSubMenu.set(item.submenu);\n this.#hoverOpened.set(true);\n } else if (this.selectedItem()?.submenu) {\n this.#closeTimer.cancel();\n this.selectedSubMenu.set(this.selectedItem()?.submenu ?? null);\n } else {\n // Ушедший с панели указатель мог за задержку взяться за поиск или строку избранного.\n this.#closeTimer.run(this.subMenuCloseDelay(), (): void => {\n if (item !== undefined || !this.#isHeld()) {\n this.closeSubMenu();\n }\n });\n }\n }\n\n /** Указатель вернулся на панель: отложенное закрытие снимается. */\n public cancelSubMenuClose(): void {\n this.#closeTimer.cancel();\n }\n\n public closeSubMenu(): void {\n this.#closeTimer.cancel();\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n this.#hoverOpened.set(false);\n this.#searchHeld.set(false);\n this.#keyboard.reset();\n }\n\n public holdSubMenu(held: boolean): void {\n this.#dragHeld.set(held);\n }\n\n public toggleFolder(item: IRtSideMenu.Item): void {\n this.#keyboard.setFolderOpen(item, !this.expandedMenuIds().includes(item.id));\n }\n\n /** Человек взялся за поле поиска: дальше подменю закрывает нажатие снаружи, а не уход указателя. */\n public onSearchHold(): void {\n if (!this.isPinned()) {\n this.#searchHeld.set(true);\n }\n }\n\n /** Клавиши поля поиска раздаются списку; умолчание отменяется только у съеденной. */\n public onSearchKeydown(event: KeyboardEvent): void {\n if (this.#keyboard.press(event.key, this.visibleSubMenuItems(), this.expandedMenuIds())) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n /** Нажат переключатель режима: выбор уходит в настройки меню и наружу. */\n public onSubMenuModeToggle(): void {\n const mode: IRtSideMenu.SubMenuMode = this.isPinned() ? 'hover' : 'pinned';\n\n this.#settings?.setSubMenuMode(this.menuId(), mode);\n this.subMenuModeChange.emit(mode);\n }\n\n public onResizeStart(event: PointerEvent): void {\n if (!this.isPinned() || this.#resize.isRunning() || event.button !== 0) {\n return;\n }\n\n // Иначе указатель выделяет подписи пунктов, и тяга выглядит выделением текста.\n event.preventDefault();\n this.#resize.start(event, this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null));\n }\n\n public onResizeKeydown(event: KeyboardEvent): void {\n const from: number = this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null);\n\n if (this.isPinned() && this.#resize.pressKey(event.key, from)) {\n event.preventDefault();\n }\n }\n\n public closeMobileMenu(): void {\n if (this.narrow()) {\n this.closeMobileMenuAction.emit();\n }\n }\n\n #isHeld(): boolean {\n return this.#searchHeld() || this.#dragHeld();\n }\n\n /**\n * Нажат пункт полосы, пока подменю закреплено: нажатие — выбор раздела. Пункт со своим адресом и\n * без разделов выбор снимает: у страницы нет разделов, и панель прежнего раздела врала бы.\n */\n #pickPinnedSubMenu(item: IRtSideMenu.Item): void {\n if (item.submenu?.length) {\n this.selectedItem.set(item);\n this.selectedSubMenu.set(item.submenu);\n } else if (item.link) {\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n } else {\n return;\n }\n\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n }\n\n /**\n * Строка ищется по номеру среди узлов панели с id и нажимается как мышью: у ссылки номер стоит\n * на ней самой, у папки — на кнопке заголовка её раскрывающейся панели.\n */\n #pressRow(item: IRtSideMenu.Item): void {\n const rows: HTMLElement[] = Array.from(this.panelRef()?.nativeElement.querySelectorAll<HTMLElement>('[id]') ?? []);\n\n rows.find((row: HTMLElement): boolean => row.id === String(item.id))?.click();\n }\n}\n","<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- Узкий экран: один столбец — главные пункты или подменю выбранного раздела с кнопкой назад. -->\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 <!-- Подложка незакреплённой панели: подменю, взятое полем поиска, закрывает нажатие снаружи. -->\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 (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\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\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- Ручка есть только у закреплённого подменю: незакреплённое уходит раньше, чем до края доедет рука. -->\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 iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\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 <!-- Блок избранного раздела: сам решает, есть ли ему что показать, и без строк места не занимает. -->\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 <!-- Без подписей под значками имя пункта — в подсказке справа; выключенные подсказки гасят и её. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\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 rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\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 <!-- Имя Material первого кита рисуется своей парой; имя, которого кит не рисует, — своим шаблоном меню. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\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 && railTitlesShown()) {\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 <!-- Стрелка раздела, как у первого кита: нажатие уводит в подменю, а не раскрывает на месте. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n"],"mappings":"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"}
1
+ {"version":3,"file":"rt-tools-ui-kit-v2-side-menu.mjs","names":["BEM_BLOCK","BEM_BLOCK"],"sources":["../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-favorites.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-settings.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-settings.service.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.tokens.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-href.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-icon.logic.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-icon.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-title-parts.pipe.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/sub-item/rt-side-menu-sub-item.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/sub-item/rt-side-menu-sub-item.component.html","../../../projects/ui-kit-v2/src/lib/components/side-menu/favorites/rt-side-menu-favorites.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/favorites/rt-side-menu-favorites.component.html","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-close-timer.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-keyboard.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-resize.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu-look.base.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.directives.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.component.ts","../../../projects/ui-kit-v2/src/lib/components/side-menu/rt-side-menu.component.html"],"sourcesContent":["import { IRtSideMenu } from './rt-side-menu.model';\n\n/** Только строки и числа, без повторов, в прежнем порядке. */\nexport function normalizeSideMenuFavorites(values: ReadonlyArray<unknown>): IRtSideMenu.FavoriteId[] {\n return values.reduce((kept: IRtSideMenu.FavoriteId[], value: unknown): IRtSideMenu.FavoriteId[] => {\n if ((typeof value === 'string' || typeof value === 'number') && !kept.includes(value)) {\n kept.push(value);\n }\n\n return kept;\n }, []);\n}\n\n/**\n * Перенос записи списка с места на место.\n *\n * Номер места вне списка у источника ничего не меняет — переносить нечего; у цели прижимается к\n * краю: рука, бросившая строку ниже последней, хотела поставить её последней.\n */\nexport function moveSideMenuFavorite(ids: ReadonlyArray<IRtSideMenu.FavoriteId>, from: number, to: number): IRtSideMenu.FavoriteId[] {\n const next: IRtSideMenu.FavoriteId[] = [...ids];\n\n if (from < 0 || from >= next.length) {\n return next;\n }\n\n const moved: IRtSideMenu.FavoriteId = next.splice(from, 1)[0];\n const target: number = Math.min(Math.max(to, 0), next.length);\n next.splice(target, 0, moved);\n\n return next;\n}\n\n/**\n * Перенос строки блока — места в блоке, а не в списке.\n *\n * Блок показывает только те номера, пункты которых в меню есть, поэтому место в блоке — не место в\n * списке. Видимые номера переставляются между собой и встают в те же ячейки списка, которые\n * занимали; скрытые остаются на своих местах: их пункт вернётся вместе с правом, и человек найдёт\n * его там, куда ставил. Видимый номер, которого в списке нет, пропускается: список могла поменять\n * другая вкладка, а место номера, которого нет, занять нечем.\n */\nexport function moveVisibleSideMenuFavorite(\n ids: ReadonlyArray<IRtSideMenu.FavoriteId>,\n visible: ReadonlyArray<IRtSideMenu.FavoriteId>,\n from: number,\n to: number\n): IRtSideMenu.FavoriteId[] {\n const listed: Set<IRtSideMenu.FavoriteId> = new Set(ids);\n const reordered: IRtSideMenu.FavoriteId[] = moveSideMenuFavorite(normalizeSideMenuFavorites(visible), from, to).filter(\n (id: IRtSideMenu.FavoriteId): boolean => listed.has(id)\n );\n const slots: Set<IRtSideMenu.FavoriteId> = new Set(reordered);\n let next: number = 0;\n\n return ids.map((id: IRtSideMenu.FavoriteId): IRtSideMenu.FavoriteId => {\n if (!slots.has(id)) {\n return id;\n }\n\n const placed: IRtSideMenu.FavoriteId = reordered[next];\n next += 1;\n\n return placed;\n });\n}\n\n/**\n * Может ли пункт стоять в избранном: ссылка есть — может; папка без ссылки никуда не ведёт, а\n * пункт с флагом `favoriteDisabled` приложение исключило само.\n */\nexport function isSideMenuFavoriteCandidate(item: IRtSideMenu.Item): boolean {\n return Boolean(item.link?.trim()) && !item.favoriteDisabled;\n}\n\nfunction collectLinkedItems(items: ReadonlyArray<IRtSideMenu.Item>, byId: Map<IRtSideMenu.FavoriteId, IRtSideMenu.Item>): void {\n for (const item of items) {\n if (isSideMenuFavoriteCandidate(item) && !byId.has(item.id)) {\n byId.set(item.id, item);\n }\n\n collectLinkedItems(item.submenu ?? [], byId);\n }\n}\n\n/**\n * Пункты меню, стоящие за номерами списка, в порядке списка.\n *\n * Пункт ищется в подменю пунктов полосы, вглубь папок, и берётся первый со ссылкой: избранное,\n * которое никуда не ведёт, — сломанное избранное. Номер без пункта пропускается, но из списка не\n * уходит — это решает сервис, а не меню.\n */\nexport function findSideMenuFavoriteItems(\n menu: ReadonlyArray<IRtSideMenu.Item>,\n ids: ReadonlyArray<IRtSideMenu.FavoriteId>\n): IRtSideMenu.Item[] {\n const byId: Map<IRtSideMenu.FavoriteId, IRtSideMenu.Item> = new Map();\n collectLinkedItems(\n menu.flatMap((item: IRtSideMenu.Item): IRtSideMenu.Item[] => item.submenu ?? []),\n byId\n );\n\n return ids.reduce((found: IRtSideMenu.Item[], id: IRtSideMenu.FavoriteId): IRtSideMenu.Item[] => {\n const item: IRtSideMenu.Item | undefined = byId.get(id);\n\n if (item) {\n found.push(item);\n }\n\n return found;\n }, []);\n}\n\n/**\n * Пункт полосы, чьё подменю сейчас показано, если избранное у него включено.\n *\n * Избранное у каждого пункта своё и по умолчанию выключено: включает его потребитель флагом\n * `favorites` пункта полосы. Показанный набор даёт меню — закреплённый или открытый наведением, —\n * и пустой набор не принадлежит никому: подменю без списка не показывает ничьё избранное.\n *\n * Раздел узнаётся сначала по самому набору, затем по номеру его первого пункта: приложение вправе\n * передать меню заново теми же пунктами в новых объектах — при смене языка или прав, — и набор,\n * который подменю ещё держит, перестаёт совпадать с пунктами полосы по ссылке. Номера пунктов\n * подменю единственны на всё меню — это обязанность потребителя.\n */\nexport function sideMenuFavoritesSection(menu: ReadonlyArray<IRtSideMenu.Item>, shown: IRtSideMenu.Item[]): IRtSideMenu.Item | null {\n const first: IRtSideMenu.Item | undefined = shown[0];\n\n if (!first) {\n return null;\n }\n\n const section: IRtSideMenu.Item | undefined =\n menu.find((item: IRtSideMenu.Item): boolean => item.submenu === shown) ??\n menu.find((item: IRtSideMenu.Item): boolean => !!item.submenu?.some((child: IRtSideMenu.Item): boolean => child.id === first.id));\n\n return section?.favorites ? section : null;\n}\n\n/** Место на экране, где строку отпустили, и прямоугольник панели подменю. */\nexport interface IRtSideMenuDropPoint {\n x: number;\n y: number;\n}\n\n/**\n * Строку отпустили за пределами панели. Пока её тянули, уход указателя с панели не закрывал\n * подменю, открытое наведением, — после броска он должен сработать так, будто случился сейчас.\n */\nexport function isSideMenuDropOutside(panel: Pick<DOMRect, 'left' | 'right' | 'top' | 'bottom'>, point: IRtSideMenuDropPoint): boolean {\n return point.x < panel.left || point.x > panel.right || point.y < panel.top || point.y > panel.bottom;\n}\n","import { rtSearchMatches, rtSearchWords } from '@rt-tools/ui-kit-v2/core';\n\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Отбор пунктов подменю по подписи — правилом поиска кита: каждое слово запроса в любом порядке.\n *\n * Отбор спускается внутрь папок на любую глубину: потребитель кладёт в папки то, что человек и ищет\n * по имени, и по одному верхнему уровню такой пункт не нашёлся бы.\n *\n * В выдаче стоят только строки, совпавшие с запросом. Папка остаётся путём к совпавшим детям, а её\n * состав отбирается тем же правилом: иначе одно совпадение по имени папки вытаскивало бы весь её\n * состав. Папка, совпавшая только по имени, остаётся одной строкой без детей.\n *\n * Отобранная папка — новый объект: правка на месте переписала бы набор потребителя, и стёртый\n * запрос вернул бы урезанное меню.\n */\nexport function filterSideMenuItems(items: ReadonlyArray<IRtSideMenu.Item>, query: string): IRtSideMenu.Item[] {\n if (rtSearchWords(query).length === 0) {\n return [...items];\n }\n\n return items.reduce((kept: IRtSideMenu.Item[], item: IRtSideMenu.Item): IRtSideMenu.Item[] => {\n const isFolder: boolean = Boolean(item.submenu?.length);\n const inside: IRtSideMenu.Item[] = isFolder ? filterSideMenuItems(item.submenu ?? [], query) : [];\n\n if (inside.length) {\n kept.push({ ...item, submenu: inside });\n\n return kept;\n }\n\n if (rtSearchMatches(item.name ?? '', query)) {\n kept.push(isFolder ? { ...item, submenu: [] } : item);\n }\n\n return kept;\n }, []);\n}\n\n/**\n * Папки отобранного списка — те, что должны стоять раскрытыми. Возвращаются номера, а не пункты:\n * отобранный список пересобирается на каждую букву запроса, и ссылка на пункт этого не переживает.\n */\nexport function sideMenuIdsToExpand(items: ReadonlyArray<IRtSideMenu.Item>): Array<string | number> {\n return items.reduce((ids: Array<string | number>, item: IRtSideMenu.Item): Array<string | number> => {\n if (item.submenu?.length) {\n ids.push(item.id, ...sideMenuIdsToExpand(item.submenu));\n }\n\n return ids;\n }, []);\n}\n\n/**\n * Пределы ширины подменю в пикселях. Уже нижнего не помещается ни одна подпись пункта, шире\n * верхнего подменю закрывает содержимое страницы.\n */\nexport const RT_SIDE_MENU_WIDTH_MIN: number = 120;\nexport const RT_SIDE_MENU_WIDTH_MAX: number = 480;\n\n/** Шаг ширины с клавиатуры: стрелку держат нажатой, и крупный шаг проскакивал бы подпись целиком. */\nexport const RT_SIDE_MENU_WIDTH_STEP: number = 16;\n\n/** Приведение ширины к пределам. */\nexport function clampSideMenuWidth(width: number): number {\n return Math.min(RT_SIDE_MENU_WIDTH_MAX, Math.max(RT_SIDE_MENU_WIDTH_MIN, width));\n}\n\n/**\n * Пункты подменю в том порядке, в каком они стоят на экране. Внутрь папки список спускается, только\n * если та раскрыта: ходьба стрелками идёт по видимому, а не по всему набору.\n */\nexport function walkSideMenuItems(items: ReadonlyArray<IRtSideMenu.Item>, expandedIds: ReadonlyArray<string | number>): IRtSideMenu.Item[] {\n return items.reduce((walk: IRtSideMenu.Item[], item: IRtSideMenu.Item): IRtSideMenu.Item[] => {\n walk.push(item);\n\n if (item.submenu?.length && expandedIds.includes(item.id)) {\n walk.push(...walkSideMenuItems(item.submenu, expandedIds));\n }\n\n return walk;\n }, []);\n}\n\n/**\n * Куда уходит подсветка на шаг стрелкой. Считается по номеру пункта: видимый список пересобирается\n * на каждую букву запроса. Без подсветки вниз берёт первый пункт, вверх — последний. У краёв ходьба\n * останавливается: заворот уводил бы взгляд через всю панель.\n */\nexport function stepSideMenuHighlight(\n walk: ReadonlyArray<IRtSideMenu.Item>,\n highlightedId: string | number | null,\n step: number\n): string | number | null {\n if (walk.length === 0) {\n return null;\n }\n\n const at: number = walk.findIndex((item: IRtSideMenu.Item): boolean => item.id === highlightedId);\n\n if (at === -1) {\n return step > 0 ? walk[0].id : walk[walk.length - 1].id;\n }\n\n return walk[Math.min(walk.length - 1, Math.max(0, at + step))].id;\n}\n\n/** Чем вешается слушатель. У кита это `Renderer2.listen`, у проверки — своя функция. */\nexport type TRtPointerListen = (target: HTMLElement, event: string, handler: (event: PointerEvent) => void) => () => void;\n\n/** Что тяга сообщает меню: новую ширину по ходу и конец — отпусканием или отнятием указателя. */\nexport interface IRtSubMenuWidthDragHooks {\n readonly onWidth: (width: number) => void;\n readonly onEnd: () => void;\n}\n\n/**\n * Тяга ширины подменю указательными событиями: мышиных палец и перо не дают. Слушатели висят на\n * ручке, а держит их захват указателя — слушатель на документе терял бы движение над чужим кадром.\n * Указатель, отнятый средой, кончает тягу так же, как отпускание.\n *\n * Возвращает снятие слушателей. Пустое значение — тянуть нечем.\n */\nexport function startSideMenuWidthDrag(\n event: PointerEvent,\n startWidth: number,\n listen: TRtPointerListen,\n hooks: IRtSubMenuWidthDragHooks\n): (() => void) | null {\n const handle: EventTarget | null = event.currentTarget;\n\n // Тянет только основная кнопка: отпускание правой над меню среды ручке не приходит.\n if (!(handle instanceof HTMLElement) || event.button !== 0) {\n return null;\n }\n\n const pointerId: number = event.pointerId;\n const startX: number = event.clientX;\n\n // Захвата нет у среды, где идут проверки.\n if (typeof handle.setPointerCapture === 'function') {\n handle.setPointerCapture(pointerId);\n }\n\n const stopMove: () => void = listen(handle, 'pointermove', (moveEvent: PointerEvent): void => {\n if (moveEvent.pointerId === pointerId) {\n hooks.onWidth(clampSideMenuWidth(startWidth + moveEvent.clientX - startX));\n }\n });\n const stopUp: () => void = listen(handle, 'pointerup', (): void => hooks.onEnd());\n const stopCancel: () => void = listen(handle, 'pointercancel', (): void => hooks.onEnd());\n // Захват снимается и без отпускания — ручку убрали из разметки; тогда конец тяги — сама потеря.\n const stopLost: () => void = listen(handle, 'lostpointercapture', (): void => hooks.onEnd());\n\n return (): void => {\n stopMove();\n stopUp();\n stopCancel();\n stopLost();\n\n if (typeof handle.releasePointerCapture === 'function' && handle.hasPointerCapture(pointerId)) {\n handle.releasePointerCapture(pointerId);\n }\n };\n}\n\n/**\n * Ширина, которой панель нарисована. Панели нет или раскладка не посчитана — нижний предел: с нуля\n * тяга уводила бы ширину в отрицательные числа.\n */\nexport function drawnSideMenuWidth(panel: HTMLElement | null): number {\n const width: number = panel?.getBoundingClientRect().width ?? 0;\n\n return width > 0 ? width : RT_SIDE_MENU_WIDTH_MIN;\n}\n\n/**\n * Число, которое уходит наружу по концу тяги: большее из натянутого и нарисованного. Нижний предел\n * держит оформление, и панель не бывает уже его, даже если рука ушла левее.\n */\nexport function reportedSideMenuWidth(dragged: number, panel: HTMLElement | null): number {\n return Math.max(dragged, drawnSideMenuWidth(panel));\n}\n\n/**\n * Новая ширина по нажатой клавише. Пустое значение — клавиша не о ширине, и умолчание не отменяется:\n * иначе ручка съедала бы табуляцию. Влево значит уже при любом направлении письма: ручка стоит у\n * правого края панели.\n */\nexport function sideMenuWidthByKey(key: string, width: number): number | null {\n switch (key) {\n case 'ArrowRight':\n return clampSideMenuWidth(width + RT_SIDE_MENU_WIDTH_STEP);\n case 'ArrowLeft':\n return clampSideMenuWidth(width - RT_SIDE_MENU_WIDTH_STEP);\n case 'Home':\n return RT_SIDE_MENU_WIDTH_MIN;\n case 'End':\n return RT_SIDE_MENU_WIDTH_MAX;\n default:\n return null;\n }\n}\n\n/**\n * Что показывает закреплённое подменю: выбранный человеком раздел, а пока выбора нет — раздел\n * активного адреса. Выбор впереди активности: иначе до соседнего раздела не добраться.\n */\nexport function pinnedSideMenuItems(\n picked: IRtSideMenu.Item[] | null | undefined,\n activeIds: ReadonlyArray<string | number>,\n items: ReadonlyArray<IRtSideMenu.Item>\n): IRtSideMenu.Item[] {\n if (picked?.length) {\n return picked;\n }\n\n return items.find((item: IRtSideMenu.Item): boolean => activeIds.includes(item.id) && !!item.submenu?.length)?.submenu ?? [];\n}\n\n/**\n * Нажатие, которое браузер открывает сам по адресу ссылки: не левой кнопкой или с клавишей —\n * новая вкладка, окно, загрузка. Строка подменю такое нажатие не перехватывает.\n */\nexport function sideMenuPressOpensInBrowser(event: MouseEvent): boolean {\n const withKey: boolean = [event.ctrlKey, event.metaKey, event.shiftKey, event.altKey].some(Boolean);\n\n return event.button !== 0 || withKey;\n}\n","import { isRecord } from '@rt-tools/utils';\n\nimport { normalizeSideMenuFavorites } from './rt-side-menu-favorites.logic';\nimport { clampSideMenuWidth } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Номер меню, которому приложение своего не задало. */\nexport const RT_SIDE_MENU_DEFAULT_ID: string = 'main';\n\n/**\n * Номер меню, под которым лежат его настройки. Пустой — тот же, что не заданный: атрибут без\n * значения даёт пустую строку, и настройки легли бы под ключ, которого никто не называл.\n */\nexport function normalizeSideMenuId(menuId: string | null | undefined): string {\n return menuId?.trim() || RT_SIDE_MENU_DEFAULT_ID;\n}\n\n/** Запись хранилища как есть: номер меню — и что под ним лежит, в том числе сломанное. */\nexport type TRtSideMenuSettingsRecord = Record<string, unknown>;\n\nfunction isSubMenuMode(value: unknown): value is IRtSideMenu.SubMenuMode {\n return value === 'hover' || value === 'pinned';\n}\n\n/**\n * Настройки одного меню из того, что лежит под его номером. Недопустимое поле читается как\n * отсутствие значения, соседние поля остаются: сломанная ширина не стирает режим. Незнакомые поля\n * не удаляются. Ширина приводится к пределам подменю, списки избранного — к номерам без повторов.\n */\nexport function normalizeSideMenuSettings(value: unknown): IRtSideMenu.Settings {\n if (!isRecord(value)) {\n return {};\n }\n\n const settings: IRtSideMenu.Settings = { ...value };\n const width: unknown = value['subMenuWidth'];\n const favorites: unknown = value['favorites'];\n const collapsed: unknown = value['favoritesCollapsed'];\n\n if (!isSubMenuMode(value['subMenuMode'])) {\n delete settings.subMenuMode;\n }\n\n if (typeof width === 'number' && Number.isFinite(width)) {\n settings.subMenuWidth = clampSideMenuWidth(width);\n } else {\n delete settings.subMenuWidth;\n }\n\n // Список — только строки и числа без повторов; не массив читается отсутствием списка.\n if (Array.isArray(favorites)) {\n settings.favorites = normalizeSideMenuFavorites(favorites);\n } else {\n delete settings.favorites;\n }\n\n if (Array.isArray(collapsed)) {\n settings.favoritesCollapsed = normalizeSideMenuFavorites(collapsed);\n } else {\n delete settings.favoritesCollapsed;\n }\n\n return settings;\n}\n\n/** Запись хранилища как объект. Не JSON, не объект, пустое хранилище — одинаково пустая запись. */\nexport function parseSideMenuSettingsRecord(raw: string | null): TRtSideMenuSettingsRecord {\n if (raw === null) {\n return {};\n }\n\n try {\n const value: unknown = JSON.parse(raw);\n\n return isRecord(value) ? value : {};\n } catch {\n return {};\n }\n}\n\n/**\n * Настройки всех меню из записи. Меню, чьё значение не объект, пропускается: сломанный угол одного\n * меню не стирает соседние.\n */\nexport function readSideMenuSettings(record: TRtSideMenuSettingsRecord): Record<string, IRtSideMenu.Settings> {\n return Object.fromEntries(\n Object.entries(record)\n .filter(([, settings]: [string, unknown]): boolean => isRecord(settings))\n .map(([menuId, settings]: [string, unknown]): [string, IRtSideMenu.Settings] => [menuId, normalizeSideMenuSettings(settings)])\n );\n}\n\n/**\n * Запись с правкой полей одного меню. Остальные меню и поля уходят такими, какими лежали, даже\n * сломанные: кит правит только то, о чём его попросили.\n */\nexport function patchSideMenuSettings(\n record: TRtSideMenuSettingsRecord,\n menuId: string,\n patch: Partial<IRtSideMenu.Settings>\n): TRtSideMenuSettingsRecord {\n const current: unknown = record[menuId];\n\n return { ...record, [menuId]: { ...(isRecord(current) ? current : {}), ...patch } };\n}\n\n/** Запись без настроек одного меню. */\nexport function omitSideMenuSettings(record: TRtSideMenuSettingsRecord, menuId: string): TRtSideMenuSettingsRecord {\n return Object.fromEntries(Object.entries(record).filter(([id]: [string, unknown]): boolean => id !== menuId));\n}\n","import {\n computed,\n DestroyRef,\n EnvironmentProviders,\n inject,\n Injectable,\n InjectionToken,\n makeEnvironmentProviders,\n Signal,\n signal,\n WritableSignal,\n} from '@angular/core';\n\nimport { LOCAL_STORAGE, PlatformService, WINDOW } from '@rt-tools/core';\n\nimport { ERtStorageKeys } from '@rt-tools/ui-kit-v2/core';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { moveVisibleSideMenuFavorite, normalizeSideMenuFavorites } from './rt-side-menu-favorites.logic';\nimport {\n normalizeSideMenuId,\n normalizeSideMenuSettings,\n omitSideMenuSettings,\n parseSideMenuSettingsRecord,\n patchSideMenuSettings,\n readSideMenuSettings,\n TRtSideMenuSettingsRecord,\n} from './rt-side-menu-settings.logic';\nimport { clampSideMenuWidth } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Значки кнопок строки избранного: «убрать» и ручка перетаскивания. */\nexport interface IRtSideMenuFavoritesIcons {\n readonly remove: IRtIcon.Name;\n readonly drag: IRtIcon.Name;\n}\n\nexport interface IRtSideMenuSettingsConfig {\n /** Свой ключ хранилища, если приложению нужен не общий ключ кита. */\n readonly storageKey?: string;\n /** Свои значки кнопок избранного; не названный берётся из набора кита. */\n readonly icons?: Partial<IRtSideMenuFavoritesIcons>;\n}\n\n/** Корзина и вертикальная двойная стрелка — те же знаки, что у первого кита. */\nconst DEFAULT_FAVORITES_ICONS: IRtSideMenuFavoritesIcons = { remove: 'trash', drag: 'arrows-v' };\n\nexport const RT_SIDE_MENU_SETTINGS_CONFIG: InjectionToken<IRtSideMenuSettingsConfig> = new InjectionToken<IRtSideMenuSettingsConfig>(\n 'RT_SIDE_MENU_SETTINGS_CONFIG'\n);\n\n/** Сигнал на номер меню один: повторный вызов отдаёт тот же. */\nfunction cached<T>(cache: Map<string, Signal<T>>, menuId: string, read: (menuId: string) => T): Signal<T> {\n const id: string = normalizeSideMenuId(menuId);\n let value: Signal<T> | undefined = cache.get(id);\n\n if (!value) {\n value = computed((): T => read(id));\n cache.set(id, value);\n }\n\n return value;\n}\n\n/**\n * Настройки боковых меню в хранилище браузера: режим и ширина подменю, избранное и свёрнутые блоки\n * избранного под номером каждого меню.\n *\n * Перед каждой записью ключ читается заново: соседняя вкладка или приложение могли записать своё,\n * и запись из копии затёрла бы их. Правится одно поле одного меню, остальное уходит как лежало.\n * Изменения из других вкладок приходят событием `storage` и обновляют сигналы.\n *\n * Хранилище берётся токеном `@rt-tools/core` напрямую, а не общим сервисом хранилища: сервису\n * нужно знать, что запись не удалась, и держать правду в памяти до перезагрузки. Без хранилища, вне\n * браузера, закрытое или полное — настройки живут в памяти, и ни чтение, ни запись не бросают.\n *\n * Сервис ставится провайдером окружения — `provideRtSideMenuSettings()`, — а меню берёт его\n * необязательно: не поставлен — меню ничего не хранит, режим и ширина приходят входами.\n */\n@Injectable()\nexport class RtSideMenuSettingsService {\n readonly #config: IRtSideMenuSettingsConfig = inject(RT_SIDE_MENU_SETTINGS_CONFIG, { optional: true }) ?? {};\n readonly #storage: Storage | null = inject(LOCAL_STORAGE, { optional: true }) ?? null;\n readonly #key: string = this.#config.storageKey?.trim() || ERtStorageKeys.SideMenu;\n readonly #menus: Map<string, Signal<IRtSideMenu.Settings>> = new Map();\n readonly #modes: Map<string, Signal<IRtSideMenu.SubMenuMode>> = new Map();\n readonly #widths: Map<string, Signal<number | null>> = new Map();\n readonly #favorites: Map<string, Signal<ReadonlyArray<IRtSideMenu.FavoriteId>>> = new Map();\n readonly #collapsed: Map<string, Signal<ReadonlyArray<IRtSideMenu.Item['id']>>> = new Map();\n /** Последняя известная запись: ею сервис живёт, пока хранилище недоступно. */\n #record: TRtSideMenuSettingsRecord = this.#load() ?? {};\n /** Последняя запись в хранилище не удалась — оно полно, и правда лежит в памяти. */\n #unsaved: boolean = false;\n readonly #settings: WritableSignal<Readonly<Record<string, IRtSideMenu.Settings>>> = signal(readSideMenuSettings(this.#record));\n\n /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */\n public readonly favoritesIcons: IRtSideMenuFavoritesIcons = { ...DEFAULT_FAVORITES_ICONS, ...this.#config.icons };\n\n /** Номера меню, чьи настройки лежат в хранилище. */\n public readonly menuIds: Signal<string[]> = computed((): string[] => Object.keys(this.#settings()));\n\n constructor() {\n if (!inject(PlatformService).isPlatformBrowser) {\n return;\n }\n\n const view: Window = inject(WINDOW);\n const onStorage: (event: StorageEvent) => void = (event: StorageEvent): void => {\n if (event.key === this.#key || event.key === null) {\n this.#unsaved = false;\n this.#apply(this.#fresh());\n }\n };\n\n view.addEventListener('storage', onStorage);\n inject(DestroyRef).onDestroy((): void => view.removeEventListener('storage', onStorage));\n }\n\n /** Настройки одного меню целиком, с незнакомыми полями; не сохранены — пустой объект. */\n public settings(menuId: string): Signal<IRtSideMenu.Settings> {\n return cached(this.#menus, menuId, (id: string): IRtSideMenu.Settings => this.#settings()[id] ?? {});\n }\n\n public subMenuMode(menuId: string): Signal<IRtSideMenu.SubMenuMode> {\n return cached(this.#modes, menuId, (id: string): IRtSideMenu.SubMenuMode => this.#settings()[id]?.subMenuMode ?? 'hover');\n }\n\n public subMenuWidth(menuId: string): Signal<number | null> {\n return cached(this.#widths, menuId, (id: string): number | null => this.#settings()[id]?.subMenuWidth ?? null);\n }\n\n /** Избранное меню в порядке списка; пусто — пустой список. */\n public favoriteIds(menuId: string): Signal<ReadonlyArray<IRtSideMenu.FavoriteId>> {\n return cached(\n this.#favorites,\n menuId,\n (id: string): ReadonlyArray<IRtSideMenu.FavoriteId> => this.#settings()[id]?.favorites ?? []\n );\n }\n\n /** Пункты полосы, чей блок избранного свёрнут; ничего не свёрнуто — пустой список. */\n public favoritesCollapsed(menuId: string): Signal<ReadonlyArray<IRtSideMenu.Item['id']>> {\n return cached(\n this.#collapsed,\n menuId,\n (id: string): ReadonlyArray<IRtSideMenu.Item['id']> => this.#settings()[id]?.favoritesCollapsed ?? []\n );\n }\n\n /** Сворачивает или разворачивает блок одного раздела; остальные разделы остаются как были. */\n public setFavoritesCollapsed(menuId: string, sectionId: IRtSideMenu.Item['id'], collapsed: boolean): void {\n const current: ReadonlyArray<IRtSideMenu.Item['id']> = this.#freshSettings(menuId).favoritesCollapsed ?? [];\n const others: Array<IRtSideMenu.Item['id']> = current.filter((id: IRtSideMenu.Item['id']): boolean => id !== sectionId);\n\n this.#update(menuId, { favoritesCollapsed: collapsed ? [...others, sectionId] : others });\n }\n\n /** Звезда: нет в списке — встаёт последним, есть — уходит. */\n public toggleFavorite(menuId: string, id: IRtSideMenu.FavoriteId): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n ids.includes(id) ? ids.filter((kept: IRtSideMenu.FavoriteId): boolean => kept !== id) : [...ids, id]\n );\n }\n\n public removeFavorite(menuId: string, id: IRtSideMenu.FavoriteId): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n ids.filter((kept: IRtSideMenu.FavoriteId): boolean => kept !== id)\n );\n }\n\n /** Перенос строки блока: места в блоке, скрытые номера остаются на своих. */\n public moveVisibleSideMenuFavorite(menuId: string, visibleIds: ReadonlyArray<IRtSideMenu.FavoriteId>, from: number, to: number): void {\n this.#updateFavorites(menuId, (ids: IRtSideMenu.FavoriteId[]): IRtSideMenu.FavoriteId[] =>\n moveVisibleSideMenuFavorite(ids, visibleIds, from, to)\n );\n }\n\n /** Список целиком: приложение заводит избранное само, например по умолчанию для роли. */\n public setFavorites(menuId: string, ids: ReadonlyArray<IRtSideMenu.FavoriteId>): void {\n this.#updateFavorites(menuId, (): IRtSideMenu.FavoriteId[] => normalizeSideMenuFavorites(ids));\n }\n\n public setSubMenuMode(menuId: string, mode: IRtSideMenu.SubMenuMode): void {\n this.#update(menuId, { subMenuMode: mode });\n }\n\n public setSubMenuWidth(menuId: string, width: number): void {\n this.#update(menuId, { subMenuWidth: clampSideMenuWidth(width) });\n }\n\n /** Настройки меню уходят только по вызову приложения: кит сам ничего не удаляет. */\n public deleteSettings(menuId: string): void {\n this.#commit(omitSideMenuSettings(this.#fresh(), normalizeSideMenuId(menuId)));\n }\n\n /** Прочитать хранилище → поправить поля одного меню → записать. */\n #update(menuId: string, patch: Partial<IRtSideMenu.Settings>): void {\n const record: TRtSideMenuSettingsRecord = this.#fresh();\n const id: string = normalizeSideMenuId(menuId);\n\n this.#commit(patchSideMenuSettings(record, id, { ...normalizeSideMenuSettings(record[id]), ...patch }));\n }\n\n /** Список правится по свежей записи хранилища: соседняя вкладка могла его поменять. */\n #updateFavorites(menuId: string, next: (ids: IRtSideMenu.FavoriteId[]) => IRtSideMenu.FavoriteId[]): void {\n this.#update(menuId, { favorites: next([...(this.#freshSettings(menuId).favorites ?? [])]) });\n }\n\n #freshSettings(menuId: string): IRtSideMenu.Settings {\n return normalizeSideMenuSettings(this.#fresh()[normalizeSideMenuId(menuId)]);\n }\n\n #commit(record: TRtSideMenuSettingsRecord): void {\n this.#apply(record);\n\n try {\n this.#storage?.setItem(this.#key, JSON.stringify(record));\n this.#unsaved = false;\n } catch {\n // хранилище закрыто или полно — настройки живут в памяти до перезагрузки\n this.#unsaved = true;\n }\n }\n\n #apply(record: TRtSideMenuSettingsRecord): void {\n this.#record = record;\n this.#settings.set(readSideMenuSettings(record));\n }\n\n /** Запись из хранилища, а если оно недоступно или не приняло прошлую запись — последняя известная. */\n #fresh(): TRtSideMenuSettingsRecord {\n return this.#unsaved ? this.#record : (this.#load() ?? this.#record);\n }\n\n /** Пусто — хранилища нет или оно не отвечает. Сломанная запись читается пустой, но читается. */\n #load(): TRtSideMenuSettingsRecord | null {\n if (!this.#storage) {\n return null;\n }\n\n try {\n return parseSideMenuSettingsRecord(this.#storage.getItem(this.#key));\n } catch {\n return null;\n }\n }\n}\n\n/**\n * Сервис настроек бокового меню для приложения.\n *\n * ```ts\n * bootstrapApplication(App, { providers: [provideRtSideMenuSettings()] });\n * ```\n */\nexport function provideRtSideMenuSettings(config: IRtSideMenuSettingsConfig = {}): EnvironmentProviders {\n return makeEnvironmentProviders([{ provide: RT_SIDE_MENU_SETTINGS_CONFIG, useValue: config }, RtSideMenuSettingsService]);\n}\n","import { InjectionToken, Signal, TemplateRef } from '@angular/core';\n\nimport { IRtSideMenuIconContext } from './rt-side-menu.directives';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Меню, в котором стоит подпункт. Токен вместо самого компонента: меню объявляет подпункт в своих\n * импортах, и прямая ссылка замкнула бы круг «меню → подпункт → меню».\n *\n * Активность и раскрытость — два значения: активным остаётся адрес, на котором человек стоит, а\n * раскрытой — всякая папка, в которой нашлось совпадение.\n */\nexport interface IRtSideMenuHost {\n readonly activeMenuIds: Signal<ReadonlyArray<string | number>>;\n readonly expandedMenuIds: Signal<ReadonlyArray<string | number>>;\n readonly highlightedMenuId: Signal<string | number | null>;\n readonly subMenuQuery: Signal<string>;\n /** Номер, под которым меню хранит настройки, и пункты полосы: по ним избранное находит свой раздел. */\n readonly menuId: Signal<string>;\n readonly menuItems: Signal<ReadonlyArray<IRtSideMenu.Item>>;\n /** Набор, который подменю наполняет сейчас, до отбора поиском. */\n readonly shownSubMenu: Signal<IRtSideMenu.Item[]>;\n readonly favoritesCount: Signal<IRtSideMenu.FavoritesCount>;\n /** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */\n readonly isFavoritesSearchShown: Signal<boolean>;\n /** Свой значок пунктов, чьё имя кит не рисует: шаблон `rtSideMenuIcon` меню. */\n readonly ownIconTpl: Signal<TemplateRef<IRtSideMenuIconContext> | undefined>;\n /** Подсказки строк подменю: выключено — их нет ни у подписей, ни у кнопок строк. */\n readonly subMenuTooltipsShown: Signal<boolean>;\n /** Залитые значки строк и папок подменю. */\n readonly subItemIconFill: Signal<boolean>;\n /** Раскрыть или свернуть папку нажатием её заголовка. */\n toggleFolder(item: IRtSideMenu.Item): void;\n /** Уход указателя с панели: подменю, открытое наведением, закрывается. */\n toggleSubMenu(): void;\n /** Строку избранного тянут: подменю, открытое наведением, не закрывается уходом указателя. */\n holdSubMenu(held: boolean): void;\n}\n\nexport const RT_SIDE_MENU: InjectionToken<IRtSideMenuHost> = new InjectionToken<IRtSideMenuHost>('RT_SIDE_MENU');\n","import { inject, Pipe, PipeTransform } from '@angular/core';\nimport { ActivatedRoute, Router, UrlTree } from '@angular/router';\n\n/**\n * Адрес пункта так, как его строит ссылка роутера: относительно маршрута, на котором стоит меню.\n * Им строка подменю и переходит, и подписывает `href` — для средней кнопки и копирования ссылки.\n */\nexport function sideMenuLinkTree(router: Router, route: ActivatedRoute | null, link: string): UrlTree {\n return router.createUrlTree([link], { relativeTo: route ?? undefined });\n}\n\n/** `href` строки подменю. Переход строка делает сама: ссылка роутера не дала бы потребителю его взять. */\n@Pipe({\n name: 'rtSideMenuHref',\n})\nexport class RtSideMenuHrefPipe implements PipeTransform {\n readonly #router: Router = inject(Router);\n readonly #route: ActivatedRoute | null = inject(ActivatedRoute, { optional: true });\n\n public transform(link: string | undefined): string | null {\n return link ? this.#router.serializeUrl(sideMenuLinkTree(this.#router, this.#route, link)) : null;\n }\n}\n","import { isKitIconName, materialPairOf } from '@rt-tools/ui-kit-v2/icon';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/**\n * Значок пункта по его `icon`: имя кита как есть, имя Material первого кита — его парой из перечня\n * кита, как у `rt-menu-item`. `null` — кит такой значок не рисует: пункт берёт свой шаблон меню или\n * стоит без значка.\n */\nexport function sideMenuIconName(icon: string | undefined): IRtIcon.Name | null {\n if (!icon) {\n return null;\n }\n if (isKitIconName(icon)) {\n return icon;\n }\n return materialPairOf(icon);\n}\n\n/**\n * Имена значков меню, о которых оно должно предупредить: ни имени кита, ни пары в перечне. И значок\n * пункта, и значок кнопки строки закрывает свой шаблон меню.\n * Без предупреждения такой значок пуст, и пропуск не заметен. Обходит пункты на любую глубину;\n * каждое имя — один раз.\n */\nexport function unpairedSideMenuIcons(items: ReadonlyArray<IRtSideMenu.Item>, hasOwnIcon: boolean): string[] {\n const found: Set<string> = new Set<string>();\n const walk: (list: ReadonlyArray<IRtSideMenu.Item>) => void = (list: ReadonlyArray<IRtSideMenu.Item>): void => {\n for (const item of list) {\n if (!hasOwnIcon && item.icon && sideMenuIconName(item.icon) === null) {\n found.add(item.icon);\n }\n if (!hasOwnIcon && item.iconButton?.icon && sideMenuIconName(item.iconButton.icon) === null) {\n found.add(item.iconButton.icon);\n }\n walk(item.submenu ?? []);\n }\n };\n walk(items);\n\n return [...found];\n}\n\n/**\n * Предупреждение разработчику приложения об именах из `unpairedSideMenuIcons`: имя без значка кита и\n * без пары рисует пункт без значка, и пропуск без предупреждения не заметен.\n */\nexport function warnUnpairedSideMenuIcons(menuId: string, items: ReadonlyArray<IRtSideMenu.Item>, hasOwnIcon: boolean): void {\n const unpaired: string[] = unpairedSideMenuIcons(items, hasOwnIcon);\n\n if (unpaired.length) {\n const names: string = unpaired.map((name: string): string => `«${name}»`).join(', ');\n // eslint-disable-next-line no-console -- предупреждение разработчику приложения: другого канала у кита нет\n console.warn(\n `rt-side-menu «${menuId}»: значков ${names} нет ни в наборе кита, ни в перечне имён Material. ` +\n 'Задайте имя кита или свой значок через <ng-template rtSideMenuIcon>.'\n );\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { sideMenuIconName } from './rt-side-menu-icon.logic';\n\n/** Значок пункта, который рисует кит: имя кита или пара имени Material; `null` — такого нет. */\n@Pipe({\n name: 'rtSideMenuIcon',\n})\nexport class RtSideMenuIconPipe implements PipeTransform {\n public transform(icon: string | undefined): IRtIcon.Name | null {\n return sideMenuIconName(icon);\n }\n}\n","import { Pipe, PipeTransform } from '@angular/core';\n\nimport { rtMatchParts } from '@rt-tools/ui-kit-v2/core';\n\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Подпись пункта кусками: отмечено ровно то, чем она совпала с запросом поиска. */\n@Pipe({\n name: 'rtSideMenuTitleParts',\n})\nexport class RtSideMenuTitlePartsPipe implements PipeTransform {\n public transform(name: string | undefined, query: string): IRtSideMenu.TitlePart[] {\n return rtMatchParts(name ?? '', query);\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n ErrorHandler,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n output,\n OutputEmitterRef,\n Signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { sideMenuFavoritesSection } from '../rt-side-menu-favorites.logic';\nimport { RtSideMenuSettingsService } from '../rt-side-menu-settings.service';\nimport { RtSideMenuHrefPipe, sideMenuLinkTree } from '../rt-side-menu-href.pipe';\nimport { RtSideMenuIconPipe } from '../rt-side-menu-icon.pipe';\nimport { RtSideMenuTitlePartsPipe } from '../rt-side-menu-title-parts.pipe';\nimport { sideMenuPressOpensInBrowser } from '../rt-side-menu.logic';\nimport { IRtSideMenu } from '../rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from '../rt-side-menu.tokens';\n\nconst BEM_BLOCK: string = 'rt-side-menu-sub-item';\n/** Строка блока избранного и кнопка избранного в ней: фокус уходит на соседнюю до удаления строки. */\nconst FAVORITE_ROW: string = '.rt-side-menu-favorites__row';\nconst FAVORITE_BUTTON: string = '.rt-side-menu-sub-item__favorite button';\n\n/**\n * Строка подменю: пункт со ссылкой или папка со своими пунктами. Раскрытость, подсветку и запрос\n * строка берёт у меню — своего состояния у неё нет.\n *\n * В разделе, где приложение включило избранное, у пункта со ссылкой стоит звезда; в строке блока\n * избранного — кнопка «убрать» и слот `[rtSideMenuSubItemAction]`, куда блок кладёт ручку.\n */\n@Component({\n selector: 'rt-side-menu-sub-item',\n host: { class: BEM_BLOCK },\n templateUrl: './rt-side-menu-sub-item.component.html',\n styleUrls: ['./rt-side-menu-sub-item.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtExpansionPanelComponent,\n RtExpansionPanelContentDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtTooltipDirective,\n RtSideMenuTitlePartsPipe,\n RtSideMenuIconPipe,\n RtSideMenuHrefPipe,\n ],\n})\nexport class RtSideMenuSubItemComponent {\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n readonly #router: Router = inject(Router);\n readonly #route: ActivatedRoute | null = inject(ActivatedRoute, { optional: true });\n readonly #errors: ErrorHandler = inject(ErrorHandler);\n\n protected readonly menuRef: IRtSideMenuHost = inject(RT_SIDE_MENU);\n protected readonly narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n /** Значки кнопок избранного: из настроек провайдера, иначе из набора кита. */\n protected readonly favoritesIcons: RtSideMenuSettingsService['favoritesIcons'] | null = this.#settings?.favoritesIcons ?? null;\n protected readonly addLabel: Signal<string> = rtKitLabel('sideMenuFavoriteAdd');\n protected readonly removeLabel: Signal<string> = rtKitLabel('sideMenuFavoriteRemove');\n /** Избранное включено у раздела, чьё подменю показано, и приложение поставило настройки меню. */\n protected readonly favoritesOn: Signal<boolean> = computed(\n (): boolean => !!this.#settings && sideMenuFavoritesSection(this.menuRef.menuItems(), this.menuRef.shownSubMenu()) !== null\n );\n protected readonly favoriteIds: Signal<ReadonlyArray<IRtSideMenu.FavoriteId>> = computed(\n (): ReadonlyArray<IRtSideMenu.FavoriteId> => this.#settings?.favoriteIds(this.menuRef.menuId())() ?? []\n );\n\n public readonly item: InputSignal<IRtSideMenu.Item> = input.required<IRtSideMenu.Item>();\n /** Строка стоит в блоке избранного: вместо звезды — «убрать», без номера и подсветки клавиатуры. */\n public readonly inFavorites: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n\n /**\n * Нажатие строки уходит потребителю раньше перехода. Потребитель, взявший переход на себя,\n * отменяет умолчание события — тогда строка не переходит. Нажатие с клавишей или не левой\n * кнопкой остаётся браузеру: он открывает адрес строки сам.\n */\n public onClickSubMenu(item: IRtSideMenu.Item, event: MouseEvent): void {\n this.clickSubMenuAction.emit({ item, event });\n\n if (event.defaultPrevented || !item.link || sideMenuPressOpensInBrowser(event)) {\n return;\n }\n event.preventDefault();\n // Отказ перехода уходит обработчику ошибок приложения, как у ссылки роутера.\n this.#router\n .navigateByUrl(sideMenuLinkTree(this.#router, this.#route, item.link))\n .catch((error: unknown): void => this.#errors.handleError(error));\n }\n\n /** Кнопка потребителя в строке: ни перехода по ссылке строки, ни закрытия подменю. */\n public onClickSubMenuAdditional(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.clickSubMenuAdditionalAction.emit({ data: item.iconButton?.data, event });\n }\n\n /** Звезда переключает избранное и больше ничего: ни перехода, ни закрытия подменю. */\n public onToggleFavorite(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n this.#settings?.toggleFavorite(this.menuRef.menuId(), item.id);\n }\n\n /**\n * «Убрать» в строке блока. Фокус уходит на такую же кнопку соседней строки до того, как строка\n * исчезнет: иначе он падал бы на страницу, и человек с клавиатуры терял бы место.\n */\n public onRemoveFavorite(item: IRtSideMenu.Item, event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n\n const row: Element | null = event.target instanceof Element ? event.target.closest(FAVORITE_ROW) : null;\n const neighbour: Element | null | undefined = row?.nextElementSibling ?? row?.previousElementSibling;\n\n neighbour?.querySelector<HTMLElement>(FAVORITE_BUTTON)?.focus();\n this.#settings?.removeFavorite(this.menuRef.menuId(), item.id);\n }\n\n public onToggleFolder(item: IRtSideMenu.Item): void {\n this.menuRef.toggleFolder(item);\n }\n}\n","<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 Номер пункта на самой строке — признак для правил приложения: по нему оно находит строку,\n например ту, что открывает страницу раздела. Стоит и у строки блока избранного.\n Строка блока избранного без номера: по номеру клавиатура нажимает строку списка. И без\n родного перетаскивания ссылки: нажатие на ручку внутри строки браузер забирал себе,\n и перетаскивание CDK не начиналось.\n -->\n <a\n rtElem=\"row\"\n qa-dataid=\"side-menu-sub-item\"\n [id]=\"inFavorites() ? null : row.id\"\n [attr.data-item-id]=\"row.id\"\n [attr.draggable]=\"inFavorites() ? 'false' : null\"\n [attr.href]=\"row.link | rtSideMenuHref\"\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 <!-- Кусок без отметки тоже узел: голая интерполяция разрывала бы подпись пробелами. -->\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 Строка блока несёт «убрать», а не звезду: всё в блоке уже в избранном. Звезда —\n у пункта со своим адресом в разделе с избранным: полая — не в избранном,\n закрашенная цветом темы — в нём. Имя кнопки называет следующее нажатие.\n -->\n @if (inFavorites() && favoritesIcons) {\n <rt-icon-button\n rtElem=\"favorite\"\n qa-dataid=\"side-menu-favorite-remove\"\n size=\"sm\"\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 [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 <!-- Действия, которые строке передаёт владелец: блок избранного кладёт сюда ручку. -->\n <ng-content select=\"[rtSideMenuSubItemAction]\" />\n\n <!-- Кнопка потребителя видна всегда и стоит последней: кнопки избранного встают перед ней. -->\n @if (row.iconButton?.icon; as buttonIcon) {\n <!-- Имя без значка кита берёт свой шаблон меню, как значок строки. Без шаблона кнопка пуста и\n незнакомого имени значку не передаёт: значка с таким именем нет, и ждать его некому. -->\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 [icon]=\"buttonKitIcon\"\n [iconFill]=\"menuRef.subItemIconFill()\"\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 Папка — раскрывающаяся панель кита. Рисуется и с пустым составом: отбор оставляет так\n ту, что совпала подписью. Шеврона у такой папки нет — раскрывать нечего. Раскрытие\n держит меню: панель сообщает о нажатии, меню переключает папку. Кнопке заголовка\n отдан номер пункта — по нему клавиатура меню нажимает строку.\n -->\n @let isOpen = menuRef.expandedMenuIds().includes(row.id);\n <rt-expansion-panel\n rtElem=\"folder\"\n appearance=\"plain\"\n [headerId]=\"'' + row.id\"\n [attr.data-item-id]=\"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 <!-- Значок строки: имя кита или пара имени Material, а имя, которого кит не рисует, — свой шаблон меню. -->\n <ng-template #iconTpl let-row>\n @let kitIcon = row.icon | rtSideMenuIcon;\n @if (kitIcon) {\n <rt-icon rtElem=\"icon\" size=\"sm\" [name]=\"kitIcon\" [fill]=\"menuRef.subItemIconFill()\" />\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","import { CdkDrag, CdkDragDrop, CdkDragEnd, CdkDragHandle, CdkDropList } from '@angular/cdk/drag-drop';\nimport {\n afterNextRender,\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n ElementRef,\n inject,\n Injector,\n linkedSignal,\n output,\n OutputEmitterRef,\n Signal,\n signal,\n viewChildren,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtExpansionPanelComponent, RtExpansionPanelContentDirective } from '@rt-tools/ui-kit-v2/expansion-panel';\nimport { IRtIcon } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { sideMenuFavoritesSection, findSideMenuFavoriteItems, isSideMenuDropOutside } from '../rt-side-menu-favorites.logic';\nimport { RtSideMenuSettingsService } from '../rt-side-menu-settings.service';\nimport { filterSideMenuItems } from '../rt-side-menu.logic';\nimport { IRtSideMenu } from '../rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from '../rt-side-menu.tokens';\nimport { RtSideMenuSubItemComponent } from '../sub-item/rt-side-menu-sub-item.component';\n\nconst BEM_BLOCK: string = 'rt-side-menu-favorites';\n/** Панель подменю: уход указателя с неё закрывает подменю, открытое наведением. */\nconst SUB_MENU_PANEL: string = '.rt-side-menu__panel';\n\n/**\n * Блок избранного вверху подменю: выбранные человеком разделы в порядке его списка. Перенос блока\n * первого кита на части второго, без Material: блок — раскрывающаяся панель кита, как папка\n * подменю, строки тянутся за ручку перетаскиванием CDK.\n *\n * Строки собираются из пунктов самого меню, а не из хранилища: подпись следует языку пунктов, адрес\n * — объявлению. Номер без пункта в меню не показывается, но из списка не уходит. Строку рисует тот же\n * подпункт, что и в списке.\n *\n * Избранное у каждого пункта полосы своё и по умолчанию выключено: блок стоит только в подменю пункта\n * с флагом `favorites`. Без настроек меню (`provideRtSideMenuSettings()`) блока нет вовсе. Показать\n * нечего — блок места не занимает.\n */\n@Component({\n selector: 'rt-side-menu-favorites',\n host: { class: BEM_BLOCK },\n templateUrl: './rt-side-menu-favorites.component.html',\n styleUrls: ['./rt-side-menu-favorites.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n CdkDrag,\n CdkDragHandle,\n CdkDropList,\n\n // directives\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtExpansionPanelContentDirective,\n\n // components\n RtExpansionPanelComponent,\n RtIconButtonComponent,\n RtIconComponent,\n RtSideMenuSubItemComponent,\n ],\n})\nexport class RtSideMenuFavoritesComponent {\n readonly #menu: IRtSideMenuHost = inject(RT_SIDE_MENU);\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n readonly #injector: Injector = inject(Injector);\n readonly #host: ElementRef<HTMLElement> = inject(ElementRef);\n /** Строку тянут: подменю держится открытым, пока её не бросят. */\n readonly #dragging: WritableSignal<boolean> = signal(false);\n readonly #section: Signal<IRtSideMenu.Item | null> = computed((): IRtSideMenu.Item | null =>\n this.#settings ? sideMenuFavoritesSection(this.#menu.menuItems(), this.#menu.shownSubMenu()) : null\n );\n\n /** В поиске что-то набрано: блок показывает совпавшие строки раскрытым, сохранённое не трогая. */\n protected readonly searching: Signal<boolean> = computed((): boolean => this.#menu.subMenuQuery().trim() !== '');\n protected readonly rows: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] => {\n const section: IRtSideMenu.Item | null = this.#section();\n\n if (!this.#settings || !section || (this.searching() && !this.#menu.isFavoritesSearchShown())) {\n return [];\n }\n\n const found: IRtSideMenu.Item[] = findSideMenuFavoriteItems([section], this.#settings.favoriteIds(this.#menu.menuId())());\n\n return filterSideMenuItems(found, this.#menu.subMenuQuery());\n });\n /**\n * Блок раздела свёрнут: заголовок и черта остаются, строк нет. Состояние своё у каждого раздела и\n * лежит в настройках меню. Поиск его не меняет: на время поиска блок стоит раскрытым.\n */\n protected readonly collapsed: Signal<boolean> = computed((): boolean => {\n const section: IRtSideMenu.Item | null = this.#section();\n\n return !!section && !!this.#settings?.favoritesCollapsed(this.#menu.menuId())().includes(section.id);\n });\n /**\n * Раскрытие панели блока. Начинается заново с сохранённого выбора при каждой его смене и при\n * входе в поиск и выходе из него: свёрнутый в поиске блок — выбор на время поиска, он не\n * сохраняется и с концом поиска возвращается к сохранённому.\n */\n protected readonly open: WritableSignal<boolean> = linkedSignal({\n source: (): { collapsed: boolean; searching: boolean } => ({ collapsed: this.collapsed(), searching: this.searching() }),\n computation: (state: { collapsed: boolean; searching: boolean }): boolean => !state.collapsed || state.searching,\n });\n /** Заголовок показывает число строк: всегда, у свёрнутого блока или никогда — по входу меню. */\n protected readonly countShown: Signal<boolean> = computed((): boolean => {\n const mode: IRtSideMenu.FavoritesCount = this.#menu.favoritesCount();\n\n return mode === 'always' || (mode === 'collapsed' && !this.open());\n });\n /** Ручки строк по порядку: стрелка возвращает фокус на ручку переставленной строки. */\n protected readonly handles: Signal<ReadonlyArray<ElementRef<HTMLElement>>> = viewChildren<string, ElementRef<HTMLElement>>('handle', {\n read: ElementRef,\n });\n protected readonly dragIcon: IRtIcon.Name = this.#settings?.favoritesIcons.drag ?? 'arrows-v';\n /** Узкий экран: подсказка у ручки не показывается — наводиться там нечем. */\n protected readonly narrow: Signal<boolean> = inject(BreakpointsService).narrow;\n protected readonly titleLabel: Signal<string> = rtKitLabel('sideMenuFavorites');\n protected readonly dragLabel: Signal<string> = rtKitLabel('sideMenuFavoriteDrag');\n protected readonly expandLabel: Signal<string> = rtKitLabel('sideMenuFavoritesExpand');\n protected readonly collapseLabel: Signal<string> = rtKitLabel('sideMenuFavoritesCollapse');\n\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n\n constructor() {\n // Блок уничтожили посреди тяги — указатель увёл подменю на другой раздел. CDK закрывает тягу\n // без события конца, и удержание осталось бы навсегда.\n inject(DestroyRef).onDestroy((): void => this.#release());\n }\n\n /** Заголовок нажат: выбор ложится в настройки меню. Во время поиска он не сохраняется. */\n public onToggle(expanded: boolean): void {\n const section: IRtSideMenu.Item | null = this.#section();\n\n if (this.#settings && section && !this.searching()) {\n this.#settings.setFavoritesCollapsed(this.#menu.menuId(), section.id, !expanded);\n }\n }\n\n /** Строка в руке: подменю, открытое наведением, не закрывается, когда рука выходит за панель. */\n public onDragStart(): void {\n this.#dragging.set(true);\n this.#menu.holdSubMenu(true);\n }\n\n /** Строка брошена за панелью: уход указателя, пропущенный за время тяги, срабатывает сейчас. */\n public onDragEnd(event: CdkDragEnd): void {\n this.#release();\n\n const panel: Element | null = this.#host.nativeElement.closest(SUB_MENU_PANEL);\n\n if (panel && isSideMenuDropOutside(panel.getBoundingClientRect(), event.dropPoint)) {\n this.#menu.toggleSubMenu();\n }\n }\n\n /** Брошенная вне блока строка ничего не меняет: убирает из избранного кнопка, а не перетаскивание. */\n public onDrop(event: CdkDragDrop<IRtSideMenu.Item[]>): void {\n if (event.isPointerOverContainer) {\n this.#move(event.previousIndex, event.currentIndex);\n }\n }\n\n /** Стрелка на ручке переставляет строку на соседнее место, и фокус едет вместе с ручкой. */\n public onHandleKey(event: Event, index: number, step: number): void {\n // Стрелки не уходят в меню: там они ведут подсветку по списку раздела.\n event.preventDefault();\n event.stopPropagation();\n\n const target: number = index + step;\n\n if (target < 0 || target >= this.rows().length) {\n return;\n }\n\n this.#move(index, target);\n afterNextRender((): void => this.handles()[target]?.nativeElement.querySelector<HTMLElement>('button')?.focus(), {\n injector: this.#injector,\n });\n }\n\n /** Нажатие ручки — не переход по строке: она стоит внутри ссылки пункта. */\n public onHandleClick(event: MouseEvent): void {\n event.preventDefault();\n event.stopPropagation();\n }\n\n #release(): void {\n if (!this.#dragging()) {\n return;\n }\n\n this.#dragging.set(false);\n this.#menu.holdSubMenu(false);\n }\n\n /** Место в блоке переводится в место списка по свежей записи хранилища: скрытые номера остаются на своих. */\n #move(from: number, to: number): void {\n if (!this.#settings || from === to) {\n return;\n }\n\n const visible: IRtSideMenu.FavoriteId[] = this.rows().map((item: IRtSideMenu.Item): IRtSideMenu.FavoriteId => item.id);\n this.#settings.moveVisibleSideMenuFavorite(this.#menu.menuId(), visible, from, to);\n }\n}\n","<ng-container rtBlock=\"rt-side-menu-favorites\">\n @if (rows().length) {\n <!--\n Блок — раскрывающаяся панель кита, как папка подменю, со звездой вместо значка папки. Имя\n кнопки заголовка называет следующее нажатие; число строк — по входу меню.\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 Ручка стоит внутри строки, последней перед кнопкой потребителя. Строку тянут\n только за ручку: остальная строка — ссылка. С клавиатуры строку переставляют\n стрелки на ручке: перетаскивание CDK клавиш не знает.\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 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","import { DestroyRef } from '@angular/core';\n\n/**\n * Отложенное закрытие подменю, открытого наведением. Уход указателя ставит закрытие, возврат на\n * панель или наведение на пункт с подменю его снимают. Нулевая задержка закрывает сразу, как\n * раньше: без входа задержки меню ведёт себя прежним образом.\n */\nexport class RtSideMenuCloseTimer {\n #handle: ReturnType<typeof setTimeout> | null = null;\n\n constructor(destroyRef: DestroyRef) {\n destroyRef.onDestroy((): void => this.cancel());\n }\n\n /** Закрыть через `delay` миллисекунд; поставленное раньше закрытие заменяется новым. */\n public run(delay: number, close: () => void): void {\n this.cancel();\n if (delay <= 0) {\n close();\n\n return;\n }\n this.#handle = setTimeout((): void => {\n this.#handle = null;\n close();\n }, delay);\n }\n\n public cancel(): void {\n if (this.#handle !== null) {\n clearTimeout(this.#handle);\n this.#handle = null;\n }\n }\n}\n","import { signal, Signal, WritableSignal } from '@angular/core';\n\nimport { stepSideMenuHighlight, walkSideMenuItems } from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Что клавиатура просит у меню: открыть пункт и стереть запрос. Сама она ни того, ни другого не умеет. */\nexport interface IRtSubMenuKeyboardHooks {\n open: (item: IRtSideMenu.Item) => void;\n clearQuery: () => void;\n}\n\n/**\n * Ходьба по подменю с клавиатуры. Проверяется вызовом, без подъёма компонента.\n *\n * Клавиши слушает поле поиска: фокус из поля не уходит, и между стрелками работает набор текста.\n * Съедаются только клавиши ходьбы — на остальные ответ отрицательный, иначе поле перестанет\n * принимать буквы. Подсветка держится по номеру пункта: видимый список пересобирается на каждую\n * букву запроса.\n */\nexport class RtSubMenuKeyboard {\n readonly #highlightedId: WritableSignal<string | number | null> = signal(null);\n /**\n * Папки, раскрытые и свёрнутые стрелками. Два списка: раскрытость приходит ещё и от поиска и от\n * активного адреса, и свернуть такую папку можно, только помня об этом отдельно.\n */\n readonly #openedIds: WritableSignal<Array<string | number>> = signal([]);\n readonly #closedIds: WritableSignal<Array<string | number>> = signal([]);\n readonly #hooks: IRtSubMenuKeyboardHooks;\n\n public readonly highlightedId: Signal<string | number | null> = this.#highlightedId.asReadonly();\n public readonly openedIds: Signal<Array<string | number>> = this.#openedIds.asReadonly();\n public readonly closedIds: Signal<Array<string | number>> = this.#closedIds.asReadonly();\n\n constructor(hooks: IRtSubMenuKeyboardHooks) {\n this.#hooks = hooks;\n }\n\n /** Всё, что клавиатура успела наделать, снимается: набран новый запрос или подменю закрылось. */\n public reset(): void {\n this.#highlightedId.set(null);\n this.#openedIds.set([]);\n this.#closedIds.set([]);\n }\n\n /** Нажата клавиша в поле поиска. Отвечает, съедена ли она: только съеденная отменяет умолчание. */\n public press(key: string, items: ReadonlyArray<IRtSideMenu.Item>, expandedIds: ReadonlyArray<string | number>): boolean {\n const walk: IRtSideMenu.Item[] = walkSideMenuItems(items, expandedIds);\n const current: IRtSideMenu.Item | null = walk.find((item: IRtSideMenu.Item): boolean => item.id === this.#highlightedId()) ?? null;\n\n switch (key) {\n case 'ArrowDown':\n case 'ArrowUp':\n this.#highlightedId.set(stepSideMenuHighlight(walk, this.#highlightedId(), key === 'ArrowDown' ? 1 : -1));\n\n return true;\n case 'ArrowRight':\n return this.setFolderOpen(current, true);\n case 'ArrowLeft':\n return this.setFolderOpen(current, false);\n case 'Enter':\n return this.#enter(current);\n case 'Escape':\n this.reset();\n this.#hooks.clearQuery();\n\n return true;\n default:\n return false;\n }\n }\n\n /** Enter открывает подсвеченное: у пункта со ссылкой это переход, у папки — раскрытие. */\n #enter(item: IRtSideMenu.Item | null): boolean {\n if (item === null) {\n return false;\n }\n\n if (item.link) {\n this.#hooks.open(item);\n\n return true;\n }\n\n return this.setFolderOpen(item, true);\n }\n\n /** Раскрыть или свернуть папку: стрелкой, Enter или нажатием её заголовка. */\n public setFolderOpen(item: IRtSideMenu.Item | null, open: boolean): boolean {\n if (item === null || !item.submenu?.length) {\n return false;\n }\n\n const without: (ids: Array<string | number>) => Array<string | number> = (ids: Array<string | number>): Array<string | number> =>\n ids.filter((id: string | number): boolean => id !== item.id);\n\n this.#openedIds.update((ids: Array<string | number>): Array<string | number> => (open ? [...without(ids), item.id] : without(ids)));\n this.#closedIds.update((ids: Array<string | number>): Array<string | number> => (open ? without(ids) : [...without(ids), item.id]));\n\n return true;\n }\n}\n","import { computed, DestroyRef, signal, Signal, WritableSignal } from '@angular/core';\n\nimport {\n clampSideMenuWidth,\n reportedSideMenuWidth,\n sideMenuWidthByKey,\n startSideMenuWidthDrag,\n TRtPointerListen,\n} from './rt-side-menu.logic';\n\n/** Что тяга просит у меню. Сама она ни о ширине не помнит, ни наружу говорить не умеет. */\nexport interface IRtSideMenuResizeHooks {\n /** Ширина, о которой меню просит по концу тяги или по нажатой клавише. */\n askWidth: (width: number) => void;\n /** Начало и конец тяги для потребителя: по ним он накрывает чужие кадры и снимает накрытие. */\n started: () => void;\n ended: () => void;\n /** Панель подменю — по ней замеряется нарисованная ширина. Пустая, пока панели нет. */\n panel: () => HTMLElement | null;\n /** Ширина, названная потребителем или настройками. Пустая — ширину ставит оформление. */\n namedWidth: () => number | null;\n}\n\n/**\n * Тяга ширины закреплённого подменю: натянутая ширина и снятие слушателей. Механика событий —\n * `startSideMenuWidthDrag` в логике рядом, здесь держится только то, что живёт дольше одного события.\n */\nexport class RtSideMenuResize {\n /** Натянутая ширина. Пустая — тяги нет, и ширину ставит потребитель или оформление. */\n readonly #draggedWidth: WritableSignal<number | null> = signal(null);\n readonly #listen: TRtPointerListen;\n readonly #hooks: IRtSideMenuResizeHooks;\n\n #stop: (() => void) | null = null;\n\n public readonly draggedWidth: Signal<number | null> = this.#draggedWidth.asReadonly();\n\n /**\n * Ширина для диктора: только то, что кит знает сам. Не названо ничего и ничего не тянули — числа\n * нет: выдуманное назвало бы ширину, которой панель не нарисована.\n */\n public readonly valueNow: Signal<number | null> = computed((): number | null => {\n const width: number | null = this.#draggedWidth() ?? this.#hooks.namedWidth();\n\n return width === null ? null : clampSideMenuWidth(width);\n });\n\n /**\n * Меню, разрушенное посреди тяги, кончает её здесь же: иначе накрытие чужих кадров, снимаемое по\n * концу, осталось бы стоять.\n */\n constructor(listen: TRtPointerListen, hooks: IRtSideMenuResizeHooks, destroyRef?: DestroyRef) {\n this.#listen = listen;\n this.#hooks = hooks;\n destroyRef?.onDestroy((): void => this.finish());\n }\n\n /** Тяга идёт. Второе нажатие при начатой тяге ничего не начинает: указатель уже захвачен. */\n public isRunning(): boolean {\n return this.#stop !== null;\n }\n\n /**\n * Нажата клавиша на ручке. Просьба о ширине уходит сразу: жеста нет, и держать её не до чего.\n * Отрицательный ответ — клавиша не о ширине, и умолчание у неё не отменяется.\n */\n public pressKey(key: string, from: number): boolean {\n const width: number | null = sideMenuWidthByKey(key, from);\n\n if (width === null) {\n return false;\n }\n\n this.#hooks.askWidth(width);\n\n return true;\n }\n\n public start(event: PointerEvent, startWidth: number): void {\n if (this.isRunning()) {\n return;\n }\n\n const stop: (() => void) | null = startSideMenuWidthDrag(event, startWidth, this.#listen, {\n onWidth: (width: number): void => this.#draggedWidth.set(width),\n onEnd: (): void => this.finish(),\n });\n\n if (stop !== null) {\n this.#stop = stop;\n this.#hooks.started();\n }\n }\n\n /** Конец тяги. Наружу уходит то число, которым панель нарисована: нижний предел держит оформление. */\n public finish(): void {\n if (!this.isRunning()) {\n return;\n }\n\n // Замер идёт до сброса натянутой ширины: сбросив её, панель перерисуют, и мерить будет нечего.\n const width: number | null = this.#draggedWidth();\n const drawn: number | null = width === null ? null : reportedSideMenuWidth(width, this.#hooks.panel());\n\n this.#stop?.();\n this.#stop = null;\n this.#draggedWidth.set(null);\n\n if (drawn !== null) {\n this.#hooks.askWidth(drawn);\n }\n\n this.#hooks.ended();\n }\n}\n","import { booleanAttribute, Directive, input, InputSignal, InputSignalWithTransform } from '@angular/core';\n\nimport { IRtInput } from '@rt-tools/ui-kit-v2/core';\n\n/**\n * Входы вида бокового меню: подписи и заливка значков, поле поиска, подсказка прокрутки панели. Ни\n * один не меняет поведения — по ним приложение даёт меню вид первого кита, а без них меню рисуется\n * как раньше. Вынесены из класса меню, чтобы тот оставался в пределе длины файла.\n */\n@Directive()\nexport abstract class RtSideMenuLookBase {\n /** Подсказки строк подменю — у подписей и у кнопок строк. Доступные имена остаются. */\n public readonly subMenuTooltipsShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /**\n * Подписи под значками полосы. Выключено — имя пункта уходит в подсказку справа от значка, а\n * подсказка подчиняется `subMenuTooltipsShown`; доступное имя пункт держит всегда.\n */\n public readonly railTitlesShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Залитые значки полосы — залитый рисунок материального набора и заливка шрифта лигатуры. */\n public readonly railIconFill: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Залитые значки строк и папок подменю — так же, как `railIconFill` у полосы. */\n public readonly subItemIconFill: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Подсказка прокрутки внизу панели подменю, как у полосы: длинный список не обрывается молча. */\n public readonly panelScrollHintShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(false, {\n transform: booleanAttribute,\n });\n /** Размер поля поиска подменю — ступень `rt-input`. */\n public readonly searchSize: InputSignal<IRtInput.Size> = input<IRtInput.Size>('sm');\n /** Вид поля поиска подменю — вид `rt-input`. */\n public readonly searchAppearance: InputSignal<IRtInput.Appearance> = input<IRtInput.Appearance>('outline');\n}\n","import { Directive, inject, TemplateRef } from '@angular/core';\n\nimport { IRtSideMenu } from './rt-side-menu.model';\n\n/** Шапка бокового меню: над полосой пунктов. */\n@Directive({\n selector: '[rtSideMenuHeader]',\n})\nexport class RtSideMenuHeaderDirective {}\n\n/** Подвал бокового меню: под полосой пунктов. */\n@Directive({\n selector: '[rtSideMenuFooter]',\n})\nexport class RtSideMenuFooterDirective {}\n\n/**\n * Контекст своего значка: пункт, имя, которое надо нарисовать, и место. Шаблон один на меню и рисует\n * и значок строки, и кнопку строки, поэтому рисунок выбирают по `icon`, а не по `item.icon`: у кнопки\n * это имя её значка.\n */\nexport interface IRtSideMenuIconContext {\n $implicit: IRtSideMenu.Item;\n /** Имя значка этого места: значка строки или кнопки строки. */\n icon: string;\n /** Место: значок строки или кнопка строки. */\n slot: 'icon' | 'iconButton';\n}\n\n/**\n * Помечает `<ng-template>` своего значка пунктов меню — для имён, которых нет ни в наборе кита, ни\n * в перечне имён Material. Шаблон один на меню и рисует и значок строки, и значок кнопки строки; кит\n * ставит его на место значка размером значка и цветом строки. Рисунок выбирают по `icon` контекста.\n * Значок, который кит рисует сам, шаблон не перебивает.\n *\n * ```html\n * <rt-side-menu [menuItems]=\"items\">\n * <ng-template rtSideMenuIcon let-icon=\"icon\"><img [src]=\"'/icons/' + icon + '.svg'\" alt=\"\" /></ng-template>\n * </rt-side-menu>\n * ```\n */\n@Directive({\n selector: 'ng-template[rtSideMenuIcon]',\n})\nexport class RtSideMenuIconDirective {\n public readonly templateRef: TemplateRef<IRtSideMenuIconContext> = inject<TemplateRef<IRtSideMenuIconContext>>(TemplateRef);\n\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- второй довод стража контекста шаблона стоит только в типе-предикате; убрать его нечем, подпись задаёт каркас\n public static ngTemplateContextGuard(_directive: RtSideMenuIconDirective, context: unknown): context is IRtSideMenuIconContext {\n return true;\n }\n}\n","import { NgTemplateOutlet } from '@angular/common';\nimport {\n booleanAttribute,\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n ElementRef,\n effect,\n inject,\n input,\n InputSignal,\n InputSignalWithTransform,\n isDevMode,\n numberAttribute,\n output,\n OutputEmitterRef,\n Renderer2,\n Signal,\n signal,\n TemplateRef,\n viewChild,\n ViewEncapsulation,\n WritableSignal,\n} from '@angular/core';\nimport { FormControl, ReactiveFormsModule } from '@angular/forms';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { RouterLink } from '@angular/router';\n\nimport { BlockDirective, ElemDirective, ModDirective } from '@rt-tools/core';\n\nimport { rtKitLabel } from '@rt-tools/ui-kit-v2/core';\nimport { BreakpointsService } from '@rt-tools/ui-kit-v2/core';\nimport { RtIconComponent } from '@rt-tools/ui-kit-v2/icon';\nimport { RtIconButtonComponent } from '@rt-tools/ui-kit-v2/icon-button';\nimport { RtInputComponent } from '@rt-tools/ui-kit-v2/input';\nimport {\n RtScrollAreaComponent,\n RtScrollAreaContentDirective,\n RtScrollAreaFooterDirective,\n RtScrollAreaHeaderDirective,\n} from '@rt-tools/ui-kit-v2/scroll-area';\nimport { RtTooltipDirective } from '@rt-tools/ui-kit-v2/tooltip';\nimport { RtSideMenuFavoritesComponent } from './favorites/rt-side-menu-favorites.component';\nimport { RtSideMenuCloseTimer } from './rt-side-menu-close-timer';\nimport { RtSubMenuKeyboard } from './rt-side-menu-keyboard';\nimport { RtSideMenuResize } from './rt-side-menu-resize';\nimport { normalizeSideMenuId, RT_SIDE_MENU_DEFAULT_ID } from './rt-side-menu-settings.logic';\nimport { RtSideMenuSettingsService } from './rt-side-menu-settings.service';\nimport { warnUnpairedSideMenuIcons } from './rt-side-menu-icon.logic';\nimport { RtSideMenuIconPipe } from './rt-side-menu-icon.pipe';\nimport { RtSideMenuLookBase } from './rt-side-menu-look.base';\nimport {\n IRtSideMenuIconContext,\n RtSideMenuFooterDirective,\n RtSideMenuHeaderDirective,\n RtSideMenuIconDirective,\n} from './rt-side-menu.directives';\nimport {\n clampSideMenuWidth,\n drawnSideMenuWidth,\n filterSideMenuItems,\n pinnedSideMenuItems,\n RT_SIDE_MENU_WIDTH_MAX,\n RT_SIDE_MENU_WIDTH_MIN,\n sideMenuIdsToExpand,\n} from './rt-side-menu.logic';\nimport { IRtSideMenu } from './rt-side-menu.model';\nimport { IRtSideMenuHost, RT_SIDE_MENU } from './rt-side-menu.tokens';\nimport { RtSideMenuSubItemComponent } from './sub-item/rt-side-menu-sub-item.component';\n\nconst BEM_BLOCK: string = 'rt-side-menu';\n\n/**\n * Боковая навигация: полоса пунктов и подменю раздела рядом. Перенос бокового меню первого кита на\n * части второго; `rt-menu` — другое семейство, выпадающее меню действий.\n */\n@Component({\n selector: 'rt-side-menu',\n host: {\n class: BEM_BLOCK,\n // Раскладка хоста меняется только у закреплённого режима: панель встаёт в поток рядом с полосой.\n '[class.rt-side-menu--pinned]': 'isPinned()',\n // Натянутая ширина подменю приходит своим свойством: панель берёт наибольшее из него и ширины оформления.\n '[style.--rt-side-menu-panel-dragged-width]': 'panelWidthStyle()',\n // Кнопки избранного, ждущие наведения, в покое ширины не занимают.\n '[class.rt-side-menu--favorite-actions-none]': \"favoriteActionsReserve() === 'none'\",\n },\n templateUrl: './rt-side-menu.component.html',\n styleUrls: ['./rt-side-menu.component.scss'],\n providers: [{ provide: RT_SIDE_MENU, useExisting: RtSideMenuComponent }],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n imports: [\n NgTemplateOutlet,\n ReactiveFormsModule,\n RouterLink,\n BlockDirective,\n ElemDirective,\n ModDirective,\n RtIconComponent,\n RtIconButtonComponent,\n RtInputComponent,\n RtScrollAreaComponent,\n RtScrollAreaContentDirective,\n RtScrollAreaFooterDirective,\n RtScrollAreaHeaderDirective,\n RtTooltipDirective,\n RtSideMenuFavoritesComponent,\n RtSideMenuIconPipe,\n RtSideMenuSubItemComponent,\n ],\n})\nexport class RtSideMenuComponent extends RtSideMenuLookBase implements IRtSideMenuHost {\n readonly #breakpoints: BreakpointsService = inject(BreakpointsService);\n readonly #renderer: Renderer2 = inject(Renderer2);\n /** Настройки меню, если приложение их включило: режим и ширина хранятся там под `menuId`. */\n readonly #settings: RtSideMenuSettingsService | null = inject(RtSideMenuSettingsService, { optional: true });\n\n /** Тяга ширины: просьба уходит одна, на отпускании — в настройки меню и наружу. */\n readonly #resize: RtSideMenuResize = new RtSideMenuResize(\n (target: HTMLElement, name: string, handler: (event: PointerEvent) => void): (() => void) =>\n this.#renderer.listen(target, name, handler),\n {\n askWidth: (width: number): void => {\n this.#settings?.setSubMenuWidth(this.menuId(), width);\n this.subMenuWidthChange.emit(width);\n },\n started: (): void => this.subMenuResizeStart.emit(),\n ended: (): void => this.subMenuResizeEnd.emit(),\n panel: (): HTMLElement | null => this.panelRef()?.nativeElement ?? null,\n namedWidth: (): number | null => this.#width(),\n },\n inject(DestroyRef)\n );\n /** Закрытие подменю, отложенное входом `subMenuCloseDelay`. */\n readonly #closeTimer: RtSideMenuCloseTimer = new RtSideMenuCloseTimer(inject(DestroyRef));\n\n /** Ходьба с клавиатуры открывает пункт настоящим нажатием его строки — той же дорогой, что и мышь. */\n readonly #keyboard: RtSubMenuKeyboard = new RtSubMenuKeyboard({\n open: (item: IRtSideMenu.Item): void => this.#pressRow(item),\n clearQuery: (): void => this.searchControl.setValue(''),\n });\n\n /** Подменю открыл указатель. У закреплённого режима открытость считается иначе. */\n readonly #hoverOpened: WritableSignal<boolean> = signal(false);\n /** Человек в поле поиска: подменю держится открытым до нажатия снаружи. */\n readonly #searchHeld: WritableSignal<boolean> = signal(false);\n /** Строку избранного тянут: уход указателя за панель подменю не закрывает. */\n readonly #dragHeld: WritableSignal<boolean> = signal(false);\n /**\n * Режим и ширина в работе: вход приложения, иначе сохранённые под номером меню. Без кнопки\n * закрепления сохранённый режим не читается: открепить меню, закреплённое раньше, было бы нечем.\n */\n readonly #mode: Signal<IRtSideMenu.SubMenuMode> = computed(\n (): IRtSideMenu.SubMenuMode =>\n this.subMenuMode() ?? (this.pinShown() ? this.#settings?.subMenuMode(this.menuId())() : undefined) ?? 'hover'\n );\n readonly #width: Signal<number | null> = computed(\n (): number | null => this.subMenuWidth() ?? this.#settings?.subMenuWidth(this.menuId())() ?? null\n );\n readonly #pinnedSubMenu: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n this.isPinned() ? pinnedSideMenuItems(this.selectedSubMenu(), this.activeMenuIds(), this.menuItems()) : []\n );\n\n protected readonly searchControl: FormControl<string | null> = new FormControl<string | null>('');\n protected readonly panelRef: Signal<ElementRef<HTMLElement> | undefined> = viewChild<ElementRef<HTMLElement>>('panel');\n protected readonly headerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtSideMenuHeaderDirective, { read: TemplateRef });\n protected readonly footerTpl: Signal<TemplateRef<unknown> | undefined> = contentChild(RtSideMenuFooterDirective, { read: TemplateRef });\n\n protected readonly searchLabel: Signal<string> = rtKitLabel('sideMenuSearch');\n protected readonly pinLabel: Signal<string> = rtKitLabel('sideMenuPin');\n protected readonly unpinLabel: Signal<string> = rtKitLabel('sideMenuUnpin');\n protected readonly nothingFoundLabel: Signal<string> = rtKitLabel('sideMenuNothingFound');\n protected readonly resizeLabel: Signal<string> = rtKitLabel('sideMenuResize');\n protected readonly backLabel: Signal<string> = rtKitLabel('sideMenuBack');\n\n /** Экран узкий: замер кита, и другого источника у этого признака нет. */\n protected readonly narrow: Signal<boolean> = this.#breakpoints.narrow;\n protected readonly resizeValueNow: Signal<number | null> = this.#resize.valueNow;\n protected readonly resizeValueMin: number = RT_SIDE_MENU_WIDTH_MIN;\n protected readonly resizeValueMax: number = RT_SIDE_MENU_WIDTH_MAX;\n\n /** Натянутая ширина панели. Своего выбора нет — свойство не ставится, и ширину берёт оформление. */\n protected readonly panelWidthStyle: Signal<string | null> = computed((): string | null => {\n const width: number | null = this.#resize.draggedWidth() ?? this.#width();\n\n return width === null ? null : `${clampSideMenuWidth(width)}px`;\n });\n\n /** Закрепление действует только на широком экране: на узком подменю занимает экран целиком. */\n protected readonly isPinned: Signal<boolean> = computed((): boolean => this.#mode() === 'pinned' && !this.narrow());\n /** Подменю держится набором запроса: уходом указателя не закрывается. */\n protected readonly isSearchHeld: Signal<boolean> = computed((): boolean => this.#searchHeld() && !this.isPinned());\n /** Закреплённое подменю открыто, пока ему есть что показать: указатель на это не влияет. */\n protected readonly subMenuOpened: Signal<boolean> = computed((): boolean =>\n this.isPinned() ? this.#pinnedSubMenu().length > 0 : this.#hoverOpened()\n );\n protected readonly visibleSubMenuItems: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n filterSideMenuItems(this.shownSubMenu(), this.subMenuQuery())\n );\n protected readonly isNothingFound: Signal<boolean> = computed(\n (): boolean => this.subMenuQuery().trim() !== '' && this.visibleSubMenuItems().length === 0\n );\n /** Пункт полосы подсвечен активным: выбранный человеком, а без выбора — пункт текущего адреса. */\n protected readonly activeRailIds: Signal<ReadonlyArray<string | number>> = computed((): ReadonlyArray<string | number> => {\n const selected: IRtSideMenu.Item | null = this.selectedItem();\n\n return selected ? [selected.id] : this.activeMenuIds();\n });\n\n /** Набор, который подменю наполняет сейчас, до отбора поиском. */\n public readonly shownSubMenu: Signal<IRtSideMenu.Item[]> = computed((): IRtSideMenu.Item[] =>\n this.isPinned() ? this.#pinnedSubMenu() : (this.selectedSubMenu() ?? [])\n );\n public readonly selectedItem: WritableSignal<IRtSideMenu.Item | null> = signal(null);\n public readonly selectedSubMenu: WritableSignal<IRtSideMenu.Item[] | null> = signal(null);\n /** Что набрано в поиске: строка берёт запрос отсюда, чтобы отметить совпавшее. */\n public readonly subMenuQuery: WritableSignal<string> = signal('');\n public readonly highlightedMenuId: Signal<string | number | null> = this.#keyboard.highlightedId;\n\n /**\n * Какие папки раскрыты. Пустой запрос оставляет раздел текущего адреса; непустой добавляет\n * папки с совпадением — иначе найденное лежит за закрытым заголовком.\n */\n public readonly expandedMenuIds: Signal<ReadonlyArray<string | number>> = computed((): ReadonlyArray<string | number> => {\n const found: Array<string | number> = this.subMenuQuery().trim() === '' ? [] : sideMenuIdsToExpand(this.visibleSubMenuItems());\n const closed: ReadonlyArray<string | number> = this.#keyboard.closedIds();\n\n return [...this.activeMenuIds(), ...found, ...this.#keyboard.openedIds()].filter(\n (id: string | number): boolean => !closed.includes(id)\n );\n });\n\n public readonly activeMenuIds: InputSignal<ReadonlyArray<string | number>> = input.required<ReadonlyArray<string | number>>();\n public readonly menuItems: InputSignal<ReadonlyArray<IRtSideMenu.Item>> = input<ReadonlyArray<IRtSideMenu.Item>>([]);\n /** Режим подменю от приложения. Задан — важнее сохранённого; нет ни того, ни другого — наведение. */\n public readonly subMenuMode: InputSignal<IRtSideMenu.SubMenuMode | undefined> = input<IRtSideMenu.SubMenuMode | undefined>(undefined);\n /** Ширина закреплённого подменю в пикселях. Пусто — сохранённая, её нет — оформление. */\n public readonly subMenuWidth: InputSignal<number | null | undefined> = input<number | null | undefined>(undefined);\n /** Номер, под которым меню хранит свои настройки: у двух меню приложения — два номера. */\n public readonly menuId: InputSignalWithTransform<string, string | null | undefined> = input<string, string | null | undefined>(\n RT_SIDE_MENU_DEFAULT_ID,\n { transform: normalizeSideMenuId }\n );\n\n /** Число строк в заголовке блока избранного: всегда, у свёрнутого блока или никогда. */\n public readonly favoritesCount: InputSignal<IRtSideMenu.FavoritesCount> = input<IRtSideMenu.FavoritesCount>('collapsed');\n /** Место под кнопки избранного, ждущие наведения: держать всегда или отдавать подписи. */\n public readonly favoriteActionsReserve: InputSignal<IRtSideMenu.FavoriteActionsReserve> =\n input<IRtSideMenu.FavoriteActionsReserve>('none');\n /** Поиск показывает совпавшие строки избранного; выключено — на время поиска блока нет. */\n public readonly isFavoritesSearchShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /** Кнопка закрепления в шапке подменю; выключено — подменю только всплывает, если режим не задан входом. */\n public readonly pinShown: InputSignalWithTransform<boolean, unknown> = input<boolean, unknown>(true, {\n transform: booleanAttribute,\n });\n /**\n * Задержка закрытия подменю, открытого наведением, в миллисекундах: уход указателя закрывает его\n * не сразу, а возврат на панель держит открытым. Ноль — закрытие сразу, как раньше.\n */\n public readonly subMenuCloseDelay: InputSignalWithTransform<number, unknown> = input<number, unknown>(0, {\n transform: (value: unknown): number => numberAttribute(value, 0),\n });\n\n public readonly subMenuModeChange: OutputEmitterRef<IRtSideMenu.SubMenuMode> = output<IRtSideMenu.SubMenuMode>();\n public readonly subMenuWidthChange: OutputEmitterRef<number> = output<number>();\n /** Начало и конец тяги: по ним потребитель накрывает чужие кадры и снимает накрытие. */\n public readonly subMenuResizeStart: OutputEmitterRef<void> = output<void>();\n public readonly subMenuResizeEnd: OutputEmitterRef<void> = output<void>();\n public readonly closeMobileMenuAction: OutputEmitterRef<void> = output<void>();\n public readonly clickSubMenuAction: OutputEmitterRef<{ item: IRtSideMenu.Item; event: MouseEvent }> = output<{\n item: IRtSideMenu.Item;\n event: MouseEvent;\n }>();\n public readonly clickSubMenuAdditionalAction: OutputEmitterRef<{ data: IRtSideMenu.ItemData | undefined; event: MouseEvent }> = output<{\n data: IRtSideMenu.ItemData | undefined;\n event: MouseEvent;\n }>();\n /** Свой значок пунктов, чьё имя кит не рисует; подпункты читают его через токен меню. */\n public readonly ownIconTpl: Signal<TemplateRef<IRtSideMenuIconContext> | undefined> = contentChild(RtSideMenuIconDirective, {\n read: TemplateRef,\n });\n\n constructor() {\n super();\n this.searchControl.valueChanges.pipe(takeUntilDestroyed()).subscribe((query: string | null): void => {\n this.subMenuQuery.set(query ?? '');\n this.onSearchHold();\n // Видимый список пересобрался: подсветка и раскрытое стрелками к нему больше не относятся.\n this.#keyboard.reset();\n });\n\n // Имя без значка кита и без пары рисует пункт без значка, и пропуск без предупреждения не заметен.\n if (isDevMode()) {\n effect((): void => warnUnpairedSideMenuIcons(this.menuId(), this.menuItems(), this.ownIconTpl() !== undefined));\n }\n }\n\n public onClickMenu(item: IRtSideMenu.Item): void {\n if (this.isPinned()) {\n this.#pickPinnedSubMenu(item);\n this.closeMobileMenu();\n\n return;\n }\n\n this.selectedItem.set(item);\n\n if (item.submenu) {\n this.selectedSubMenu.set(item.submenu);\n this.#hoverOpened.set(true);\n } else if (this.selectedSubMenu()) {\n this.closeSubMenu();\n } else {\n // У пункта нет подменю, и открытого подменю тоже нет — закрывать нечего.\n }\n\n if (item.link) {\n this.closeMobileMenu();\n }\n }\n\n public onClickSubMenu({ item, event }: { item: IRtSideMenu.Item; event: MouseEvent }): void {\n if (!item.link) {\n return;\n }\n\n this.clickSubMenuAction.emit({ item, event });\n\n if (!this.isPinned()) {\n this.closeSubMenu();\n }\n\n this.closeMobileMenu();\n }\n\n public onClickSubMenuAdditional(payload: { data: IRtSideMenu.ItemData | undefined; event: MouseEvent }): void {\n this.clickSubMenuAdditionalAction.emit(payload);\n }\n\n public onBackToMainMenu(): void {\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n }\n\n /**\n * Наведение на пункт полосы открывает его подменю сразу; уход указателя с панели — без пункта —\n * и пункт без подменю закрывают его через задержку.\n */\n public toggleSubMenu(item?: IRtSideMenu.Item): void {\n if (this.isPinned() || (item === undefined && this.#isHeld())) {\n return;\n }\n\n if (item?.submenu) {\n this.#closeTimer.cancel();\n this.selectedSubMenu.set(item.submenu);\n this.#hoverOpened.set(true);\n } else if (this.selectedItem()?.submenu) {\n this.#closeTimer.cancel();\n this.selectedSubMenu.set(this.selectedItem()?.submenu ?? null);\n } else {\n // Ушедший с панели указатель мог за задержку взяться за поиск или строку избранного.\n this.#closeTimer.run(this.subMenuCloseDelay(), (): void => {\n if (item !== undefined || !this.#isHeld()) {\n this.closeSubMenu();\n }\n });\n }\n }\n\n /** Указатель вернулся на панель: отложенное закрытие снимается. */\n public cancelSubMenuClose(): void {\n this.#closeTimer.cancel();\n }\n\n public closeSubMenu(): void {\n this.#closeTimer.cancel();\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n this.#hoverOpened.set(false);\n this.#searchHeld.set(false);\n this.#keyboard.reset();\n }\n\n public holdSubMenu(held: boolean): void {\n this.#dragHeld.set(held);\n }\n\n public toggleFolder(item: IRtSideMenu.Item): void {\n this.#keyboard.setFolderOpen(item, !this.expandedMenuIds().includes(item.id));\n }\n\n /** Человек взялся за поле поиска: дальше подменю закрывает нажатие снаружи, а не уход указателя. */\n public onSearchHold(): void {\n if (!this.isPinned()) {\n this.#searchHeld.set(true);\n }\n }\n\n /** Клавиши поля поиска раздаются списку; умолчание отменяется только у съеденной. */\n public onSearchKeydown(event: KeyboardEvent): void {\n if (this.#keyboard.press(event.key, this.visibleSubMenuItems(), this.expandedMenuIds())) {\n event.preventDefault();\n event.stopPropagation();\n }\n }\n\n /** Нажат переключатель режима: выбор уходит в настройки меню и наружу. */\n public onSubMenuModeToggle(): void {\n const mode: IRtSideMenu.SubMenuMode = this.isPinned() ? 'hover' : 'pinned';\n\n this.#settings?.setSubMenuMode(this.menuId(), mode);\n this.subMenuModeChange.emit(mode);\n }\n\n public onResizeStart(event: PointerEvent): void {\n if (!this.isPinned() || this.#resize.isRunning() || event.button !== 0) {\n return;\n }\n\n // Иначе указатель выделяет подписи пунктов, и тяга выглядит выделением текста.\n event.preventDefault();\n this.#resize.start(event, this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null));\n }\n\n public onResizeKeydown(event: KeyboardEvent): void {\n const from: number = this.#width() ?? drawnSideMenuWidth(this.panelRef()?.nativeElement ?? null);\n\n if (this.isPinned() && this.#resize.pressKey(event.key, from)) {\n event.preventDefault();\n }\n }\n\n public closeMobileMenu(): void {\n if (this.narrow()) {\n this.closeMobileMenuAction.emit();\n }\n }\n\n #isHeld(): boolean {\n return this.#searchHeld() || this.#dragHeld();\n }\n\n /**\n * Нажат пункт полосы, пока подменю закреплено: нажатие — выбор раздела. Пункт со своим адресом и\n * без разделов выбор снимает: у страницы нет разделов, и панель прежнего раздела врала бы.\n */\n #pickPinnedSubMenu(item: IRtSideMenu.Item): void {\n if (item.submenu?.length) {\n this.selectedItem.set(item);\n this.selectedSubMenu.set(item.submenu);\n } else if (item.link) {\n this.selectedItem.set(null);\n this.selectedSubMenu.set(null);\n } else {\n return;\n }\n\n this.searchControl.setValue('', { emitEvent: false });\n this.subMenuQuery.set('');\n }\n\n /**\n * Строка ищется по номеру среди узлов панели с id и нажимается как мышью: у ссылки номер стоит\n * на ней самой, у папки — на кнопке заголовка её раскрывающейся панели.\n */\n #pressRow(item: IRtSideMenu.Item): void {\n const rows: HTMLElement[] = Array.from(this.panelRef()?.nativeElement.querySelectorAll<HTMLElement>('[id]') ?? []);\n\n rows.find((row: HTMLElement): boolean => row.id === String(item.id))?.click();\n }\n}\n","<ng-container rtBlock=\"rt-side-menu\">\n @if (narrow()) {\n <!-- Узкий экран: один столбец — главные пункты или подменю выбранного раздела с кнопкой назад. -->\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 <!-- Подложка незакреплённой панели: подменю, взятое полем поиска, закрывает нажатие снаружи. -->\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 (mouseenter)=\"cancelSubMenuClose()\"\n (mouseleave)=\"toggleSubMenu()\">\n <div rtElem=\"panel-head\">\n <rt-input\n rtElem=\"search\"\n qa-dataid=\"side-menu-search\"\n iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\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\" [isScrollHintShown]=\"panelScrollHintShown()\">\n <ng-container *rtScrollAreaContent>\n <ng-container [ngTemplateOutlet]=\"subMenuTpl\" />\n </ng-container>\n </rt-scroll-area>\n </div>\n\n @if (isPinned()) {\n <!-- Ручка есть только у закреплённого подменю: незакреплённое уходит раньше, чем до края доедет рука. -->\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 iconLeft=\"search\"\n [size]=\"searchSize()\"\n [appearance]=\"searchAppearance()\"\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 <!-- Блок избранного раздела: сам решает, есть ли ему что показать, и без строк места не занимает. -->\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 <!-- Без подписей под значками имя пункта — в подсказке справа; выключенные подсказки гасят и её. -->\n @let railTip = railTitlesShown() || !subMenuTooltipsShown() ? '' : item.name;\n @if (item.link) {\n <a\n rtElem=\"rail-item\"\n qa-dataid=\"side-menu-item\"\n rtTooltipPlacement=\"right\"\n [routerLink]=\"item.link\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-current]=\"isActive ? 'page' : null\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\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 rtTooltipPlacement=\"right\"\n [rtMod]=\"{ active: isActive }\"\n [attr.aria-label]=\"railTitlesShown() ? null : item.name\"\n [rtTooltip]=\"railTip\"\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 <!-- Имя Material первого кита рисуется своей парой; имя, которого кит не рисует, — своим шаблоном меню. -->\n @let kitIcon = item.icon | rtSideMenuIcon;\n <span rtElem=\"rail-icon\">\n @if (kitIcon) {\n <rt-icon [name]=\"kitIcon\" [fill]=\"railIconFill()\" />\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 && railTitlesShown()) {\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 <!-- Стрелка раздела, как у первого кита: нажатие уводит в подменю, а не раскрывает на месте. -->\n <rt-icon rtElem=\"mobile-chevron\" size=\"sm\" name=\"arrow-right\" />\n }\n </ng-template>\n</ng-container>\n"],"mappings":"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"}