@cloud-ru/ds-uikit-product-header 1.3.2 → 1.3.3

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 (33) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +2 -0
  3. package/dist/cjs/components/MainMenu/helperComponents/Content/Content.js +1 -1
  4. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.d.ts +3 -1
  5. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.js +3 -2
  6. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +16 -0
  7. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupSkeleton.js +1 -1
  8. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.js +5 -2
  9. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.js +6 -2
  10. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.css +3 -0
  11. package/dist/cjs/components/MainMenu/types.d.ts +2 -0
  12. package/dist/cjs/style.css +19 -0
  13. package/dist/esm/components/MainMenu/helperComponents/Content/Content.js +1 -1
  14. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.d.ts +3 -1
  15. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.js +3 -2
  16. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +16 -0
  17. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupSkeleton.js +1 -1
  18. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.js +5 -2
  19. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.js +6 -2
  20. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.css +3 -0
  21. package/dist/esm/components/MainMenu/types.d.ts +2 -0
  22. package/dist/esm/style.css +19 -0
  23. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  24. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  25. package/package.json +9 -9
  26. package/src/components/MainMenu/helperComponents/Content/Content.tsx +1 -0
  27. package/src/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.tsx +29 -11
  28. package/src/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.scss +22 -0
  29. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SortableGroupSkeleton.tsx +1 -1
  30. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.tsx +6 -2
  31. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.tsx +10 -2
  32. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.scss +7 -0
  33. package/src/components/MainMenu/types.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 1.3.3 (2026-09-05)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **FF-8974:** cards group title skeleton size ([135daa5](https://github.com/cloud-ru-tech/snack-v2/commit/135daa5eac721af7cda5f79578507d27bc76e65d))
11
+ - **FF-8974:** skeleton for segment, visible subCategory actions on mobile and proper size, disable subCategory expand when clicked on title ([60e0b1a](https://github.com/cloud-ru-tech/snack-v2/commit/60e0b1a3dd9569b206fc5aca781d0a75f2b2ef52))
12
+
6
13
  ## 1.3.2 (2026-09-03)
7
14
 
8
15
  **Note:** Version bump only for package @ds/uikit-product-header
package/README.md CHANGED
@@ -342,6 +342,7 @@ function LocalMainMenu() {
342
342
  | `badge` | `CardServiceLightProps` \| `PromoTagPredefinedBaseProps` | — | |
343
343
  | `description` | `string \| undefined` | — | Краткое описание сервиса — отображается при включённом переключателе «Описание». |
344
344
  | `disabled` | `boolean \| undefined` | — | |
345
+ | `expandableEnabled` | `boolean \| undefined` | — | Разрешено ли раскрытие карточки. default=false - Запрещено |
345
346
  | `favoritesEnabled` | `boolean \| undefined` | — | Разрешено ли добавление карточки в избранное. default=true - разрешено |
346
347
  | `hidden` | `boolean \| undefined` | — | |
347
348
  | `href` | `string \| undefined` | — | |
@@ -513,6 +514,7 @@ import { MainMenu, MenuMobile } from '@cloud-ru/ds-uikit-product-header';
513
514
  | `badge` | `CardServiceLightProps` \| `PromoTagPredefinedBaseProps` | — | |
514
515
  | `description` | `string \| undefined` | — | Краткое описание сервиса — отображается при включённом переключателе «Описание». |
515
516
  | `disabled` | `boolean \| undefined` | — | |
517
+ | `expandableEnabled` | `boolean \| undefined` | — | Разрешено ли раскрытие карточки. default=false - Запрещено |
516
518
  | `favoritesEnabled` | `boolean \| undefined` | — | Разрешено ли добавление карточки в избранное. default=true - разрешено |
517
519
  | `hidden` | `boolean \| undefined` | — | |
518
520
  | `href` | `string \| undefined` | — | |
@@ -97,5 +97,5 @@ function Content({ searchValue, search, rightTop, segments, searchGroups = [], s
97
97
  appearance: 'neutral',
98
98
  }, content: isSearching ? t('noDataFound') : t('noData'), "data-test-id": isSearching ? constants_1.TEST_IDS.noDataFound : constants_1.TEST_IDS.noData, "data-mobile": isMobile || undefined, className: styles_module_scss_1.default.noData }));
99
99
  })();
100
- return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [search, (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.content, className), "data-empty": (!loading && !hasCards) || undefined, children: [!isSearching && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isMobile && rightTop, (0, jsx_runtime_1.jsx)(ContentToolbar_1.ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile }), !isMobile && rightTop] })), cards, footer] })] }));
100
+ return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [search, (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.content, className), "data-empty": (!loading && !hasCards) || undefined, children: [!isSearching && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isMobile && rightTop, (0, jsx_runtime_1.jsx)(ContentToolbar_1.ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile, loading: loading }), !isMobile && rightTop] })), cards, footer] })] }));
101
101
  }
@@ -14,5 +14,7 @@ export type ContentToolbarProps = {
14
14
  /** Настройки меню (модалка по кнопке). Не передано — кнопка настроек не отображается. */
15
15
  preferences?: MainMenuPreferencesProps;
16
16
  isMobile?: boolean;
17
+ /** Флаг загрузки данных — сегменты подгружаются с бэка, поэтому SegmentControl тоже уходит в скелетон. */
18
+ loading?: boolean;
17
19
  };
18
- export declare function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, }: ContentToolbarProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, loading, }: ContentToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -9,6 +9,7 @@ const button_1 = require("@cloud-ru/ds-button");
9
9
  const system_1 = require("@cloud-ru/ds-icons/interface/system");
10
10
  const modal_1 = require("@cloud-ru/ds-modal");
11
11
  const segment_control_1 = require("@cloud-ru/ds-segment-control");
12
+ const skeleton_1 = require("@cloud-ru/ds-skeleton");
12
13
  const uikit_product_switch_row_1 = require("@cloud-ru/ds-uikit-product-switch-row");
13
14
  const utils_1 = require("@cloud-ru/ds-utils");
14
15
  const react_1 = require("react");
@@ -16,7 +17,7 @@ const locale_1 = require("../../../../../../locale");
16
17
  const HeaderButton_1 = require("../../../../../HeaderButton");
17
18
  const constants_1 = require("../../constants");
18
19
  const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
19
- function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, }) {
20
+ function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, loading, }) {
20
21
  const { t } = locale_1.headerLocale.useTranslations();
21
22
  const [open = false, setOpen] = (0, utils_1.useValueControl)({
22
23
  value: preferences?.open,
@@ -28,7 +29,7 @@ function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpan
28
29
  (0, react_1.startTransition)(() => preferences?.showDescription?.onChange(value));
29
30
  };
30
31
  const hasSegmentItems = segmentItems && segmentItems?.length > 0;
31
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-test-id": constants_1.TEST_IDS.toolbar, children: [hasSegmentItems && ((0, jsx_runtime_1.jsx)(segment_control_1.SegmentControl, { size: 'm', outline: true, value: segment, onChange: onSegmentChange, className: styles_module_scss_1.default.segmentControl, "data-test-id": constants_1.TEST_IDS.segmentControl, items: segmentItems, width: isMobile ? 'full' : 'auto' })), (preferences || !isMobile) && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.actions, children: [preferences && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(button_1.Button, { view: 'simple', size: 'm', appearance: 'neutral', icon: (0, jsx_runtime_1.jsx)(system_1.SettingsSVG, {}), "aria-label": t('menuSettingsTitle'), "data-test-id": constants_1.TEST_IDS.settingsButton, onClick: openModal }), (0, jsx_runtime_1.jsx)(modal_1.Modal, { open: open, onClose: closeModal, title: t('menuSettingsTitle'), "data-test-id": constants_1.TEST_IDS.settingsModal, cancelButton: {
32
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-test-id": constants_1.TEST_IDS.toolbar, children: [hasSegmentItems && ((0, jsx_runtime_1.jsx)(skeleton_1.WithSkeleton, { loading: loading, skeleton: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.segmentControlSkeleton, "data-test-id": constants_1.TEST_IDS.segmentControl, "data-mobile": isMobile || undefined, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: styles_module_scss_1.default.segmentControlSkeletonBlock }) }), children: (0, jsx_runtime_1.jsx)(segment_control_1.SegmentControl, { size: 'm', outline: true, value: segment, onChange: onSegmentChange, className: styles_module_scss_1.default.segmentControl, "data-test-id": constants_1.TEST_IDS.segmentControl, items: segmentItems, width: isMobile ? 'full' : 'auto' }) })), (preferences || !isMobile) && !loading && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.actions, children: [preferences && ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(button_1.Button, { view: 'simple', size: 'm', appearance: 'neutral', icon: (0, jsx_runtime_1.jsx)(system_1.SettingsSVG, {}), "aria-label": t('menuSettingsTitle'), "data-test-id": constants_1.TEST_IDS.settingsButton, onClick: openModal }), (0, jsx_runtime_1.jsx)(modal_1.Modal, { open: open, onClose: closeModal, title: t('menuSettingsTitle'), "data-test-id": constants_1.TEST_IDS.settingsModal, cancelButton: {
32
33
  label: t('close'),
33
34
  onClick: closeModal,
34
35
  }, content: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.settingsContent, children: [preferences.showGroupsColors && ((0, jsx_runtime_1.jsx)(uikit_product_switch_row_1.SwitchRow, { title: t('menuSettingsShowGroupsColors'), checked: preferences.showGroupsColors.value, onChange: preferences.showGroupsColors.onChange, "data-test-id": constants_1.TEST_IDS.settingsShowGroupsColorsSwitch })), (0, jsx_runtime_1.jsx)(uikit_product_switch_row_1.SwitchRow, { title: t('menuSettingsShowDescription'), checked: preferences.showDescription.value, onChange: handleShowDescriptionChange, "data-test-id": constants_1.TEST_IDS.settingsShowDescriptionSwitch })] }) })] })), !isMobile && ((0, jsx_runtime_1.jsx)(HeaderButton_1.HeaderButton, { icon: allGroupsExpanded ? (0, jsx_runtime_1.jsx)(system_1.CollapseVerticalSVG, {}) : (0, jsx_runtime_1.jsx)(system_1.ExpandVerticalSVG, {}), "aria-label": allGroupsExpanded ? t('contentCollapseAllGroups') : t('contentExpandAllGroups'), "data-test-id": constants_1.TEST_IDS.toggleAllGroups, onClick: onToggleAllGroupsExpanded, tooltip: { tip: allGroupsExpanded ? t('contentCollapseAllGroups') : t('contentExpandAllGroups') } }))] }))] }));
@@ -12,6 +12,22 @@
12
12
  flex-shrink:0;
13
13
  }
14
14
 
15
+ .segmentControlSkeleton{
16
+ display:flex;
17
+ flex-shrink:0;
18
+ width:264px;
19
+ height:var(--sn-segmentControl-anatomy-size-m-segment-container-height, var(--sn-density-size-m, var(--sn-primitive-dimension-56, 56px)));
20
+ }
21
+ .segmentControlSkeleton[data-mobile]{
22
+ width:100%;
23
+ }
24
+
25
+ .segmentControlSkeletonBlock{
26
+ width:100%;
27
+ height:100%;
28
+ border-radius:var(--sn-segmentControl-anatomy-size-m-container-borderRadius, var(--sn-brand-anatomy-radius-button-m, var(--sn-primitive-dimension-2, 2px)));
29
+ }
30
+
15
31
  .actions{
16
32
  display:flex;
17
33
  flex-shrink:0;
@@ -8,5 +8,5 @@ const jsx_runtime_1 = require("react/jsx-runtime");
8
8
  const skeleton_1 = require("@cloud-ru/ds-skeleton");
9
9
  const styles_module_scss_1 = __importDefault(require('../styles.module.css'));
10
10
  function SortableGroupSkeleton({ isMobile }) {
11
- return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.group, children: (0, jsx_runtime_1.jsxs)(skeleton_1.SkeletonContext.Provider, { value: true, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonTitle, children: (0, jsx_runtime_1.jsx)(skeleton_1.SkeletonText, { lines: 1, width: isMobile ? '50%' : '30%' }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonBody, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.groupBody, "data-mobile": isMobile || undefined, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) })] }) })] }) }));
11
+ return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.group, children: (0, jsx_runtime_1.jsxs)(skeleton_1.SkeletonContext.Provider, { value: true, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonTitle, children: (0, jsx_runtime_1.jsx)(skeleton_1.SkeletonText, { variant: 'title', size: 'm', lines: 1, width: isMobile ? '50%' : '30%' }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonBody, children: (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.groupBody, "data-mobile": isMobile || undefined, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) }), (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonService, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { height: 24, borderRadius: 2 }) })] }) })] }) }));
12
12
  }
@@ -17,11 +17,14 @@ function SubCategory({ groupId, service, showDescription, isMobile, dragDisabled
17
17
  const subcategoryId = (0, innerLink_1.getSubCategoryId)(groupId, service.id);
18
18
  const nestedGroupId = (0, innerLink_1.getNestedServiceGroupId)(groupId, service.id);
19
19
  const nestedItems = (0, react_1.useMemo)(() => service.items?.filter(item => !item.hidden) ?? [], [service.items]);
20
+ const isExpandableEnabled = service.expandableEnabled ?? false;
20
21
  // Раскрытие подкатегории — локальное uncontrolled состояние: наружу его пока не выводим.
21
22
  const [isExpanded, setIsExpanded] = (0, react_1.useState)(subcategoryId);
22
23
  const handleExpandedChange = (0, react_1.useCallback)(() => {
23
- setIsExpanded(isExpanded ? undefined : subcategoryId);
24
- }, [isExpanded, subcategoryId]);
24
+ if (isExpandableEnabled) {
25
+ setIsExpanded(isExpanded ? undefined : subcategoryId);
26
+ }
27
+ }, [isExpanded, subcategoryId, isExpandableEnabled]);
25
28
  return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_2.default.subcategory, "data-test-id": `${constants_1.TEST_IDS.subcategory}-${service.id}`, children: (0, jsx_runtime_1.jsx)(accordion_1.Accordion, { selectionMode: 'single', expanded: isExpanded, onExpandedChange: handleExpandedChange, children: (0, jsx_runtime_1.jsx)(accordion_1.Accordion.CollapseBlockTertiary, { id: subcategoryId, showChevron: false, className: styles_module_scss_2.default.subcategoryAccordion, afterTitle: (0, jsx_runtime_1.jsx)(SubCategoryTitle_1.SubCategoryTitle, { groupId: groupId, service: service, expandable: {
26
29
  value: Boolean(isExpanded),
27
30
  onClick: handleExpandedChange,
@@ -39,7 +39,7 @@ function SubCategoryTitle({ groupId, service, showDescription: showDescriptionPr
39
39
  ],
40
40
  });
41
41
  if (expandable?.value) {
42
- return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.subcategoryTitle, children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.subcategoryTitleRow, onKeyDown: handleKeyDown, children: [(0, jsx_runtime_1.jsx)(uikit_product_title_clickable_1.TitleClickable, { as: 'a', href: service.href, onClick: handleServiceClick, innerRef: titleRef, title: service.label, icon: (0, utils_2.getLinkEmblem)(service), className: styles_module_scss_1.default.subcategoryTitleClickable, "data-test-id": titleTestId }), (0, jsx_runtime_1.jsx)(uikit_product_card_predefined_1.CardActionsSurface, { className: styles_module_scss_1.default.subcategoryTitleActions, actionsVisibility: 'always', tooltip: hasTooltip
42
+ return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.subcategoryTitle, "data-mobile": isMobile || undefined, children: [(0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.subcategoryTitleRow, onKeyDown: handleKeyDown, children: [(0, jsx_runtime_1.jsx)(uikit_product_title_clickable_1.TitleClickable, { as: 'a', href: service.href, onClick: handleServiceClick, innerRef: titleRef, title: service.label, icon: (0, utils_2.getLinkEmblem)(service), className: styles_module_scss_1.default.subcategoryTitleClickable, "data-test-id": titleTestId }), (0, jsx_runtime_1.jsx)(uikit_product_card_predefined_1.CardActionsSurface, { className: styles_module_scss_1.default.subcategoryTitleActions, actionsVisibility: 'always', actionsSize: isMobile ? 's' : 'm', tooltip: hasTooltip
43
43
  ? {
44
44
  tip: service.description,
45
45
  buttonRef: tooltipTriggerRef,
@@ -56,5 +56,9 @@ function SubCategoryTitle({ groupId, service, showDescription: showDescriptionPr
56
56
  }
57
57
  : undefined })] }), showDescription && ((0, jsx_runtime_1.jsx)(typography_1.Typography, { as: 'p', variant: 'body', size: 's', className: styles_module_scss_1.default.subcategoryDescription, "data-test-id": `${titleTestId}-description`, children: service.description }))] }));
58
58
  }
59
- return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.subcategoryTitleDraggable, onPointerDown: utils_1.stopEventPropagation, children: (0, jsx_runtime_1.jsx)(ServiceCard_1.DraggableServiceCard, { groupId: groupId, service: service, favorite: favorite, isMobile: isMobile, onServiceClick: onServiceClick, showDescription: showDescriptionProp, dragDisabled: dragDisabled, expandable: expandable }) }));
59
+ return (
60
+ // Карточка — ссылка `<a>`: клик по ней бы долетал до `onClick={toggleOpen}` заголовка
61
+ // родительского CollapseBlockTertiary (см. TODO там) и лишний раз переключал аккордеон.
62
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
63
+ (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.subcategoryTitleDraggable, onPointerDown: utils_1.stopEventPropagation, onClick: utils_1.stopEventPropagation, children: (0, jsx_runtime_1.jsx)(ServiceCard_1.DraggableServiceCard, { groupId: groupId, service: service, favorite: favorite, isMobile: isMobile, onServiceClick: onServiceClick, showDescription: showDescriptionProp, dragDisabled: dragDisabled, expandable: expandable }) }));
60
64
  }
@@ -32,6 +32,9 @@
32
32
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
33
33
  visibility:visible;
34
34
  }
35
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
36
+ visibility:visible;
37
+ }
35
38
 
36
39
  .subcategoryTitleRow{
37
40
  display:flex;
@@ -36,6 +36,8 @@ export type InnerLink = {
36
36
  badge?: CardServiceLightProps['promoTag'];
37
37
  /** Разрешено ли добавление карточки в избранное. default=true - разрешено */
38
38
  favoritesEnabled?: boolean;
39
+ /** Разрешено ли раскрытие карточки. default=false - Запрещено */
40
+ expandableEnabled?: boolean;
39
41
  /**
40
42
  * Вложенные сервисы подкатегории.
41
43
  *
@@ -166,6 +166,22 @@
166
166
  flex-shrink:0;
167
167
  }
168
168
 
169
+ .segmentControlSkeleton{
170
+ display:flex;
171
+ flex-shrink:0;
172
+ width:264px;
173
+ height:var(--sn-segmentControl-anatomy-size-m-segment-container-height, var(--sn-density-size-m, var(--sn-primitive-dimension-56, 56px)));
174
+ }
175
+ .segmentControlSkeleton[data-mobile]{
176
+ width:100%;
177
+ }
178
+
179
+ .segmentControlSkeletonBlock{
180
+ width:100%;
181
+ height:100%;
182
+ border-radius:var(--sn-segmentControl-anatomy-size-m-container-borderRadius, var(--sn-brand-anatomy-radius-button-m, var(--sn-primitive-dimension-2, 2px)));
183
+ }
184
+
169
185
  .actions{
170
186
  display:flex;
171
187
  flex-shrink:0;
@@ -214,6 +230,9 @@
214
230
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
215
231
  visibility:visible;
216
232
  }
233
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
234
+ visibility:visible;
235
+ }
217
236
 
218
237
  .subcategoryTitleRow{
219
238
  display:flex;
@@ -91,5 +91,5 @@ export function Content({ searchValue, search, rightTop, segments, searchGroups
91
91
  appearance: 'neutral',
92
92
  }, content: isSearching ? t('noDataFound') : t('noData'), "data-test-id": isSearching ? TEST_IDS.noDataFound : TEST_IDS.noData, "data-mobile": isMobile || undefined, className: styles.noData }));
93
93
  })();
94
- return (_jsxs(_Fragment, { children: [search, _jsxs("div", { className: cn(styles.content, className), "data-empty": (!loading && !hasCards) || undefined, children: [!isSearching && (_jsxs(_Fragment, { children: [isMobile && rightTop, _jsx(ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile }), !isMobile && rightTop] })), cards, footer] })] }));
94
+ return (_jsxs(_Fragment, { children: [search, _jsxs("div", { className: cn(styles.content, className), "data-empty": (!loading && !hasCards) || undefined, children: [!isSearching && (_jsxs(_Fragment, { children: [isMobile && rightTop, _jsx(ContentToolbar, { segment: segmentId, onSegmentChange: setSegmentId, segmentItems: segmentItems, allGroupsExpanded: allGroupsExpanded, onToggleAllGroupsExpanded: handleToggleAllGroupsExpanded, preferences: preferences, isMobile: isMobile, loading: loading }), !isMobile && rightTop] })), cards, footer] })] }));
95
95
  }
@@ -14,5 +14,7 @@ export type ContentToolbarProps = {
14
14
  /** Настройки меню (модалка по кнопке). Не передано — кнопка настроек не отображается. */
15
15
  preferences?: MainMenuPreferencesProps;
16
16
  isMobile?: boolean;
17
+ /** Флаг загрузки данных — сегменты подгружаются с бэка, поэтому SegmentControl тоже уходит в скелетон. */
18
+ loading?: boolean;
17
19
  };
18
- export declare function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, }: ContentToolbarProps): import("react/jsx-runtime").JSX.Element;
20
+ export declare function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, loading, }: ContentToolbarProps): import("react/jsx-runtime").JSX.Element;
@@ -3,6 +3,7 @@ import { Button } from '@cloud-ru/ds-button';
3
3
  import { CollapseVerticalSVG, ExpandVerticalSVG, SettingsSVG } from '@cloud-ru/ds-icons/interface/system';
4
4
  import { Modal } from '@cloud-ru/ds-modal';
5
5
  import { SegmentControl } from '@cloud-ru/ds-segment-control';
6
+ import { Skeleton, WithSkeleton } from '@cloud-ru/ds-skeleton';
6
7
  import { SwitchRow } from '@cloud-ru/ds-uikit-product-switch-row';
7
8
  import { useValueControl } from '@cloud-ru/ds-utils';
8
9
  import { startTransition } from 'react';
@@ -10,7 +11,7 @@ import { headerLocale } from '../../../../../../locale/index.js';
10
11
  import { HeaderButton } from '../../../../../HeaderButton/index.js';
11
12
  import { TEST_IDS } from '../../constants.js';
12
13
  import styles from './styles.module.css';
13
- export function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, }) {
14
+ export function ContentToolbar({ segment, onSegmentChange, segmentItems, allGroupsExpanded, onToggleAllGroupsExpanded, preferences, isMobile, loading, }) {
14
15
  const { t } = headerLocale.useTranslations();
15
16
  const [open = false, setOpen] = useValueControl({
16
17
  value: preferences?.open,
@@ -22,7 +23,7 @@ export function ContentToolbar({ segment, onSegmentChange, segmentItems, allGrou
22
23
  startTransition(() => preferences?.showDescription?.onChange(value));
23
24
  };
24
25
  const hasSegmentItems = segmentItems && segmentItems?.length > 0;
25
- return (_jsxs("div", { className: styles.root, "data-test-id": TEST_IDS.toolbar, children: [hasSegmentItems && (_jsx(SegmentControl, { size: 'm', outline: true, value: segment, onChange: onSegmentChange, className: styles.segmentControl, "data-test-id": TEST_IDS.segmentControl, items: segmentItems, width: isMobile ? 'full' : 'auto' })), (preferences || !isMobile) && (_jsxs("div", { className: styles.actions, children: [preferences && (_jsxs(_Fragment, { children: [_jsx(Button, { view: 'simple', size: 'm', appearance: 'neutral', icon: _jsx(SettingsSVG, {}), "aria-label": t('menuSettingsTitle'), "data-test-id": TEST_IDS.settingsButton, onClick: openModal }), _jsx(Modal, { open: open, onClose: closeModal, title: t('menuSettingsTitle'), "data-test-id": TEST_IDS.settingsModal, cancelButton: {
26
+ return (_jsxs("div", { className: styles.root, "data-test-id": TEST_IDS.toolbar, children: [hasSegmentItems && (_jsx(WithSkeleton, { loading: loading, skeleton: _jsx("div", { className: styles.segmentControlSkeleton, "data-test-id": TEST_IDS.segmentControl, "data-mobile": isMobile || undefined, children: _jsx(Skeleton, { className: styles.segmentControlSkeletonBlock }) }), children: _jsx(SegmentControl, { size: 'm', outline: true, value: segment, onChange: onSegmentChange, className: styles.segmentControl, "data-test-id": TEST_IDS.segmentControl, items: segmentItems, width: isMobile ? 'full' : 'auto' }) })), (preferences || !isMobile) && !loading && (_jsxs("div", { className: styles.actions, children: [preferences && (_jsxs(_Fragment, { children: [_jsx(Button, { view: 'simple', size: 'm', appearance: 'neutral', icon: _jsx(SettingsSVG, {}), "aria-label": t('menuSettingsTitle'), "data-test-id": TEST_IDS.settingsButton, onClick: openModal }), _jsx(Modal, { open: open, onClose: closeModal, title: t('menuSettingsTitle'), "data-test-id": TEST_IDS.settingsModal, cancelButton: {
26
27
  label: t('close'),
27
28
  onClick: closeModal,
28
29
  }, content: _jsxs("div", { className: styles.settingsContent, children: [preferences.showGroupsColors && (_jsx(SwitchRow, { title: t('menuSettingsShowGroupsColors'), checked: preferences.showGroupsColors.value, onChange: preferences.showGroupsColors.onChange, "data-test-id": TEST_IDS.settingsShowGroupsColorsSwitch })), _jsx(SwitchRow, { title: t('menuSettingsShowDescription'), checked: preferences.showDescription.value, onChange: handleShowDescriptionChange, "data-test-id": TEST_IDS.settingsShowDescriptionSwitch })] }) })] })), !isMobile && (_jsx(HeaderButton, { icon: allGroupsExpanded ? _jsx(CollapseVerticalSVG, {}) : _jsx(ExpandVerticalSVG, {}), "aria-label": allGroupsExpanded ? t('contentCollapseAllGroups') : t('contentExpandAllGroups'), "data-test-id": TEST_IDS.toggleAllGroups, onClick: onToggleAllGroupsExpanded, tooltip: { tip: allGroupsExpanded ? t('contentCollapseAllGroups') : t('contentExpandAllGroups') } }))] }))] }));
@@ -12,6 +12,22 @@
12
12
  flex-shrink:0;
13
13
  }
14
14
 
15
+ .segmentControlSkeleton{
16
+ display:flex;
17
+ flex-shrink:0;
18
+ width:264px;
19
+ height:var(--sn-segmentControl-anatomy-size-m-segment-container-height, var(--sn-density-size-m, var(--sn-primitive-dimension-56, 56px)));
20
+ }
21
+ .segmentControlSkeleton[data-mobile]{
22
+ width:100%;
23
+ }
24
+
25
+ .segmentControlSkeletonBlock{
26
+ width:100%;
27
+ height:100%;
28
+ border-radius:var(--sn-segmentControl-anatomy-size-m-container-borderRadius, var(--sn-brand-anatomy-radius-button-m, var(--sn-primitive-dimension-2, 2px)));
29
+ }
30
+
15
31
  .actions{
16
32
  display:flex;
17
33
  flex-shrink:0;
@@ -2,5 +2,5 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Skeleton, SkeletonContext, SkeletonText } from '@cloud-ru/ds-skeleton';
3
3
  import styles from '../styles.module.css';
4
4
  export function SortableGroupSkeleton({ isMobile }) {
5
- return (_jsx("div", { className: styles.group, children: _jsxs(SkeletonContext.Provider, { value: true, children: [_jsx("div", { className: styles.skeletonTitle, children: _jsx(SkeletonText, { lines: 1, width: isMobile ? '50%' : '30%' }) }), _jsx("div", { className: styles.skeletonBody, children: _jsxs("div", { className: styles.groupBody, "data-mobile": isMobile || undefined, children: [_jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) })] }) })] }) }));
5
+ return (_jsx("div", { className: styles.group, children: _jsxs(SkeletonContext.Provider, { value: true, children: [_jsx("div", { className: styles.skeletonTitle, children: _jsx(SkeletonText, { variant: 'title', size: 'm', lines: 1, width: isMobile ? '50%' : '30%' }) }), _jsx("div", { className: styles.skeletonBody, children: _jsxs("div", { className: styles.groupBody, "data-mobile": isMobile || undefined, children: [_jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) }), _jsx("div", { className: styles.skeletonService, children: _jsx(Skeleton, { height: 24, borderRadius: 2 }) })] }) })] }) }));
6
6
  }
@@ -11,11 +11,14 @@ export function SubCategory({ groupId, service, showDescription, isMobile, dragD
11
11
  const subcategoryId = getSubCategoryId(groupId, service.id);
12
12
  const nestedGroupId = getNestedServiceGroupId(groupId, service.id);
13
13
  const nestedItems = useMemo(() => service.items?.filter(item => !item.hidden) ?? [], [service.items]);
14
+ const isExpandableEnabled = service.expandableEnabled ?? false;
14
15
  // Раскрытие подкатегории — локальное uncontrolled состояние: наружу его пока не выводим.
15
16
  const [isExpanded, setIsExpanded] = useState(subcategoryId);
16
17
  const handleExpandedChange = useCallback(() => {
17
- setIsExpanded(isExpanded ? undefined : subcategoryId);
18
- }, [isExpanded, subcategoryId]);
18
+ if (isExpandableEnabled) {
19
+ setIsExpanded(isExpanded ? undefined : subcategoryId);
20
+ }
21
+ }, [isExpanded, subcategoryId, isExpandableEnabled]);
19
22
  return (_jsx("div", { className: styles.subcategory, "data-test-id": `${TEST_IDS.subcategory}-${service.id}`, children: _jsx(Accordion, { selectionMode: 'single', expanded: isExpanded, onExpandedChange: handleExpandedChange, children: _jsx(Accordion.CollapseBlockTertiary, { id: subcategoryId, showChevron: false, className: styles.subcategoryAccordion, afterTitle: _jsx(SubCategoryTitle, { groupId: groupId, service: service, expandable: {
20
23
  value: Boolean(isExpanded),
21
24
  onClick: handleExpandedChange,
@@ -33,7 +33,7 @@ export function SubCategoryTitle({ groupId, service, showDescription: showDescri
33
33
  ],
34
34
  });
35
35
  if (expandable?.value) {
36
- return (_jsxs("div", { className: styles.subcategoryTitle, children: [_jsxs("div", { className: styles.subcategoryTitleRow, onKeyDown: handleKeyDown, children: [_jsx(TitleClickable, { as: 'a', href: service.href, onClick: handleServiceClick, innerRef: titleRef, title: service.label, icon: getLinkEmblem(service), className: styles.subcategoryTitleClickable, "data-test-id": titleTestId }), _jsx(CardActionsSurface, { className: styles.subcategoryTitleActions, actionsVisibility: 'always', tooltip: hasTooltip
36
+ return (_jsxs("div", { className: styles.subcategoryTitle, "data-mobile": isMobile || undefined, children: [_jsxs("div", { className: styles.subcategoryTitleRow, onKeyDown: handleKeyDown, children: [_jsx(TitleClickable, { as: 'a', href: service.href, onClick: handleServiceClick, innerRef: titleRef, title: service.label, icon: getLinkEmblem(service), className: styles.subcategoryTitleClickable, "data-test-id": titleTestId }), _jsx(CardActionsSurface, { className: styles.subcategoryTitleActions, actionsVisibility: 'always', actionsSize: isMobile ? 's' : 'm', tooltip: hasTooltip
37
37
  ? {
38
38
  tip: service.description,
39
39
  buttonRef: tooltipTriggerRef,
@@ -50,5 +50,9 @@ export function SubCategoryTitle({ groupId, service, showDescription: showDescri
50
50
  }
51
51
  : undefined })] }), showDescription && (_jsx(Typography, { as: 'p', variant: 'body', size: 's', className: styles.subcategoryDescription, "data-test-id": `${titleTestId}-description`, children: service.description }))] }));
52
52
  }
53
- return (_jsx("div", { className: styles.subcategoryTitleDraggable, onPointerDown: stopEventPropagation, children: _jsx(DraggableServiceCard, { groupId: groupId, service: service, favorite: favorite, isMobile: isMobile, onServiceClick: onServiceClick, showDescription: showDescriptionProp, dragDisabled: dragDisabled, expandable: expandable }) }));
53
+ return (
54
+ // Карточка — ссылка `<a>`: клик по ней бы долетал до `onClick={toggleOpen}` заголовка
55
+ // родительского CollapseBlockTertiary (см. TODO там) и лишний раз переключал аккордеон.
56
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions
57
+ _jsx("div", { className: styles.subcategoryTitleDraggable, onPointerDown: stopEventPropagation, onClick: stopEventPropagation, children: _jsx(DraggableServiceCard, { groupId: groupId, service: service, favorite: favorite, isMobile: isMobile, onServiceClick: onServiceClick, showDescription: showDescriptionProp, dragDisabled: dragDisabled, expandable: expandable }) }));
54
58
  }
@@ -32,6 +32,9 @@
32
32
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
33
33
  visibility:visible;
34
34
  }
35
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
36
+ visibility:visible;
37
+ }
35
38
 
36
39
  .subcategoryTitleRow{
37
40
  display:flex;
@@ -36,6 +36,8 @@ export type InnerLink = {
36
36
  badge?: CardServiceLightProps['promoTag'];
37
37
  /** Разрешено ли добавление карточки в избранное. default=true - разрешено */
38
38
  favoritesEnabled?: boolean;
39
+ /** Разрешено ли раскрытие карточки. default=false - Запрещено */
40
+ expandableEnabled?: boolean;
39
41
  /**
40
42
  * Вложенные сервисы подкатегории.
41
43
  *
@@ -166,6 +166,22 @@
166
166
  flex-shrink:0;
167
167
  }
168
168
 
169
+ .segmentControlSkeleton{
170
+ display:flex;
171
+ flex-shrink:0;
172
+ width:264px;
173
+ height:var(--sn-segmentControl-anatomy-size-m-segment-container-height, var(--sn-density-size-m, var(--sn-primitive-dimension-56, 56px)));
174
+ }
175
+ .segmentControlSkeleton[data-mobile]{
176
+ width:100%;
177
+ }
178
+
179
+ .segmentControlSkeletonBlock{
180
+ width:100%;
181
+ height:100%;
182
+ border-radius:var(--sn-segmentControl-anatomy-size-m-container-borderRadius, var(--sn-brand-anatomy-radius-button-m, var(--sn-primitive-dimension-2, 2px)));
183
+ }
184
+
169
185
  .actions{
170
186
  display:flex;
171
187
  flex-shrink:0;
@@ -214,6 +230,9 @@
214
230
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
215
231
  visibility:visible;
216
232
  }
233
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
234
+ visibility:visible;
235
+ }
217
236
 
218
237
  .subcategoryTitleRow{
219
238
  display:flex;