@cloud-ru/ds-uikit-product-header 1.3.1 → 1.3.3-preview-18093d38.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 (30) hide show
  1. package/CHANGELOG.md +4 -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 +4 -2
  6. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +5 -0
  7. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.js +5 -2
  8. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.js +6 -2
  9. package/dist/cjs/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.css +3 -0
  10. package/dist/cjs/components/MainMenu/types.d.ts +2 -0
  11. package/dist/cjs/style.css +8 -0
  12. package/dist/esm/components/MainMenu/helperComponents/Content/Content.js +1 -1
  13. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.d.ts +3 -1
  14. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.js +5 -3
  15. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.css +5 -0
  16. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.js +5 -2
  17. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.js +6 -2
  18. package/dist/esm/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.css +3 -0
  19. package/dist/esm/components/MainMenu/types.d.ts +2 -0
  20. package/dist/esm/style.css +8 -0
  21. package/dist/tsconfig.cjs.tsbuildinfo +1 -1
  22. package/dist/tsconfig.esm.tsbuildinfo +1 -1
  23. package/package.json +27 -27
  24. package/src/components/MainMenu/helperComponents/Content/Content.tsx +1 -0
  25. package/src/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/ContentToolbar.tsx +27 -11
  26. package/src/components/MainMenu/helperComponents/Content/helperComponents/ContentToolbar/styles.module.scss +5 -0
  27. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategory.tsx +6 -2
  28. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/SubCategoryTitle.tsx +10 -2
  29. package/src/components/MainMenu/helperComponents/Content/helperComponents/SortableGroup/components/SubCategory/styles.module.scss +7 -0
  30. package/src/components/MainMenu/types.ts +2 -0
package/CHANGELOG.md CHANGED
@@ -3,6 +3,10 @@
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.2 (2026-09-03)
7
+
8
+ **Note:** Version bump only for package @ds/uikit-product-header
9
+
6
10
  ## 1.3.1 (2026-09-03)
7
11
 
8
12
  **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,8 @@ 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
+ const segmentControlSkeletonSize = isMobile ? { width: '100%', height: 48 } : { width: 262, height: 32 };
33
+ 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)(jsx_runtime_1.Fragment, { children: loading ? ((0, jsx_runtime_1.jsx)(skeleton_1.SkeletonContext.Provider, { value: true, children: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.segmentControlSkeleton, "data-test-id": constants_1.TEST_IDS.segmentControl, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { ...segmentControlSkeletonSize }) }) })) : ((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
34
  label: t('close'),
33
35
  onClick: closeModal,
34
36
  }, 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,11 @@
12
12
  flex-shrink:0;
13
13
  }
14
14
 
15
+ .segmentControlSkeleton{
16
+ display:flex;
17
+ flex-shrink:0;
18
+ }
19
+
15
20
  .actions{
16
21
  display:flex;
17
22
  flex-shrink:0;
@@ -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,11 @@
166
166
  flex-shrink:0;
167
167
  }
168
168
 
169
+ .segmentControlSkeleton{
170
+ display:flex;
171
+ flex-shrink:0;
172
+ }
173
+
169
174
  .actions{
170
175
  display:flex;
171
176
  flex-shrink:0;
@@ -214,6 +219,9 @@
214
219
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
215
220
  visibility:visible;
216
221
  }
222
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
223
+ visibility:visible;
224
+ }
217
225
 
218
226
  .subcategoryTitleRow{
219
227
  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;
@@ -1,8 +1,9 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  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, SkeletonContext } 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,8 @@ 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
+ const segmentControlSkeletonSize = isMobile ? { width: '100%', height: 48 } : { width: 262, height: 32 };
27
+ return (_jsxs("div", { className: styles.root, "data-test-id": TEST_IDS.toolbar, children: [hasSegmentItems && (_jsx(_Fragment, { children: loading ? (_jsx(SkeletonContext.Provider, { value: true, children: _jsx("div", { className: styles.segmentControlSkeleton, "data-test-id": TEST_IDS.segmentControl, children: _jsx(Skeleton, { ...segmentControlSkeletonSize }) }) })) : (_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
28
  label: t('close'),
27
29
  onClick: closeModal,
28
30
  }, 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,11 @@
12
12
  flex-shrink:0;
13
13
  }
14
14
 
15
+ .segmentControlSkeleton{
16
+ display:flex;
17
+ flex-shrink:0;
18
+ }
19
+
15
20
  .actions{
16
21
  display:flex;
17
22
  flex-shrink:0;
@@ -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,11 @@
166
166
  flex-shrink:0;
167
167
  }
168
168
 
169
+ .segmentControlSkeleton{
170
+ display:flex;
171
+ flex-shrink:0;
172
+ }
173
+
169
174
  .actions{
170
175
  display:flex;
171
176
  flex-shrink:0;
@@ -214,6 +219,9 @@
214
219
  .subcategoryTitle:hover .subcategoryTitleActions, .subcategoryTitle:focus-within .subcategoryTitleActions{
215
220
  visibility:visible;
216
221
  }
222
+ .subcategoryTitle[data-mobile] .subcategoryTitleActions{
223
+ visibility:visible;
224
+ }
217
225
 
218
226
  .subcategoryTitleRow{
219
227
  display:flex;