@cloud-ru/ds-uikit-product-modal-predefined 1.0.23-preview-a0540d7f.0 → 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/dist/cjs/helperComponents/DesktopReleaseNotes/DesktopReleaseNotes.js +3 -1
- package/dist/cjs/helperComponents/DesktopReleaseNotes/styles.module.css +4 -0
- package/dist/cjs/helperComponents/NoteItem/NoteItem.js +1 -1
- package/dist/cjs/helperComponents/NoteItem/NoteItemSkeleton.js +2 -1
- package/dist/cjs/helperComponents/NoteItem/styles.module.css +10 -2
- package/dist/cjs/helperComponents/ReleaseNotesContent/ReleaseNotesContent.js +6 -2
- package/dist/cjs/helperComponents/ReleaseNotesContent/styles.module.css +1 -1
- package/dist/cjs/helperComponents/ReleaseNotesFooter/ReleaseNotesFooter.js +1 -2
- package/dist/cjs/style.css +15 -3
- package/dist/esm/helperComponents/DesktopReleaseNotes/DesktopReleaseNotes.js +3 -1
- package/dist/esm/helperComponents/DesktopReleaseNotes/styles.module.css +4 -0
- package/dist/esm/helperComponents/NoteItem/NoteItem.js +2 -2
- package/dist/esm/helperComponents/NoteItem/NoteItemSkeleton.js +2 -1
- package/dist/esm/helperComponents/NoteItem/styles.module.css +10 -2
- package/dist/esm/helperComponents/ReleaseNotesContent/ReleaseNotesContent.js +6 -2
- package/dist/esm/helperComponents/ReleaseNotesContent/styles.module.css +1 -1
- package/dist/esm/helperComponents/ReleaseNotesFooter/ReleaseNotesFooter.js +1 -2
- package/dist/esm/style.css +15 -3
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +19 -19
- package/src/helperComponents/DesktopReleaseNotes/DesktopReleaseNotes.tsx +4 -1
- package/src/helperComponents/DesktopReleaseNotes/styles.module.scss +5 -0
- package/src/helperComponents/NoteItem/NoteItem.tsx +8 -6
- package/src/helperComponents/NoteItem/NoteItemSkeleton.tsx +3 -2
- package/src/helperComponents/NoteItem/styles.module.scss +15 -4
- package/src/helperComponents/ReleaseNotesContent/ReleaseNotesContent.tsx +12 -2
- package/src/helperComponents/ReleaseNotesContent/styles.module.scss +2 -1
- package/src/helperComponents/ReleaseNotesFooter/ReleaseNotesFooter.tsx +3 -4
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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.0.24 (2026-09-14)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @ds/uikit-product-modal-predefined
|
|
9
|
+
|
|
10
|
+
## 1.0.23 (2026-09-14)
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
- **FF-8945:** reserve scrollbar gutter in ReleaseNotes modal content ([76adb63](https://github.com/cloud-ru-tech/snack-v2/commit/76adb638a3c5323e4c8b50568a7341aa6a6a7be6))
|
|
15
|
+
|
|
6
16
|
## 1.0.22 (2026-09-11)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @ds/uikit-product-modal-predefined
|
|
@@ -17,5 +17,7 @@ function DesktopReleaseNotes({ open, onClose, closeOnPopstate, contentState = co
|
|
|
17
17
|
const { t } = locale_1.modalPredefinedLocale.useTranslations();
|
|
18
18
|
const { pageIndex, readablePageNumber, setPage, handleClose, handleReadLaterClick, handleNextPageClick, handlePrevPageClick, } = (0, hooks_1.useReleaseNotesNavigation)({ onClose, onReadLaterClick, onSlideChange });
|
|
19
19
|
const shouldShowFooter = contentState === constants_1.CONTENT_STATE.Data && (loading || items.length > 0);
|
|
20
|
-
return ((0, jsx_runtime_1.jsxs)(modal_1.ModalCustom, { open: open, onClose: handleClose, closeOnPopstate: closeOnPopstate, width: modal_1.WIDTH.M, "data-content-state": contentState, "data-surface": 'modal', ...(0, utils_1.extractSupportProps)(rest), children: [(0, jsx_runtime_1.jsx)(modal_1.ModalCustom.Header, { title: t('releaseNotes.title') }), (0, jsx_runtime_1.jsx)(modal_1.ModalCustom.Body, { className: styles_module_scss_1.default.body, content: (0, jsx_runtime_1.jsx)(ReleaseNotesContent_1.ReleaseNotesContent, { contentState: contentState, items: items, loading: loading, pageIndex: pageIndex, surface: 'modal', noDataTitle: t('releaseNotes.noDataTitle'), noDataDescription: t('releaseNotes.noDataDescription'), errorTitle: t('releaseNotes.errorTitle'), errorDescription: t('releaseNotes.errorDescription'), retryLabel: t('releaseNotes.retry'), onPageChange: setPage, onDataErrorRetryClick: onDataErrorRetryClick }) }), shouldShowFooter
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)(modal_1.ModalCustom, { open: open, onClose: handleClose, closeOnPopstate: closeOnPopstate, width: modal_1.WIDTH.M, "data-content-state": contentState, "data-surface": 'modal', ...(0, utils_1.extractSupportProps)(rest), children: [(0, jsx_runtime_1.jsx)(modal_1.ModalCustom.Header, { title: t('releaseNotes.title') }), (0, jsx_runtime_1.jsx)(modal_1.ModalCustom.Body, { className: styles_module_scss_1.default.body, content: (0, jsx_runtime_1.jsx)(ReleaseNotesContent_1.ReleaseNotesContent, { contentState: contentState, items: items, loading: loading, pageIndex: pageIndex, surface: 'modal', noDataTitle: t('releaseNotes.noDataTitle'), noDataDescription: t('releaseNotes.noDataDescription'), errorTitle: t('releaseNotes.errorTitle'), errorDescription: t('releaseNotes.errorDescription'), retryLabel: t('releaseNotes.retry'), onPageChange: setPage, onDataErrorRetryClick: onDataErrorRetryClick }) }), shouldShowFooter ? ((0, jsx_runtime_1.jsx)(modal_1.ModalCustom.Footer, { children: (0, jsx_runtime_1.jsx)(ReleaseNotesFooter_1.ReleaseNotesFooter, { surface: 'modal', loading: loading, total: items.length, pageIndex: pageIndex, readablePageNumber: readablePageNumber, readLaterLabel: t('releaseNotes.readLater'), onPageChange: setPage, onReadLaterClick: handleReadLaterClick, onPrevPageClick: handlePrevPageClick, onNextPageClick: () => handleNextPageClick(items.length) }) })) : (
|
|
21
|
+
// Нижнюю safe-area окна несёт футер; без него отступ рисуется здесь, как в `DesktopModal`.
|
|
22
|
+
(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.safeAreaBottom }))] }));
|
|
21
23
|
}
|
|
@@ -42,5 +42,5 @@ function NoteItemMedia({ image, video }) {
|
|
|
42
42
|
status !== constants_1.MEDIA_STATUS.Error && ((0, jsx_runtime_1.jsx)("img", { ref: handleImageRef, className: styles_module_scss_1.default.file, src: image.src, alt: image.alt, onLoad: handleImageLoad, onError: handleImageError, "data-test-id": constants_1.TEST_IDS.releaseNotesImage }))), status === constants_1.MEDIA_STATUS.Pending && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.mediaOverlay, "aria-hidden": true, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { loading: true, width: '100%', height: '100%' }) })), status === constants_1.MEDIA_STATUS.Error && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.mediaOverlay, role: 'img', "aria-label": image.alt, "data-test-id": constants_1.TEST_IDS.releaseNotesMediaFallback, children: (0, jsx_runtime_1.jsx)(system_1.ImageSVG, { className: styles_module_scss_1.default.mediaFallbackIcon, size: FALLBACK_ICON_SIZE }) }))] }));
|
|
43
43
|
}
|
|
44
44
|
function NoteItem({ title, description, image, video, surface = constants_1.SURFACE.Modal }) {
|
|
45
|
-
return ((0, jsx_runtime_1.jsxs)("article", { className: styles_module_scss_1.default.root, "data-surface": surface, "data-test-id": constants_1.TEST_IDS.releaseNotesItem, children: [(0, jsx_runtime_1.jsx)(NoteItemMedia, { image: image, video: video }, `${video ?? ''}|${image.src}`), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.content, children: [(0, jsx_runtime_1.jsx)(typography_1.Typography, { className: styles_module_scss_1.default.title, variant: 'title', size: 'l', children: title }), (0, jsx_runtime_1.jsx)(scroll_1.Scroll, { className: styles_module_scss_1.default.description, barHideStrategy: scroll_1.BAR_HIDE_STRATEGY.Leave, children: (0, jsx_runtime_1.jsx)(markdown_1.Markdown, { value: description }) })] })] }));
|
|
45
|
+
return ((0, jsx_runtime_1.jsxs)("article", { className: styles_module_scss_1.default.root, "data-surface": surface, "data-test-id": constants_1.TEST_IDS.releaseNotesItem, children: [(0, jsx_runtime_1.jsx)(NoteItemMedia, { image: image, video: video }, `${video ?? ''}|${image.src}`), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.content, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.titleWrapper, children: (0, jsx_runtime_1.jsx)(typography_1.Typography, { className: styles_module_scss_1.default.title, variant: 'title', size: 'l', children: title }) }), (0, jsx_runtime_1.jsx)(scroll_1.Scroll, { className: styles_module_scss_1.default.description, size: scroll_1.SIZE.S, barHideStrategy: scroll_1.BAR_HIDE_STRATEGY.Leave, children: (0, jsx_runtime_1.jsx)(markdown_1.Markdown, { value: description, className: styles_module_scss_1.default.markdown }) })] })] }));
|
|
46
46
|
}
|
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.NoteItemSkeleton = NoteItemSkeleton;
|
|
7
7
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
8
|
const skeleton_1 = require("@cloud-ru/ds-skeleton");
|
|
9
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
9
10
|
const constants_1 = require("../../constants");
|
|
10
11
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
11
12
|
/** Строки скелетона текста: заголовок и абзацы описания — по структуре типовой новости. */
|
|
@@ -16,5 +17,5 @@ const PARAGRAPH_LINES = [2, 3, 3];
|
|
|
16
17
|
* поэтому переход `loading → data` не меняет геометрию.
|
|
17
18
|
*/
|
|
18
19
|
function NoteItemSkeleton({ surface = constants_1.SURFACE.Modal }) {
|
|
19
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-surface": surface, "aria-hidden": true, "data-test-id": constants_1.TEST_IDS.releaseNotesSkeleton, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.media, "data-test-id": constants_1.TEST_IDS.releaseNotesMedia, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { loading: true, width: '100%', height: '100%' }) }), (0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.content, children: [(0, jsx_runtime_1.jsx)(skeleton_1.SkeletonText, { loading: true, lines: TITLE_LINES, variant: '
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-surface": surface, "aria-hidden": true, "data-test-id": constants_1.TEST_IDS.releaseNotesSkeleton, children: [(0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.media, "data-test-id": constants_1.TEST_IDS.releaseNotesMedia, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { loading: true, width: '100%', height: '100%' }) }), (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.content, styles_module_scss_1.default.skeletonContent), children: [(0, jsx_runtime_1.jsx)(skeleton_1.SkeletonText, { loading: true, lines: TITLE_LINES, variant: 'title', size: 'l' }), PARAGRAPH_LINES.map((lines, index) => ((0, jsx_runtime_1.jsx)(skeleton_1.SkeletonText, { loading: true, lines: lines }, index)))] })] }));
|
|
20
21
|
}
|
|
@@ -23,7 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.root[data-surface=bottomSheet] .media{
|
|
26
|
+
justify-self:center;
|
|
26
27
|
aspect-ratio:1;
|
|
28
|
+
width:100%;
|
|
29
|
+
max-width:calc(6 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
.file{
|
|
@@ -49,7 +52,7 @@
|
|
|
49
52
|
.content{
|
|
50
53
|
display:flex;
|
|
51
54
|
flex-direction:column;
|
|
52
|
-
row-gap:var(--sn-primitive-dimension-
|
|
55
|
+
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
53
56
|
min-width:0;
|
|
54
57
|
min-height:0;
|
|
55
58
|
}
|
|
@@ -64,10 +67,15 @@
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
.root[data-surface=modal] .content{
|
|
67
|
-
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
68
70
|
order:1;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
.root[data-surface=modal] .markdown,
|
|
74
|
+
.root[data-surface=modal] .titleWrapper,
|
|
75
|
+
.root[data-surface=modal] .skeletonContent{
|
|
76
|
+
padding-right:var(--sn-primitive-dimension-16, 16px);
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
.root[data-surface=modal] .media{
|
|
72
80
|
order:2;
|
|
73
81
|
}
|
|
@@ -9,10 +9,14 @@ const button_1 = require("@cloud-ru/ds-button");
|
|
|
9
9
|
const carousel_1 = require("@cloud-ru/ds-carousel");
|
|
10
10
|
const system_1 = require("@cloud-ru/ds-icons/interface/system");
|
|
11
11
|
const info_block_1 = require("@cloud-ru/ds-info-block");
|
|
12
|
+
const typography_1 = require("@cloud-ru/ds-typography");
|
|
12
13
|
const classnames_1 = __importDefault(require("classnames"));
|
|
13
14
|
const constants_1 = require("../../constants");
|
|
14
15
|
const NoteItem_1 = require("../NoteItem");
|
|
15
16
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
17
|
+
function InfoBlockDescription({ children }) {
|
|
18
|
+
return ((0, jsx_runtime_1.jsx)(typography_1.Typography, { variant: 'body', size: 'm', children: children }));
|
|
19
|
+
}
|
|
16
20
|
function ReleaseNotesContent({ contentState, items, loading, pageIndex, surface, noDataTitle, noDataDescription, errorTitle, errorDescription, retryLabel, onPageChange, onDataErrorRetryClick, }) {
|
|
17
21
|
const total = items.length;
|
|
18
22
|
const hasItems = total > 0;
|
|
@@ -20,10 +24,10 @@ function ReleaseNotesContent({ contentState, items, loading, pageIndex, surface,
|
|
|
20
24
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.root, styles_module_scss_1.default[surface]), children: (0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.skeletonSlide, children: (0, jsx_runtime_1.jsx)(NoteItem_1.NoteItemSkeleton, { surface: surface }) }) }));
|
|
21
25
|
}
|
|
22
26
|
if (contentState === constants_1.CONTENT_STATE.Error) {
|
|
23
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.stateWrapper, "data-surface": surface, children: (0, jsx_runtime_1.jsx)(info_block_1.InfoBlock, { className: styles_module_scss_1.default.state, size: 'l', title: errorTitle, content: errorDescription, icon: { icon: system_1.CrossSVG, appearance: 'neutral', background: true }, "data-test-id": constants_1.TEST_IDS.releaseNotesError, footer: (0, jsx_runtime_1.jsx)(button_1.Button, { view: 'filled', appearance: 'neutral', size: 'l', label: retryLabel, onClick: onDataErrorRetryClick, "data-test-id": constants_1.TEST_IDS.releaseNotesRetryButton }) }) }));
|
|
27
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.stateWrapper, "data-surface": surface, children: (0, jsx_runtime_1.jsx)(info_block_1.InfoBlock, { className: styles_module_scss_1.default.state, size: 'l', title: errorTitle, content: (0, jsx_runtime_1.jsx)(InfoBlockDescription, { children: errorDescription }), icon: { icon: system_1.CrossSVG, appearance: 'neutral', background: true }, "data-test-id": constants_1.TEST_IDS.releaseNotesError, footer: (0, jsx_runtime_1.jsx)(button_1.Button, { view: 'filled', appearance: 'neutral', size: 'l', label: retryLabel, onClick: onDataErrorRetryClick, "data-test-id": constants_1.TEST_IDS.releaseNotesRetryButton }) }) }));
|
|
24
28
|
}
|
|
25
29
|
if (contentState === constants_1.CONTENT_STATE.NoData || !hasItems) {
|
|
26
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.stateWrapper, "data-surface": surface, children: (0, jsx_runtime_1.jsx)(info_block_1.InfoBlock, { className: styles_module_scss_1.default.state, size: 'l', title: noDataTitle, content: noDataDescription, "data-test-id": constants_1.TEST_IDS.releaseNotesNoData }) }));
|
|
30
|
+
return ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.stateWrapper, "data-surface": surface, children: (0, jsx_runtime_1.jsx)(info_block_1.InfoBlock, { className: styles_module_scss_1.default.state, size: 'l', title: noDataTitle, content: (0, jsx_runtime_1.jsx)(InfoBlockDescription, { children: noDataDescription }), "data-test-id": constants_1.TEST_IDS.releaseNotesNoData }) }));
|
|
27
31
|
}
|
|
28
32
|
return ((0, jsx_runtime_1.jsx)("div", { className: (0, classnames_1.default)(styles_module_scss_1.default.root, styles_module_scss_1.default[surface]), children: (0, jsx_runtime_1.jsx)(carousel_1.Carousel, { arrows: false, pagination: false, swipe: true, state: { page: pageIndex, onChange: onPageChange }, className: styles_module_scss_1.default.carousel, children: items.map(item => ((0, jsx_runtime_1.jsx)(NoteItem_1.NoteItem, { ...item, surface: surface }, `${item.title}-${item.image.src}`))) }) }));
|
|
29
33
|
}
|
|
@@ -16,7 +16,6 @@ function ReleaseNotesFooter({ surface, total, loading, pageIndex, readablePageNu
|
|
|
16
16
|
// появляются рывком поверх уже отрисованного контента.
|
|
17
17
|
const hasPagination = loading || total > 1;
|
|
18
18
|
const isModal = surface === constants_1.SURFACE.Modal;
|
|
19
|
-
const buttonSize = isModal ? 'm' : 'l';
|
|
20
19
|
const handleSliderChange = (page) => onPageChange(page - 1);
|
|
21
|
-
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-surface": surface, children: [(0, jsx_runtime_1.jsx)(button_1.Button, { view: 'function', appearance: 'neutral', size:
|
|
20
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.root, "data-surface": surface, children: [(0, jsx_runtime_1.jsx)(button_1.Button, { view: 'function', appearance: 'neutral', size: 'm', label: readLaterLabel, "data-test-id": constants_1.TEST_IDS.releaseNotesReadLaterButton, ...readLaterButtonProps, onClick: onReadLaterClick }), isModal && hasPagination && ((0, jsx_runtime_1.jsx)("div", { className: styles_module_scss_1.default.slider, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { loading: loading, className: styles_module_scss_1.default.sliderSkeleton, children: (0, jsx_runtime_1.jsx)(pagination_1.PaginationSlider, { size: pagination_1.PAGINATION_SLIDER_SIZE.Xs, total: total, page: readablePageNumber, onChange: handleSliderChange, "data-test-id": constants_1.TEST_IDS.releaseNotesPaginationSlider }) }) })), hasPagination && ((0, jsx_runtime_1.jsxs)("div", { className: styles_module_scss_1.default.controls, children: [(0, jsx_runtime_1.jsx)(button_1.Button, { view: 'outline', appearance: 'neutral', size: 'm', icon: (0, jsx_runtime_1.jsx)(system_1.ChevronLeftSVG, {}), disabled: loading || pageIndex === 0, onClick: onPrevPageClick, "data-test-id": constants_1.TEST_IDS.releaseNotesPrevButton }), (0, jsx_runtime_1.jsx)(button_1.Button, { view: 'outline', appearance: 'neutral', size: 'm', icon: (0, jsx_runtime_1.jsx)(system_1.ChevronRightSVG, {}), disabled: loading || readablePageNumber === total, onClick: onNextPageClick, "data-test-id": constants_1.TEST_IDS.releaseNotesNextButton })] }))] }));
|
|
22
21
|
}
|
package/dist/cjs/style.css
CHANGED
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
.body{
|
|
36
36
|
min-height:calc(6 * var(--sn-primitive-dimension-64, 64px));
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
.safeAreaBottom{
|
|
40
|
+
padding-bottom:var(--sn-modal-anatomy-container-safeArea-paddingBottom, var(--sn-density-spacing-interval-s, var(--sn-primitive-dimension-8, 8px)));
|
|
41
|
+
}
|
|
38
42
|
.root{
|
|
39
43
|
display:flex;
|
|
40
44
|
flex-direction:column;
|
|
@@ -99,7 +103,10 @@
|
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
.root[data-surface=bottomSheet] .media{
|
|
106
|
+
justify-self:center;
|
|
102
107
|
aspect-ratio:1;
|
|
108
|
+
width:100%;
|
|
109
|
+
max-width:calc(6 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
.file{
|
|
@@ -125,7 +132,7 @@
|
|
|
125
132
|
.content{
|
|
126
133
|
display:flex;
|
|
127
134
|
flex-direction:column;
|
|
128
|
-
row-gap:var(--sn-primitive-dimension-
|
|
135
|
+
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
129
136
|
min-width:0;
|
|
130
137
|
min-height:0;
|
|
131
138
|
}
|
|
@@ -140,10 +147,15 @@
|
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
.root[data-surface=modal] .content{
|
|
143
|
-
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
144
150
|
order:1;
|
|
145
151
|
}
|
|
146
152
|
|
|
153
|
+
.root[data-surface=modal] .markdown,
|
|
154
|
+
.root[data-surface=modal] .titleWrapper,
|
|
155
|
+
.root[data-surface=modal] .skeletonContent{
|
|
156
|
+
padding-right:var(--sn-primitive-dimension-16, 16px);
|
|
157
|
+
}
|
|
158
|
+
|
|
147
159
|
.root[data-surface=modal] .media{
|
|
148
160
|
order:2;
|
|
149
161
|
}
|
|
@@ -184,7 +196,7 @@
|
|
|
184
196
|
height:calc(7 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
185
197
|
}
|
|
186
198
|
.stateWrapper[data-surface=bottomSheet]{
|
|
187
|
-
min-height:
|
|
199
|
+
min-height:100%;
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
.state{
|
|
@@ -11,5 +11,7 @@ export function DesktopReleaseNotes({ open, onClose, closeOnPopstate, contentSta
|
|
|
11
11
|
const { t } = modalPredefinedLocale.useTranslations();
|
|
12
12
|
const { pageIndex, readablePageNumber, setPage, handleClose, handleReadLaterClick, handleNextPageClick, handlePrevPageClick, } = useReleaseNotesNavigation({ onClose, onReadLaterClick, onSlideChange });
|
|
13
13
|
const shouldShowFooter = contentState === CONTENT_STATE.Data && (loading || items.length > 0);
|
|
14
|
-
return (_jsxs(ModalCustom, { open: open, onClose: handleClose, closeOnPopstate: closeOnPopstate, width: WIDTH.M, "data-content-state": contentState, "data-surface": 'modal', ...extractSupportProps(rest), children: [_jsx(ModalCustom.Header, { title: t('releaseNotes.title') }), _jsx(ModalCustom.Body, { className: styles.body, content: _jsx(ReleaseNotesContent, { contentState: contentState, items: items, loading: loading, pageIndex: pageIndex, surface: 'modal', noDataTitle: t('releaseNotes.noDataTitle'), noDataDescription: t('releaseNotes.noDataDescription'), errorTitle: t('releaseNotes.errorTitle'), errorDescription: t('releaseNotes.errorDescription'), retryLabel: t('releaseNotes.retry'), onPageChange: setPage, onDataErrorRetryClick: onDataErrorRetryClick }) }), shouldShowFooter
|
|
14
|
+
return (_jsxs(ModalCustom, { open: open, onClose: handleClose, closeOnPopstate: closeOnPopstate, width: WIDTH.M, "data-content-state": contentState, "data-surface": 'modal', ...extractSupportProps(rest), children: [_jsx(ModalCustom.Header, { title: t('releaseNotes.title') }), _jsx(ModalCustom.Body, { className: styles.body, content: _jsx(ReleaseNotesContent, { contentState: contentState, items: items, loading: loading, pageIndex: pageIndex, surface: 'modal', noDataTitle: t('releaseNotes.noDataTitle'), noDataDescription: t('releaseNotes.noDataDescription'), errorTitle: t('releaseNotes.errorTitle'), errorDescription: t('releaseNotes.errorDescription'), retryLabel: t('releaseNotes.retry'), onPageChange: setPage, onDataErrorRetryClick: onDataErrorRetryClick }) }), shouldShowFooter ? (_jsx(ModalCustom.Footer, { children: _jsx(ReleaseNotesFooter, { surface: 'modal', loading: loading, total: items.length, pageIndex: pageIndex, readablePageNumber: readablePageNumber, readLaterLabel: t('releaseNotes.readLater'), onPageChange: setPage, onReadLaterClick: handleReadLaterClick, onPrevPageClick: handlePrevPageClick, onNextPageClick: () => handleNextPageClick(items.length) }) })) : (
|
|
15
|
+
// Нижнюю safe-area окна несёт футер; без него отступ рисуется здесь, как в `DesktopModal`.
|
|
16
|
+
_jsx("div", { className: styles.safeAreaBottom }))] }));
|
|
15
17
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ImageSVG } from '@cloud-ru/ds-icons/interface/system';
|
|
3
3
|
import { Markdown } from '@cloud-ru/ds-markdown';
|
|
4
|
-
import { BAR_HIDE_STRATEGY, Scroll } from '@cloud-ru/ds-scroll';
|
|
4
|
+
import { BAR_HIDE_STRATEGY, Scroll, SIZE as SCROLL_SIZE } from '@cloud-ru/ds-scroll';
|
|
5
5
|
import { Skeleton } from '@cloud-ru/ds-skeleton';
|
|
6
6
|
import { Typography } from '@cloud-ru/ds-typography';
|
|
7
7
|
import { useCallback, useState } from 'react';
|
|
@@ -36,5 +36,5 @@ function NoteItemMedia({ image, video }) {
|
|
|
36
36
|
status !== MEDIA_STATUS.Error && (_jsx("img", { ref: handleImageRef, className: styles.file, src: image.src, alt: image.alt, onLoad: handleImageLoad, onError: handleImageError, "data-test-id": TEST_IDS.releaseNotesImage }))), status === MEDIA_STATUS.Pending && (_jsx("div", { className: styles.mediaOverlay, "aria-hidden": true, children: _jsx(Skeleton, { loading: true, width: '100%', height: '100%' }) })), status === MEDIA_STATUS.Error && (_jsx("div", { className: styles.mediaOverlay, role: 'img', "aria-label": image.alt, "data-test-id": TEST_IDS.releaseNotesMediaFallback, children: _jsx(ImageSVG, { className: styles.mediaFallbackIcon, size: FALLBACK_ICON_SIZE }) }))] }));
|
|
37
37
|
}
|
|
38
38
|
export function NoteItem({ title, description, image, video, surface = SURFACE.Modal }) {
|
|
39
|
-
return (_jsxs("article", { className: styles.root, "data-surface": surface, "data-test-id": TEST_IDS.releaseNotesItem, children: [_jsx(NoteItemMedia, { image: image, video: video }, `${video ?? ''}|${image.src}`), _jsxs("div", { className: styles.content, children: [_jsx(Typography, { className: styles.title, variant: 'title', size: 'l', children: title }), _jsx(Scroll, { className: styles.description, barHideStrategy: BAR_HIDE_STRATEGY.Leave, children: _jsx(Markdown, { value: description }) })] })] }));
|
|
39
|
+
return (_jsxs("article", { className: styles.root, "data-surface": surface, "data-test-id": TEST_IDS.releaseNotesItem, children: [_jsx(NoteItemMedia, { image: image, video: video }, `${video ?? ''}|${image.src}`), _jsxs("div", { className: styles.content, children: [_jsx("div", { className: styles.titleWrapper, children: _jsx(Typography, { className: styles.title, variant: 'title', size: 'l', children: title }) }), _jsx(Scroll, { className: styles.description, size: SCROLL_SIZE.S, barHideStrategy: BAR_HIDE_STRATEGY.Leave, children: _jsx(Markdown, { value: description, className: styles.markdown }) })] })] }));
|
|
40
40
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Skeleton, SkeletonText } from '@cloud-ru/ds-skeleton';
|
|
3
|
+
import cn from 'classnames';
|
|
3
4
|
import { SURFACE, TEST_IDS } from '../../constants.js';
|
|
4
5
|
import styles from './styles.module.css';
|
|
5
6
|
/** Строки скелетона текста: заголовок и абзацы описания — по структуре типовой новости. */
|
|
@@ -10,5 +11,5 @@ const PARAGRAPH_LINES = [2, 3, 3];
|
|
|
10
11
|
* поэтому переход `loading → data` не меняет геометрию.
|
|
11
12
|
*/
|
|
12
13
|
export function NoteItemSkeleton({ surface = SURFACE.Modal }) {
|
|
13
|
-
return (_jsxs("div", { className: styles.root, "data-surface": surface, "aria-hidden": true, "data-test-id": TEST_IDS.releaseNotesSkeleton, children: [_jsx("div", { className: styles.media, "data-test-id": TEST_IDS.releaseNotesMedia, children: _jsx(Skeleton, { loading: true, width: '100%', height: '100%' }) }), _jsxs("div", { className: styles.content, children: [_jsx(SkeletonText, { loading: true, lines: TITLE_LINES, variant: '
|
|
14
|
+
return (_jsxs("div", { className: styles.root, "data-surface": surface, "aria-hidden": true, "data-test-id": TEST_IDS.releaseNotesSkeleton, children: [_jsx("div", { className: styles.media, "data-test-id": TEST_IDS.releaseNotesMedia, children: _jsx(Skeleton, { loading: true, width: '100%', height: '100%' }) }), _jsxs("div", { className: cn(styles.content, styles.skeletonContent), children: [_jsx(SkeletonText, { loading: true, lines: TITLE_LINES, variant: 'title', size: 'l' }), PARAGRAPH_LINES.map((lines, index) => (_jsx(SkeletonText, { loading: true, lines: lines }, index)))] })] }));
|
|
14
15
|
}
|
|
@@ -23,7 +23,10 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.root[data-surface=bottomSheet] .media{
|
|
26
|
+
justify-self:center;
|
|
26
27
|
aspect-ratio:1;
|
|
28
|
+
width:100%;
|
|
29
|
+
max-width:calc(6 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
.file{
|
|
@@ -49,7 +52,7 @@
|
|
|
49
52
|
.content{
|
|
50
53
|
display:flex;
|
|
51
54
|
flex-direction:column;
|
|
52
|
-
row-gap:var(--sn-primitive-dimension-
|
|
55
|
+
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
53
56
|
min-width:0;
|
|
54
57
|
min-height:0;
|
|
55
58
|
}
|
|
@@ -64,10 +67,15 @@
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
.root[data-surface=modal] .content{
|
|
67
|
-
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
68
70
|
order:1;
|
|
69
71
|
}
|
|
70
72
|
|
|
73
|
+
.root[data-surface=modal] .markdown,
|
|
74
|
+
.root[data-surface=modal] .titleWrapper,
|
|
75
|
+
.root[data-surface=modal] .skeletonContent{
|
|
76
|
+
padding-right:var(--sn-primitive-dimension-16, 16px);
|
|
77
|
+
}
|
|
78
|
+
|
|
71
79
|
.root[data-surface=modal] .media{
|
|
72
80
|
order:2;
|
|
73
81
|
}
|
|
@@ -3,10 +3,14 @@ import { Button } from '@cloud-ru/ds-button';
|
|
|
3
3
|
import { Carousel } from '@cloud-ru/ds-carousel';
|
|
4
4
|
import { CrossSVG } from '@cloud-ru/ds-icons/interface/system';
|
|
5
5
|
import { InfoBlock } from '@cloud-ru/ds-info-block';
|
|
6
|
+
import { Typography } from '@cloud-ru/ds-typography';
|
|
6
7
|
import cn from 'classnames';
|
|
7
8
|
import { CONTENT_STATE, TEST_IDS } from '../../constants.js';
|
|
8
9
|
import { NoteItem, NoteItemSkeleton } from '../NoteItem/index.js';
|
|
9
10
|
import styles from './styles.module.css';
|
|
11
|
+
function InfoBlockDescription({ children }) {
|
|
12
|
+
return (_jsx(Typography, { variant: 'body', size: 'm', children: children }));
|
|
13
|
+
}
|
|
10
14
|
export function ReleaseNotesContent({ contentState, items, loading, pageIndex, surface, noDataTitle, noDataDescription, errorTitle, errorDescription, retryLabel, onPageChange, onDataErrorRetryClick, }) {
|
|
11
15
|
const total = items.length;
|
|
12
16
|
const hasItems = total > 0;
|
|
@@ -14,10 +18,10 @@ export function ReleaseNotesContent({ contentState, items, loading, pageIndex, s
|
|
|
14
18
|
return (_jsx("div", { className: cn(styles.root, styles[surface]), children: _jsx("div", { className: styles.skeletonSlide, children: _jsx(NoteItemSkeleton, { surface: surface }) }) }));
|
|
15
19
|
}
|
|
16
20
|
if (contentState === CONTENT_STATE.Error) {
|
|
17
|
-
return (_jsx("div", { className: styles.stateWrapper, "data-surface": surface, children: _jsx(InfoBlock, { className: styles.state, size: 'l', title: errorTitle, content: errorDescription, icon: { icon: CrossSVG, appearance: 'neutral', background: true }, "data-test-id": TEST_IDS.releaseNotesError, footer: _jsx(Button, { view: 'filled', appearance: 'neutral', size: 'l', label: retryLabel, onClick: onDataErrorRetryClick, "data-test-id": TEST_IDS.releaseNotesRetryButton }) }) }));
|
|
21
|
+
return (_jsx("div", { className: styles.stateWrapper, "data-surface": surface, children: _jsx(InfoBlock, { className: styles.state, size: 'l', title: errorTitle, content: _jsx(InfoBlockDescription, { children: errorDescription }), icon: { icon: CrossSVG, appearance: 'neutral', background: true }, "data-test-id": TEST_IDS.releaseNotesError, footer: _jsx(Button, { view: 'filled', appearance: 'neutral', size: 'l', label: retryLabel, onClick: onDataErrorRetryClick, "data-test-id": TEST_IDS.releaseNotesRetryButton }) }) }));
|
|
18
22
|
}
|
|
19
23
|
if (contentState === CONTENT_STATE.NoData || !hasItems) {
|
|
20
|
-
return (_jsx("div", { className: styles.stateWrapper, "data-surface": surface, children: _jsx(InfoBlock, { className: styles.state, size: 'l', title: noDataTitle, content: noDataDescription, "data-test-id": TEST_IDS.releaseNotesNoData }) }));
|
|
24
|
+
return (_jsx("div", { className: styles.stateWrapper, "data-surface": surface, children: _jsx(InfoBlock, { className: styles.state, size: 'l', title: noDataTitle, content: _jsx(InfoBlockDescription, { children: noDataDescription }), "data-test-id": TEST_IDS.releaseNotesNoData }) }));
|
|
21
25
|
}
|
|
22
26
|
return (_jsx("div", { className: cn(styles.root, styles[surface]), children: _jsx(Carousel, { arrows: false, pagination: false, swipe: true, state: { page: pageIndex, onChange: onPageChange }, className: styles.carousel, children: items.map(item => (_jsx(NoteItem, { ...item, surface: surface }, `${item.title}-${item.image.src}`))) }) }));
|
|
23
27
|
}
|
|
@@ -10,7 +10,6 @@ export function ReleaseNotesFooter({ surface, total, loading, pageIndex, readabl
|
|
|
10
10
|
// появляются рывком поверх уже отрисованного контента.
|
|
11
11
|
const hasPagination = loading || total > 1;
|
|
12
12
|
const isModal = surface === SURFACE.Modal;
|
|
13
|
-
const buttonSize = isModal ? 'm' : 'l';
|
|
14
13
|
const handleSliderChange = (page) => onPageChange(page - 1);
|
|
15
|
-
return (_jsxs("div", { className: styles.root, "data-surface": surface, children: [_jsx(Button, { view: 'function', appearance: 'neutral', size:
|
|
14
|
+
return (_jsxs("div", { className: styles.root, "data-surface": surface, children: [_jsx(Button, { view: 'function', appearance: 'neutral', size: 'm', label: readLaterLabel, "data-test-id": TEST_IDS.releaseNotesReadLaterButton, ...readLaterButtonProps, onClick: onReadLaterClick }), isModal && hasPagination && (_jsx("div", { className: styles.slider, children: _jsx(Skeleton, { loading: loading, className: styles.sliderSkeleton, children: _jsx(PaginationSlider, { size: PAGINATION_SLIDER_SIZE.Xs, total: total, page: readablePageNumber, onChange: handleSliderChange, "data-test-id": TEST_IDS.releaseNotesPaginationSlider }) }) })), hasPagination && (_jsxs("div", { className: styles.controls, children: [_jsx(Button, { view: 'outline', appearance: 'neutral', size: 'm', icon: _jsx(ChevronLeftSVG, {}), disabled: loading || pageIndex === 0, onClick: onPrevPageClick, "data-test-id": TEST_IDS.releaseNotesPrevButton }), _jsx(Button, { view: 'outline', appearance: 'neutral', size: 'm', icon: _jsx(ChevronRightSVG, {}), disabled: loading || readablePageNumber === total, onClick: onNextPageClick, "data-test-id": TEST_IDS.releaseNotesNextButton })] }))] }));
|
|
16
15
|
}
|
package/dist/esm/style.css
CHANGED
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
.body{
|
|
36
36
|
min-height:calc(6 * var(--sn-primitive-dimension-64, 64px));
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
.safeAreaBottom{
|
|
40
|
+
padding-bottom:var(--sn-modal-anatomy-container-safeArea-paddingBottom, var(--sn-density-spacing-interval-s, var(--sn-primitive-dimension-8, 8px)));
|
|
41
|
+
}
|
|
38
42
|
.root{
|
|
39
43
|
display:flex;
|
|
40
44
|
flex-direction:column;
|
|
@@ -99,7 +103,10 @@
|
|
|
99
103
|
}
|
|
100
104
|
|
|
101
105
|
.root[data-surface=bottomSheet] .media{
|
|
106
|
+
justify-self:center;
|
|
102
107
|
aspect-ratio:1;
|
|
108
|
+
width:100%;
|
|
109
|
+
max-width:calc(6 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
.file{
|
|
@@ -125,7 +132,7 @@
|
|
|
125
132
|
.content{
|
|
126
133
|
display:flex;
|
|
127
134
|
flex-direction:column;
|
|
128
|
-
row-gap:var(--sn-primitive-dimension-
|
|
135
|
+
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
129
136
|
min-width:0;
|
|
130
137
|
min-height:0;
|
|
131
138
|
}
|
|
@@ -140,10 +147,15 @@
|
|
|
140
147
|
}
|
|
141
148
|
|
|
142
149
|
.root[data-surface=modal] .content{
|
|
143
|
-
row-gap:var(--sn-primitive-dimension-16, 16px);
|
|
144
150
|
order:1;
|
|
145
151
|
}
|
|
146
152
|
|
|
153
|
+
.root[data-surface=modal] .markdown,
|
|
154
|
+
.root[data-surface=modal] .titleWrapper,
|
|
155
|
+
.root[data-surface=modal] .skeletonContent{
|
|
156
|
+
padding-right:var(--sn-primitive-dimension-16, 16px);
|
|
157
|
+
}
|
|
158
|
+
|
|
147
159
|
.root[data-surface=modal] .media{
|
|
148
160
|
order:2;
|
|
149
161
|
}
|
|
@@ -184,7 +196,7 @@
|
|
|
184
196
|
height:calc(7 * var(--sn-primitive-dimension-64, 64px) - var(--sn-primitive-dimension-4, 4px));
|
|
185
197
|
}
|
|
186
198
|
.stateWrapper[data-surface=bottomSheet]{
|
|
187
|
-
min-height:
|
|
199
|
+
min-height:100%;
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
.state{
|