@cloud-ru/uikit-product-site-section 0.23.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 (120) hide show
  1. package/CHANGELOG.md +1406 -0
  2. package/LICENSE +201 -0
  3. package/README.md +335 -0
  4. package/package.json +70 -0
  5. package/src/components/SectionAccordion/SectionAccordion.tsx +99 -0
  6. package/src/components/SectionAccordion/index.ts +1 -0
  7. package/src/components/SectionAccordion/styles.module.scss +13 -0
  8. package/src/components/SectionAccordion/types.ts +11 -0
  9. package/src/components/SectionAccordion/utils.ts +29 -0
  10. package/src/components/SectionBasic/SectionBasic.tsx +157 -0
  11. package/src/components/SectionBasic/index.ts +1 -0
  12. package/src/components/SectionBasic/styles.module.scss +88 -0
  13. package/src/components/SectionBenefits/SectionBenefits.tsx +163 -0
  14. package/src/components/SectionBenefits/components/CardNumeric/CardNumeric.tsx +17 -0
  15. package/src/components/SectionBenefits/components/CardNumeric/index.ts +1 -0
  16. package/src/components/SectionBenefits/components/CardNumeric/styles.module.scss +6 -0
  17. package/src/components/SectionBenefits/components/index.ts +1 -0
  18. package/src/components/SectionBenefits/index.ts +1 -0
  19. package/src/components/SectionBenefits/styles.module.scss +15 -0
  20. package/src/components/SectionBenefits/types.ts +91 -0
  21. package/src/components/SectionBenefitsBanner/SectionBenefitsBanner.tsx +91 -0
  22. package/src/components/SectionBenefitsBanner/index.ts +1 -0
  23. package/src/components/SectionBenefitsBanner/styles.module.scss +119 -0
  24. package/src/components/SectionBenefitsBanner/types.ts +42 -0
  25. package/src/components/SectionBenefitsBanner/utils.ts +15 -0
  26. package/src/components/SectionBlogCarousel/SectionBlogCarousel.tsx +22 -0
  27. package/src/components/SectionBlogCarousel/index.ts +1 -0
  28. package/src/components/SectionBlogCarousel/utils.ts +29 -0
  29. package/src/components/SectionCaseCarousel/SectionCaseCarousel.tsx +22 -0
  30. package/src/components/SectionCaseCarousel/index.ts +1 -0
  31. package/src/components/SectionCaseCarousel/styles.module.scss +3 -0
  32. package/src/components/SectionCaseCarousel/utils.ts +24 -0
  33. package/src/components/SectionCatalog/SectionCatalog.tsx +105 -0
  34. package/src/components/SectionCatalog/constants.ts +5 -0
  35. package/src/components/SectionCatalog/index.ts +1 -0
  36. package/src/components/SectionCatalog/styles.module.scss +45 -0
  37. package/src/components/SectionClientsCarousel/SectionClientsCarousel.tsx +23 -0
  38. package/src/components/SectionClientsCarousel/index.ts +1 -0
  39. package/src/components/SectionClientsCarousel/utils.ts +29 -0
  40. package/src/components/SectionContent/SectionContent.tsx +98 -0
  41. package/src/components/SectionContent/index.ts +1 -0
  42. package/src/components/SectionContent/styles.module.scss +70 -0
  43. package/src/components/SectionContentList/SectionContentList.tsx +99 -0
  44. package/src/components/SectionContentList/index.ts +1 -0
  45. package/src/components/SectionContentList/styles.module.scss +56 -0
  46. package/src/components/SectionContentTabs/SectionContentTabs.tsx +156 -0
  47. package/src/components/SectionContentTabs/index.ts +1 -0
  48. package/src/components/SectionContentTabs/styles.module.scss +85 -0
  49. package/src/components/SectionExpertsCarousel/SectionExpertsCarousel.tsx +18 -0
  50. package/src/components/SectionExpertsCarousel/constants.ts +2 -0
  51. package/src/components/SectionExpertsCarousel/index.ts +1 -0
  52. package/src/components/SectionExpertsCarousel/types.ts +6 -0
  53. package/src/components/SectionLeading/SectionLeading.tsx +116 -0
  54. package/src/components/SectionLeading/index.ts +1 -0
  55. package/src/components/SectionLeading/styles.module.scss +30 -0
  56. package/src/components/SectionLeading/utils.ts +11 -0
  57. package/src/components/SectionMarketplaceCarousel/SectionMarketplaceCarousel.tsx +23 -0
  58. package/src/components/SectionMarketplaceCarousel/index.ts +1 -0
  59. package/src/components/SectionMarketplaceCarousel/utils.ts +33 -0
  60. package/src/components/SectionMedia/SectionMedia.tsx +57 -0
  61. package/src/components/SectionMedia/index.ts +1 -0
  62. package/src/components/SectionMedia/styles.module.scss +9 -0
  63. package/src/components/SectionPersonalManager/SectionPersonalManager.tsx +135 -0
  64. package/src/components/SectionPersonalManager/index.ts +1 -0
  65. package/src/components/SectionPersonalManager/styles.module.scss +146 -0
  66. package/src/components/SectionPersonalManager/utils.ts +15 -0
  67. package/src/components/SectionPromoList/SectionPromoList.tsx +66 -0
  68. package/src/components/SectionPromoList/index.ts +1 -0
  69. package/src/components/SectionPromoList/styles.module.scss +52 -0
  70. package/src/components/SectionSocial/SectionSocial.tsx +66 -0
  71. package/src/components/SectionSocial/constants.ts +5 -0
  72. package/src/components/SectionSocial/index.ts +1 -0
  73. package/src/components/SectionSocial/styles.module.scss +4 -0
  74. package/src/components/SectionTable/SectionTable.tsx +46 -0
  75. package/src/components/SectionTable/index.ts +1 -0
  76. package/src/components/SectionTable/styles.module.scss +19 -0
  77. package/src/components/index.ts +19 -0
  78. package/src/constants.ts +4 -0
  79. package/src/helperComponents/BenefitItem/BenefitItem.tsx +34 -0
  80. package/src/helperComponents/BenefitItem/index.ts +1 -0
  81. package/src/helperComponents/BenefitItem/styles.module.scss +34 -0
  82. package/src/helperComponents/BenefitItem/utils.ts +28 -0
  83. package/src/helperComponents/CardClient/CardClient.tsx +15 -0
  84. package/src/helperComponents/CardClient/index.ts +1 -0
  85. package/src/helperComponents/CardClient/styles.module.scss +25 -0
  86. package/src/helperComponents/CardLeading/CardLeading.tsx +29 -0
  87. package/src/helperComponents/CardLeading/index.ts +2 -0
  88. package/src/helperComponents/CardLeading/styles.module.scss +43 -0
  89. package/src/helperComponents/CardLeading/types.ts +12 -0
  90. package/src/helperComponents/CardLeading/utils.ts +28 -0
  91. package/src/helperComponents/CardSocial/CardSocial.tsx +105 -0
  92. package/src/helperComponents/CardSocial/index.ts +1 -0
  93. package/src/helperComponents/CardSocial/styles.module.scss +48 -0
  94. package/src/helperComponents/CardSocial/utils.ts +13 -0
  95. package/src/helperComponents/Expert/Expert.tsx +28 -0
  96. package/src/helperComponents/Expert/index.ts +1 -0
  97. package/src/helperComponents/Expert/styles.module.scss +39 -0
  98. package/src/helperComponents/PromoList/PromoList.tsx +26 -0
  99. package/src/helperComponents/PromoList/index.ts +1 -0
  100. package/src/helperComponents/PromoList/styles.module.scss +46 -0
  101. package/src/helperComponents/SectionButton/SectionButton.tsx +19 -0
  102. package/src/helperComponents/SectionButton/index.tsx +1 -0
  103. package/src/helperComponents/SectionButton/types.ts +9 -0
  104. package/src/helperComponents/SectionCarousel/SectionCarousel.tsx +109 -0
  105. package/src/helperComponents/SectionCarousel/index.ts +2 -0
  106. package/src/helperComponents/SectionCarousel/styles.module.scss +8 -0
  107. package/src/helperComponents/SectionCarousel/types.ts +42 -0
  108. package/src/helperComponents/SectionCarousel/utils.ts +10 -0
  109. package/src/helperComponents/SectionCatalogFooter/SectionCatalogFooter.tsx +29 -0
  110. package/src/helperComponents/SectionCatalogFooter/index.ts +1 -0
  111. package/src/helperComponents/SectionCatalogFooter/styles.module.scss +10 -0
  112. package/src/helperComponents/SectionTitle/SectionTitle.tsx +72 -0
  113. package/src/helperComponents/SectionTitle/constants.ts +5 -0
  114. package/src/helperComponents/SectionTitle/index.ts +1 -0
  115. package/src/helperComponents/SectionTitle/styles.module.scss +33 -0
  116. package/src/helperComponents/SectionTitle/types.ts +7 -0
  117. package/src/helperComponents/SectionTitle/utils.ts +83 -0
  118. package/src/helperComponents/index.ts +10 -0
  119. package/src/index.ts +2 -0
  120. package/src/types.ts +23 -0
@@ -0,0 +1,28 @@
1
+ import cn from 'classnames';
2
+
3
+ import { Typography } from '@snack-uikit/typography';
4
+
5
+ import { ExpertDetails } from '../../components/SectionExpertsCarousel/types';
6
+ import styles from './styles.module.scss';
7
+
8
+ export type ExpertProps = ExpertDetails & {
9
+ className?: string;
10
+ };
11
+
12
+ export function Expert({ image, name, surname, jobTitle, className }: ExpertProps) {
13
+ return (
14
+ <div className={cn(styles.expert, className)}>
15
+ <div className={styles.imageWrapper}>
16
+ <div className={styles.imageBackground}></div>
17
+ <img src={image} alt='expert' className={styles.image} />
18
+ </div>
19
+ <div className={styles.personalDetails}>
20
+ <Typography.SansTitleL className={styles.detailsColor}>{name}</Typography.SansTitleL>
21
+ <Typography.SansTitleL className={styles.detailsColor}>{surname}</Typography.SansTitleL>
22
+ </div>
23
+ <div className={styles.jobTitle}>
24
+ <Typography.SansBodyM className={styles.detailsColor}>{jobTitle}</Typography.SansBodyM>
25
+ </div>
26
+ </div>
27
+ );
28
+ }
@@ -0,0 +1 @@
1
+ export * from './Expert';
@@ -0,0 +1,39 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as var;
2
+
3
+ .expert {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: var.$dimension-1m;
7
+ overflow: hidden;
8
+ }
9
+
10
+ .imageWrapper {
11
+ display: flex;
12
+ justify-content: center;
13
+ position: relative;
14
+ }
15
+
16
+ .imageBackground {
17
+ position: absolute;
18
+ width: 100%;
19
+ height: 100%;
20
+ background-color: var.$sys-neutral-accent-default;
21
+ opacity: 0.08;
22
+ }
23
+
24
+ .personalDetails,
25
+ .jobTitle {
26
+ display: flex;
27
+ flex-direction: column;
28
+ }
29
+
30
+ .detailsColor {
31
+ color: var.$sys-neutral-text-main;
32
+ }
33
+
34
+ .image {
35
+ position: relative;
36
+ height: 290px;
37
+ object-fit: cover;
38
+ pointer-events: none;
39
+ }
@@ -0,0 +1,26 @@
1
+ import { DecorCheckedSVG } from '@cloud-ru/uikit-product-icons';
2
+ import { RichText } from '@cloud-ru/uikit-product-site-rich-text';
3
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
4
+
5
+ import styles from './styles.module.scss';
6
+
7
+ export type PromoListProps = { title: string; items: string[] };
8
+
9
+ export function PromoList({ title, items, layoutType }: WithLayoutType<PromoListProps>) {
10
+ return (
11
+ <div className={styles.promoList} data-layout-type={layoutType}>
12
+ <div className={styles.title} data-layout-type={layoutType} data-test-id='promo-list__card_title'>
13
+ <RichText richText={title} />
14
+ </div>
15
+
16
+ <ul className={styles.list}>
17
+ {items.map(item => (
18
+ <li className={styles.listItem} key={item}>
19
+ <DecorCheckedSVG className={styles.listItemIcon} />
20
+ <RichText richText={item} />
21
+ </li>
22
+ ))}
23
+ </ul>
24
+ </div>
25
+ );
26
+ }
@@ -0,0 +1 @@
1
+ export * from './PromoList';
@@ -0,0 +1,46 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as var;
2
+
3
+ .promoList {
4
+ display: flex;
5
+ flex-direction: column;
6
+ gap: 16px;
7
+ color: var.$sys-invert-neutral-text-main;
8
+
9
+ position: relative;
10
+
11
+ max-width: 480px;
12
+
13
+ &[data-layout-type='mobile'] {
14
+ max-width: 100%;
15
+ }
16
+ }
17
+
18
+ .title {
19
+ @include var.composite-var(var.$sans-headline-s);
20
+
21
+ &[data-layout-type='tablet'],
22
+ &[data-layout-type='mobile'] {
23
+ @include var.composite-var(var.$sans-title-l);
24
+ }
25
+ }
26
+
27
+ .list {
28
+ list-style: none;
29
+ padding: 0;
30
+ margin: 0;
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 16px;
34
+ }
35
+
36
+ .listItem {
37
+ @include var.composite-var(var.$sans-body-m);
38
+
39
+ display: flex;
40
+ gap: 8px;
41
+ }
42
+
43
+ .listItemIcon {
44
+ color: var.$sys-invert-neutral-accent-default;
45
+ flex-shrink: 0;
46
+ }
@@ -0,0 +1,19 @@
1
+ import { ArrowLinksSVG } from '@cloud-ru/uikit-product-icons';
2
+ import { ButtonFilled, ButtonOutline } from '@snack-uikit/button';
3
+ import { Link } from '@snack-uikit/link';
4
+
5
+ import { SectionButtonProps } from './types';
6
+
7
+ export function SectionButton(button: SectionButtonProps) {
8
+ const { type, ...buttonProps } = button;
9
+
10
+ if (button.href) {
11
+ return <Link key={button.label} {...buttonProps} text={button.label} size='l' insideText />;
12
+ }
13
+
14
+ if (type === 'outline') {
15
+ return <ButtonOutline key={button.label} size='l' appearance='neutral' icon={<ArrowLinksSVG />} {...buttonProps} />;
16
+ }
17
+
18
+ return <ButtonFilled key={button.label} size='l' {...buttonProps} />;
19
+ }
@@ -0,0 +1 @@
1
+ export * from './SectionButton';
@@ -0,0 +1,9 @@
1
+ import { ButtonFilledProps } from '@snack-uikit/button';
2
+
3
+ export type SectionButtonProps = {
4
+ label: string;
5
+ href?: string;
6
+ target?: ButtonFilledProps['target'];
7
+ onClick?: ButtonFilledProps['onClick'];
8
+ type?: 'brand' | 'outline';
9
+ };
@@ -0,0 +1,109 @@
1
+ import debounce from 'lodash.debounce';
2
+ import { useEffect, useMemo, useRef, useState } from 'react';
3
+
4
+ import { extractSupportProps } from '@cloud-ru/uikit-product-utils';
5
+ import { Carousel } from '@snack-uikit/carousel';
6
+
7
+ import { SectionBasic } from '../../components';
8
+ import { SECTION_COLORS } from '../../constants';
9
+ import styles from './styles.module.scss';
10
+ import { SectionCarouselProps } from './types';
11
+ import { calculateAmountOfItemsPerPage } from './utils';
12
+
13
+ const MOBILE_LAYOUTS = ['tablet', 'mobile'];
14
+
15
+ export function SectionCarousel({
16
+ id,
17
+ title,
18
+ titleTag,
19
+ subtitle,
20
+ subtitleTag,
21
+ titleSectionSize,
22
+ description,
23
+ children,
24
+ itemMinWidth,
25
+ maxItemsPerPage,
26
+ backgroundColor = SECTION_COLORS.NeutralBackground1Level,
27
+ className,
28
+ layoutType,
29
+ gap,
30
+ moreButton,
31
+ autoSwipe = 9,
32
+ infiniteScroll = true,
33
+ ...rest
34
+ }: SectionCarouselProps) {
35
+ const [currentPage, setCurrentPage] = useState<number>(0);
36
+ const [itemsPerPageAmount, setItemsPerPageAmount] = useState<number>(0);
37
+
38
+ const wrapperRef = useRef<HTMLDivElement>(null);
39
+
40
+ useEffect(() => {
41
+ const node = wrapperRef.current;
42
+ if (!node) {
43
+ return;
44
+ }
45
+
46
+ const calculateAmountOfItems = () => {
47
+ const wrapperWidth = wrapperRef.current?.offsetWidth;
48
+ if (!wrapperWidth) {
49
+ return;
50
+ }
51
+
52
+ setItemsPerPageAmount(
53
+ calculateAmountOfItemsPerPage({
54
+ wrapperWidth,
55
+ itemMinWidth,
56
+ maxItemsPerPage,
57
+ }),
58
+ );
59
+ };
60
+
61
+ const observer = new ResizeObserver(debounce(calculateAmountOfItems, 100));
62
+ observer.observe(node);
63
+ return () => observer.disconnect();
64
+ }, [itemMinWidth, maxItemsPerPage]);
65
+
66
+ const showArrows = useMemo(() => {
67
+ if (MOBILE_LAYOUTS.includes(layoutType)) {
68
+ return false;
69
+ }
70
+
71
+ return children.length > itemsPerPageAmount;
72
+ }, [children.length, itemsPerPageAmount, layoutType]);
73
+
74
+ return (
75
+ <SectionBasic
76
+ id={id}
77
+ layoutType={layoutType}
78
+ className={className}
79
+ data-section-background={backgroundColor}
80
+ title={title}
81
+ titleTag={titleTag}
82
+ subtitle={subtitle}
83
+ subtitleTag={subtitleTag}
84
+ titleSectionSize={titleSectionSize}
85
+ description={description}
86
+ backgroundColor={backgroundColor}
87
+ moreButton={moreButton}
88
+ {...extractSupportProps(rest)}
89
+ >
90
+ <div ref={wrapperRef} className={styles.sectionCarousel} data-layout-type={layoutType}>
91
+ {itemsPerPageAmount > 0 && (
92
+ <Carousel
93
+ state={{ page: currentPage, onChange: setCurrentPage }}
94
+ arrows={showArrows}
95
+ controlsVisibility='always'
96
+ showItems={itemsPerPageAmount}
97
+ pagination={children.length > itemsPerPageAmount}
98
+ swipe={children.length > itemsPerPageAmount}
99
+ gap={gap}
100
+ autoSwipe={infiniteScroll ? autoSwipe : undefined}
101
+ infiniteScroll={infiniteScroll}
102
+ >
103
+ {children}
104
+ </Carousel>
105
+ )}
106
+ </div>
107
+ </SectionBasic>
108
+ );
109
+ }
@@ -0,0 +1,2 @@
1
+ export * from './SectionCarousel';
2
+ export { type SectionCarouselProps, type LimitedSectionCarouselProps } from './types';
@@ -0,0 +1,8 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as var;
2
+
3
+ .sectionCarousel {
4
+ width: 100%;
5
+
6
+ display: flex;
7
+ flex-direction: column;
8
+ }
@@ -0,0 +1,42 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { WithLayoutType, WithSupportProps } from '@cloud-ru/uikit-product-utils';
4
+ import { CarouselProps } from '@snack-uikit/carousel';
5
+
6
+ import { SectionBasicProps } from '../../components';
7
+ import { SectionColor } from '../../types';
8
+ import { SectionTitleProps } from '../SectionTitle';
9
+
10
+ type CarouselSlideConfig = {
11
+ /** Массив повторяющихся айтмов, из которых состоят слайды карусели */
12
+ children: ReactElement[];
13
+ /** Минимальная ширина айтема */
14
+ itemMinWidth: number;
15
+ /** Максимальное количество айтемов на слайде */
16
+ maxItemsPerPage: number;
17
+ };
18
+
19
+ export type SectionCarouselProps = WithSupportProps<
20
+ WithLayoutType<
21
+ SectionTitleProps &
22
+ CarouselSlideConfig & {
23
+ /** id секции */
24
+ id?: string;
25
+ /** Цвет фона */
26
+ backgroundColor?: SectionColor;
27
+ /** CSS - класснейм */
28
+ className?: string;
29
+ /**
30
+ * Автоматическое переключение слайдов в секундах
31
+ * @default 9
32
+ */
33
+ autoSwipe?: number;
34
+ } & Pick<CarouselProps, 'gap' | 'autoSwipe' | 'infiniteScroll'> &
35
+ Pick<SectionBasicProps, 'moreButton'>
36
+ >
37
+ >;
38
+
39
+ export type LimitedSectionCarouselProps<T extends object> = Omit<SectionCarouselProps, keyof CarouselSlideConfig> & {
40
+ /** Массив айтемов */
41
+ items: T[];
42
+ };
@@ -0,0 +1,10 @@
1
+ import { SectionCarouselProps } from './types';
2
+
3
+ type Props = Pick<SectionCarouselProps, 'itemMinWidth' | 'maxItemsPerPage'> & {
4
+ wrapperWidth: number;
5
+ };
6
+
7
+ export const calculateAmountOfItemsPerPage = ({ wrapperWidth, itemMinWidth, maxItemsPerPage }: Props): number => {
8
+ const amount = Math.floor(wrapperWidth / itemMinWidth);
9
+ return amount > maxItemsPerPage ? maxItemsPerPage : amount;
10
+ };
@@ -0,0 +1,29 @@
1
+ import { ChevronRightSVG } from '@cloud-ru/uikit-product-icons';
2
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
3
+ import { ButtonFunction, ButtonFunctionProps } from '@snack-uikit/button';
4
+
5
+ import styles from './styles.module.scss';
6
+
7
+ type FooterItem = {
8
+ label: string;
9
+ href?: string;
10
+ onClick?: ButtonFunctionProps['onClick'];
11
+ };
12
+
13
+ export type SectionCatalogFooterProps = WithLayoutType<{
14
+ items: FooterItem[];
15
+ }>;
16
+
17
+ export function SectionCatalogFooter({ items, layoutType }: SectionCatalogFooterProps) {
18
+ if (items.length === 0) {
19
+ return null;
20
+ }
21
+
22
+ return (
23
+ <div className={styles.footer} data-layout-type={layoutType}>
24
+ {items.map(({ label, onClick, href }) => (
25
+ <ButtonFunction key={label} label={label} size='m' onClick={onClick} href={href} icon={<ChevronRightSVG />} />
26
+ ))}
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1 @@
1
+ export * from './SectionCatalogFooter';
@@ -0,0 +1,10 @@
1
+ .footer {
2
+ display: flex;
3
+ flex-direction: column;
4
+ padding-left: 8px;
5
+
6
+ &[data-layout-type='tablet'],
7
+ &[data-layout-type='mobile'] {
8
+ padding-left: 0;
9
+ }
10
+ }
@@ -0,0 +1,72 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { RichText } from '@cloud-ru/uikit-product-site-rich-text';
4
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
5
+ import { Link, PickLinkProps } from '@snack-uikit/link';
6
+ import { Typography } from '@snack-uikit/typography';
7
+
8
+ import styles from './styles.module.scss';
9
+ import { SectionTag, Size } from './types';
10
+ import { getTitleTypographyProps } from './utils';
11
+
12
+ export type SectionTitleProps = WithLayoutType<{
13
+ /** Название секции */
14
+ title?: string;
15
+ /** Подзаголовок секции */
16
+ subtitle?: string;
17
+ /** Ссылка в названии */
18
+ titleLink?: PickLinkProps<typeof Link, 'href' | 'onClick' | 'target' | 'appearance' | 'textMode'>;
19
+ /** Описание секции */
20
+ description?: string;
21
+ /** Размер шрифтов заголовка и подзаголовка */
22
+ titleSectionSize?: Size;
23
+ /** Тег заголовка */
24
+ titleTag?: SectionTag;
25
+ /** Тег подзаголовка */
26
+ subtitleTag?: SectionTag;
27
+ /** Выравнивание текста */
28
+ titleAlign?: 'left' | 'center';
29
+ }>;
30
+
31
+ export function SectionTitle({
32
+ title,
33
+ subtitle,
34
+ titleLink,
35
+ description,
36
+ titleSectionSize = 'm',
37
+ titleTag = 'h2',
38
+ subtitleTag = 'h3',
39
+ layoutType,
40
+ titleAlign = 'left',
41
+ }: SectionTitleProps) {
42
+ const titleProps = useMemo(
43
+ () => getTitleTypographyProps({ titleSectionSize, layoutType }),
44
+ [layoutType, titleSectionSize],
45
+ );
46
+
47
+ return (
48
+ <>
49
+ {(title || description) && (
50
+ <div className={styles.sectionTitle} data-align={titleAlign}>
51
+ <div className={styles.titlesWrapper} data-align={titleAlign}>
52
+ {title && (
53
+ <Typography family='sans' {...titleProps} tag={titleTag} className={styles.title}>
54
+ {titleLink ? <Link {...titleLink} {...titleProps} text={title} /> : <RichText richText={title} />}
55
+ </Typography>
56
+ )}
57
+ {subtitle && (
58
+ <Typography family='sans' purpose='label' size='l' tag={subtitleTag} className={styles.subtitle}>
59
+ <RichText richText={subtitle} />
60
+ </Typography>
61
+ )}
62
+ </div>
63
+ {description && (
64
+ <Typography.SansBodyL className={styles.description}>
65
+ <RichText richText={description} />
66
+ </Typography.SansBodyL>
67
+ )}
68
+ </div>
69
+ )}
70
+ </>
71
+ );
72
+ }
@@ -0,0 +1,5 @@
1
+ export const SIZE = {
2
+ S: 's',
3
+ M: 'm',
4
+ L: 'l',
5
+ } as const;
@@ -0,0 +1 @@
1
+ export * from './SectionTitle';
@@ -0,0 +1,33 @@
1
+ @use '@sbercloud/figma-tokens-cloud-platform/build/scss/styles-theme-variables' as var;
2
+
3
+ .sectionTitle,
4
+ .titlesWrapper {
5
+ display: flex;
6
+ gap: var.$dimension-1m;
7
+ text-align: left;
8
+
9
+ &[data-align='center'] {
10
+ text-align: center;
11
+ }
12
+ }
13
+
14
+ .sectionTitle {
15
+ flex-direction: column;
16
+ }
17
+
18
+ .titlesWrapper {
19
+ flex-direction: column-reverse;
20
+ }
21
+
22
+ .title {
23
+ color: var.$sys-neutral-text-main;
24
+ white-space: pre-line;
25
+ }
26
+
27
+ .subtitle {
28
+ color: var.$sys-neutral-text-light;
29
+ }
30
+
31
+ .description {
32
+ color: var.$sys-neutral-text-support;
33
+ }
@@ -0,0 +1,7 @@
1
+ import { Tag } from '@snack-uikit/typography';
2
+ import { ValueOf } from '@snack-uikit/utils';
3
+
4
+ import { SIZE } from './constants';
5
+
6
+ export type Size = ValueOf<typeof SIZE>;
7
+ export type SectionTag = Extract<Tag, 'h2' | 'h3' | 'h4'>;
@@ -0,0 +1,83 @@
1
+ import { WithLayoutType } from '@cloud-ru/uikit-product-utils';
2
+ import { TypographyProps } from '@snack-uikit/typography';
3
+
4
+ import { Size } from './types';
5
+
6
+ type Props = WithLayoutType<{
7
+ titleSectionSize: Size;
8
+ }>;
9
+
10
+ export const getTitleTypographyProps = ({
11
+ layoutType,
12
+ titleSectionSize,
13
+ }: Props): Pick<TypographyProps, 'size' | 'purpose'> => {
14
+ switch (layoutType) {
15
+ case 'tablet':
16
+ switch (titleSectionSize) {
17
+ case 'l':
18
+ return {
19
+ purpose: 'headline',
20
+ size: 'l',
21
+ };
22
+
23
+ case 's':
24
+ return {
25
+ purpose: 'title',
26
+ size: 'l',
27
+ };
28
+
29
+ case 'm':
30
+ default:
31
+ return {
32
+ purpose: 'headline',
33
+ size: 'm',
34
+ };
35
+ }
36
+
37
+ case 'mobile':
38
+ switch (titleSectionSize) {
39
+ case 'l':
40
+ return {
41
+ purpose: 'headline',
42
+ size: 'm',
43
+ };
44
+
45
+ case 's':
46
+ return {
47
+ purpose: 'title',
48
+ size: 'm',
49
+ };
50
+
51
+ case 'm':
52
+ default:
53
+ return {
54
+ purpose: 'headline',
55
+ size: 's',
56
+ };
57
+ }
58
+
59
+ case 'desktop':
60
+ case 'desktopSmall':
61
+ default:
62
+ switch (titleSectionSize) {
63
+ case 'l':
64
+ return {
65
+ purpose: 'display',
66
+ size: 'm',
67
+ };
68
+
69
+ case 's':
70
+ return {
71
+ purpose: 'headline',
72
+ size: 's',
73
+ };
74
+
75
+ case 'm':
76
+ default:
77
+ return {
78
+ purpose: 'headline',
79
+ size: 'l',
80
+ };
81
+ }
82
+ }
83
+ };
@@ -0,0 +1,10 @@
1
+ export * from './CardLeading';
2
+ export * from './CardSocial';
3
+ export * from './Expert';
4
+ export * from './PromoList';
5
+ export * from './SectionCatalogFooter';
6
+ export * from './SectionTitle';
7
+ export * from './SectionCarousel';
8
+ export * from './CardClient';
9
+ export * from './BenefitItem';
10
+ export * from './SectionButton';
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './components';
2
+ export * from './helperComponents/SectionCarousel';
package/src/types.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { SiteVideoProps } from '@cloud-ru/uikit-product-site-media';
2
+ import { ValueOf } from '@snack-uikit/utils';
3
+
4
+ import { SECTION_COLORS } from './constants';
5
+
6
+ export type SectionColor = ValueOf<typeof SECTION_COLORS>;
7
+
8
+ type MediaVideoProps = Pick<SiteVideoProps, 'video' | 'onPlay' | 'onError'> & {
9
+ image?: never;
10
+ };
11
+
12
+ type MediaImageProps = {
13
+ video?: never;
14
+ onPlay?: never;
15
+ onError?: never;
16
+ /** Ссылка на изображение */
17
+ image: {
18
+ src: string;
19
+ alt?: string;
20
+ };
21
+ };
22
+
23
+ export type MediaContentProps = MediaImageProps | MediaVideoProps;