@cloud-ru/ds-pagination 1.0.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 (101) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/LICENSE +201 -0
  3. package/README.md +174 -0
  4. package/dist/cjs/components/Pagination/Pagination.d.ts +24 -0
  5. package/dist/cjs/components/Pagination/Pagination.js +64 -0
  6. package/dist/cjs/components/Pagination/index.d.ts +1 -0
  7. package/dist/cjs/components/Pagination/index.js +17 -0
  8. package/dist/cjs/components/Pagination/styles.module.css +19 -0
  9. package/dist/cjs/components/PaginationNumberItem/PaginationNumberItem.d.ts +10 -0
  10. package/dist/cjs/components/PaginationNumberItem/PaginationNumberItem.js +18 -0
  11. package/dist/cjs/components/PaginationNumberItem/index.d.ts +1 -0
  12. package/dist/cjs/components/PaginationNumberItem/index.js +17 -0
  13. package/dist/cjs/components/PaginationNumberItem/styles.module.css +149 -0
  14. package/dist/cjs/components/PaginationSlider/PaginationSlider.d.ts +17 -0
  15. package/dist/cjs/components/PaginationSlider/PaginationSlider.js +16 -0
  16. package/dist/cjs/components/PaginationSlider/index.d.ts +1 -0
  17. package/dist/cjs/components/PaginationSlider/index.js +17 -0
  18. package/dist/cjs/components/PaginationSlider/styles.module.css +25 -0
  19. package/dist/cjs/components/PaginationSliderItem/PaginationSliderItem.d.ts +10 -0
  20. package/dist/cjs/components/PaginationSliderItem/PaginationSliderItem.js +12 -0
  21. package/dist/cjs/components/PaginationSliderItem/index.d.ts +1 -0
  22. package/dist/cjs/components/PaginationSliderItem/index.js +17 -0
  23. package/dist/cjs/components/PaginationSliderItem/styles.module.css +102 -0
  24. package/dist/cjs/components/index.d.ts +2 -0
  25. package/dist/cjs/components/index.js +18 -0
  26. package/dist/cjs/constants.d.ts +31 -0
  27. package/dist/cjs/constants.js +37 -0
  28. package/dist/cjs/contexts.d.ts +7 -0
  29. package/dist/cjs/contexts.js +11 -0
  30. package/dist/cjs/index.d.ts +4 -0
  31. package/dist/cjs/index.js +20 -0
  32. package/dist/cjs/package.json +3 -0
  33. package/dist/cjs/style.css +295 -0
  34. package/dist/cjs/types.d.ts +5 -0
  35. package/dist/cjs/types.js +2 -0
  36. package/dist/cjs/utils/getPaginationEntries.d.ts +20 -0
  37. package/dist/cjs/utils/getPaginationEntries.js +85 -0
  38. package/dist/cjs/utils/getRange.d.ts +1 -0
  39. package/dist/cjs/utils/getRange.js +6 -0
  40. package/dist/cjs/utils/index.d.ts +2 -0
  41. package/dist/cjs/utils/index.js +18 -0
  42. package/dist/esm/components/Pagination/Pagination.d.ts +24 -0
  43. package/dist/esm/components/Pagination/Pagination.js +58 -0
  44. package/dist/esm/components/Pagination/index.d.ts +1 -0
  45. package/dist/esm/components/Pagination/index.js +1 -0
  46. package/dist/esm/components/Pagination/styles.module.css +19 -0
  47. package/dist/esm/components/PaginationNumberItem/PaginationNumberItem.d.ts +10 -0
  48. package/dist/esm/components/PaginationNumberItem/PaginationNumberItem.js +12 -0
  49. package/dist/esm/components/PaginationNumberItem/index.d.ts +1 -0
  50. package/dist/esm/components/PaginationNumberItem/index.js +1 -0
  51. package/dist/esm/components/PaginationNumberItem/styles.module.css +149 -0
  52. package/dist/esm/components/PaginationSlider/PaginationSlider.d.ts +17 -0
  53. package/dist/esm/components/PaginationSlider/PaginationSlider.js +10 -0
  54. package/dist/esm/components/PaginationSlider/index.d.ts +1 -0
  55. package/dist/esm/components/PaginationSlider/index.js +1 -0
  56. package/dist/esm/components/PaginationSlider/styles.module.css +25 -0
  57. package/dist/esm/components/PaginationSliderItem/PaginationSliderItem.d.ts +10 -0
  58. package/dist/esm/components/PaginationSliderItem/PaginationSliderItem.js +6 -0
  59. package/dist/esm/components/PaginationSliderItem/index.d.ts +1 -0
  60. package/dist/esm/components/PaginationSliderItem/index.js +1 -0
  61. package/dist/esm/components/PaginationSliderItem/styles.module.css +102 -0
  62. package/dist/esm/components/index.d.ts +2 -0
  63. package/dist/esm/components/index.js +2 -0
  64. package/dist/esm/constants.d.ts +31 -0
  65. package/dist/esm/constants.js +31 -0
  66. package/dist/esm/contexts.d.ts +7 -0
  67. package/dist/esm/contexts.js +7 -0
  68. package/dist/esm/index.d.ts +4 -0
  69. package/dist/esm/index.js +4 -0
  70. package/dist/esm/style.css +295 -0
  71. package/dist/esm/types.d.ts +5 -0
  72. package/dist/esm/types.js +1 -0
  73. package/dist/esm/utils/getPaginationEntries.d.ts +20 -0
  74. package/dist/esm/utils/getPaginationEntries.js +81 -0
  75. package/dist/esm/utils/getRange.d.ts +1 -0
  76. package/dist/esm/utils/getRange.js +3 -0
  77. package/dist/esm/utils/index.d.ts +2 -0
  78. package/dist/esm/utils/index.js +2 -0
  79. package/dist/tsconfig.cjs.tsbuildinfo +1 -0
  80. package/dist/tsconfig.esm.tsbuildinfo +1 -0
  81. package/package.json +44 -0
  82. package/src/components/Pagination/Pagination.tsx +162 -0
  83. package/src/components/Pagination/index.ts +1 -0
  84. package/src/components/Pagination/styles.module.scss +27 -0
  85. package/src/components/PaginationNumberItem/PaginationNumberItem.tsx +59 -0
  86. package/src/components/PaginationNumberItem/index.ts +1 -0
  87. package/src/components/PaginationNumberItem/styles.module.scss +82 -0
  88. package/src/components/PaginationSlider/PaginationSlider.tsx +50 -0
  89. package/src/components/PaginationSlider/index.ts +1 -0
  90. package/src/components/PaginationSlider/styles.module.scss +31 -0
  91. package/src/components/PaginationSliderItem/PaginationSliderItem.tsx +29 -0
  92. package/src/components/PaginationSliderItem/index.ts +1 -0
  93. package/src/components/PaginationSliderItem/styles.module.scss +137 -0
  94. package/src/components/index.ts +2 -0
  95. package/src/constants.ts +35 -0
  96. package/src/contexts.ts +16 -0
  97. package/src/index.ts +4 -0
  98. package/src/types.ts +7 -0
  99. package/src/utils/getPaginationEntries.ts +118 -0
  100. package/src/utils/getRange.ts +3 -0
  101. package/src/utils/index.ts +2 -0
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@cloud-ru/ds-pagination",
3
+ "version": "1.0.0",
4
+ "description": "Пакет пагинации — компоненты Pagination с нумерованными страницами и PaginationSlider для плотной мини-навигации.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/cloud-ru-tech/snack-v2.git",
8
+ "directory": "packages/pagination"
9
+ },
10
+ "homepage": "https://github.com/cloud-ru-tech/snack-v2/tree/master/packages/pagination",
11
+ "license": "Apache-2.0",
12
+ "author": "Nikita Ershov <niershov@cloud.ru>",
13
+ "type": "module",
14
+ "main": "./dist/cjs/index.js",
15
+ "module": "./dist/esm/index.js",
16
+ "types": "./dist/esm/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "source": "./src/index.ts",
20
+ "types": "./dist/esm/index.d.ts",
21
+ "import": "./dist/esm/index.js",
22
+ "require": "./dist/cjs/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src",
28
+ "./CHANGELOG.md",
29
+ "./LICENSE"
30
+ ],
31
+ "sideEffects": [
32
+ "*.css",
33
+ "**/*.css"
34
+ ],
35
+ "dependencies": {
36
+ "@cloud-ru/ds-icons": "^1.0.0",
37
+ "@cloud-ru/ds-materials": "^1.0.0",
38
+ "@cloud-ru/ds-button": "^1.0.0",
39
+ "@cloud-ru/ds-utils": "^1.0.0"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }
@@ -0,0 +1,162 @@
1
+ import { Button } from '@cloud-ru/ds-button';
2
+ import { ChevronLeftSVG, ChevronRightSVG } from '@cloud-ru/ds-icons/interface/system';
3
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
4
+ import { MouseEvent, useEffect, useRef, useState } from 'react';
5
+
6
+ import { getPageMoreTestId, getPageNumberTestId, PAGINATION_SIZE, TEST_IDS, VARIANT } from '../../constants';
7
+ import { PaginationContext } from '../../contexts';
8
+ import { PaginationSize, Variant } from '../../types';
9
+ import { getPaginationEntries, PaginationEntry, PaginationEntryKind } from '../../utils';
10
+ import { PaginationNumberItem } from '../PaginationNumberItem';
11
+ import styles from './styles.module.scss';
12
+
13
+ export type PaginationProps = WithSupportProps<{
14
+ /** Общее количество страниц */
15
+ total: number;
16
+ /** Текущая страница */
17
+ page: number;
18
+ /** Варианты тега кнопок: <a/> или <button/> */
19
+ variant?: Variant;
20
+ /** Максимальное количество страниц/элементов, помещающихся до транкейта */
21
+ maxLength?: number;
22
+ /** Колбэк смены значения */
23
+ onChange(page: number, event?: MouseEvent<HTMLButtonElement | HTMLAnchorElement>): void;
24
+ /** Колбэк форматирования ссылки */
25
+ hrefFormatter?(page: number): string;
26
+ /** CSS класснейм */
27
+ className?: string;
28
+ /** Размер
29
+ * @default 'm'
30
+ */
31
+ size?: PaginationSize;
32
+ }>;
33
+
34
+ const FIRST_PAGE = 1;
35
+ const ARROW_STEP = 1;
36
+ const MAX_LENGTH = 7;
37
+
38
+ export function Pagination({
39
+ total,
40
+ page,
41
+ onChange,
42
+ hrefFormatter,
43
+ className,
44
+ variant = VARIANT.Button,
45
+ size = PAGINATION_SIZE.M,
46
+ maxLength = MAX_LENGTH,
47
+ ...rest
48
+ }: PaginationProps) {
49
+ const entries = getPaginationEntries({
50
+ firstPage: FIRST_PAGE,
51
+ lastPage: total,
52
+ currentPage: page,
53
+ maxLength,
54
+ });
55
+
56
+ const buttonRefs = useRef<(HTMLButtonElement | HTMLAnchorElement | undefined)[]>([]);
57
+ const [buttonToFocus, setButtonToFocus] = useState(-1);
58
+
59
+ useEffect(() => {
60
+ buttonRefs.current[buttonToFocus]?.focus();
61
+ }, [buttonToFocus]);
62
+
63
+ const handlePreviousPageButtonClick = () => {
64
+ const newPage = page - ARROW_STEP;
65
+ onChange(newPage);
66
+
67
+ if (newPage === FIRST_PAGE) {
68
+ setButtonToFocus(FIRST_PAGE);
69
+ }
70
+ };
71
+
72
+ const handleNextPageButtonClick = () => {
73
+ const newPage = page + ARROW_STEP;
74
+ onChange(newPage);
75
+
76
+ if (newPage === total) {
77
+ setButtonToFocus(total);
78
+ }
79
+ };
80
+
81
+ const handleMoreButtonClick = (start: number, end: number) => {
82
+ const targetPage = Math.floor((start + end) / 2);
83
+ onChange(targetPage);
84
+ setButtonToFocus(targetPage);
85
+ };
86
+
87
+ const renderEntry = (entry: PaginationEntry) => {
88
+ if (entry.kind === PaginationEntryKind.Page) {
89
+ return (
90
+ <li key={entry.page}>
91
+ <PaginationNumberItem
92
+ label={entry.page}
93
+ activated={entry.page === page}
94
+ onClick={e => onChange(entry.page, e)}
95
+ setButtonRef={(el: HTMLButtonElement | HTMLAnchorElement) => {
96
+ buttonRefs.current[entry.page] = el;
97
+ }}
98
+ href={hrefFormatter?.(entry.page)}
99
+ aria-current={entry.page === page ? 'page' : undefined}
100
+ data-test-id={getPageNumberTestId(entry.page)}
101
+ />
102
+ </li>
103
+ );
104
+ }
105
+
106
+ if (entry.kind === PaginationEntryKind.Break) {
107
+ return (
108
+ <li key={`${entry.start}-${entry.end}`}>
109
+ <PaginationNumberItem
110
+ label='...'
111
+ onClick={() => handleMoreButtonClick(entry.start, entry.end)}
112
+ data-test-id={getPageMoreTestId(entry.start, entry.end)}
113
+ />
114
+ </li>
115
+ );
116
+ }
117
+
118
+ return null;
119
+ };
120
+
121
+ const buttonSize = size === PAGINATION_SIZE.M ? 'm' : 's';
122
+
123
+ return (
124
+ <PaginationContext.Provider value={{ size, variant }}>
125
+ <nav
126
+ className={className}
127
+ aria-label='Pagination'
128
+ data-size={size}
129
+ data-variant={variant}
130
+ {...extractSupportProps(rest)}
131
+ >
132
+ <ul className={styles.pagination} data-size={size}>
133
+ <li>
134
+ <Button
135
+ view='simple'
136
+ appearance='neutral'
137
+ icon={<ChevronLeftSVG />}
138
+ onClick={handlePreviousPageButtonClick}
139
+ disabled={page === FIRST_PAGE}
140
+ size={buttonSize}
141
+ aria-label='Previous page'
142
+ data-test-id={TEST_IDS.prev}
143
+ />
144
+ </li>
145
+ {entries.map(renderEntry)}
146
+ <li>
147
+ <Button
148
+ view='simple'
149
+ appearance='neutral'
150
+ icon={<ChevronRightSVG />}
151
+ onClick={handleNextPageButtonClick}
152
+ disabled={page === total}
153
+ size={buttonSize}
154
+ aria-label='Next page'
155
+ data-test-id={TEST_IDS.next}
156
+ />
157
+ </li>
158
+ </ul>
159
+ </nav>
160
+ </PaginationContext.Provider>
161
+ );
162
+ }
@@ -0,0 +1 @@
1
+ export * from './Pagination';
@@ -0,0 +1,27 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/pagination.module' as pagination;
3
+
4
+ $sizes: 's', 'm';
5
+
6
+ .pagination {
7
+ display: flex;
8
+ flex-direction: row;
9
+ align-items: center;
10
+
11
+ margin: 0;
12
+ padding: 0;
13
+
14
+ list-style-type: none;
15
+
16
+ @each $size in $sizes {
17
+ &[data-size='#{$size}'] {
18
+ @include base.composite-var(pagination.$pagination, 'anatomy', 'size', $size, 'paginationNumber');
19
+ }
20
+ }
21
+
22
+ > li {
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ }
27
+ }
@@ -0,0 +1,59 @@
1
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
2
+ import { MouseEvent, Ref } from 'react';
3
+
4
+ import { usePaginationContext } from '../../contexts';
5
+ import styles from './styles.module.scss';
6
+
7
+ export type PaginationNumberItemProps = WithSupportProps<{
8
+ label: number | string;
9
+ activated?: boolean;
10
+ onClick(event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>): void;
11
+ href?: string;
12
+ setButtonRef?: Ref<HTMLButtonElement | HTMLAnchorElement>;
13
+ }>;
14
+
15
+ export function PaginationNumberItem({
16
+ label,
17
+ activated,
18
+ onClick,
19
+ href,
20
+ setButtonRef,
21
+ ...rest
22
+ }: PaginationNumberItemProps) {
23
+ const { size, variant } = usePaginationContext();
24
+
25
+ const stateLayer = activated ? 'activatedOnBackground' : 'emptyNeutralOnBackground';
26
+
27
+ if (variant === 'link') {
28
+ return (
29
+ <a
30
+ role='button'
31
+ className={styles.root}
32
+ onClick={onClick}
33
+ ref={setButtonRef as Ref<HTMLAnchorElement>}
34
+ data-size={size}
35
+ {...extractSupportProps(rest)}
36
+ data-activated={activated || undefined}
37
+ href={href}
38
+ >
39
+ <span className={styles.stateLayer} aria-hidden data-state={stateLayer} />
40
+ <span className={styles.textWrapper}>{label}</span>
41
+ </a>
42
+ );
43
+ }
44
+
45
+ return (
46
+ <button
47
+ type='button'
48
+ className={styles.root}
49
+ onClick={onClick}
50
+ ref={setButtonRef as Ref<HTMLButtonElement>}
51
+ data-size={size}
52
+ {...extractSupportProps(rest)}
53
+ data-activated={activated || undefined}
54
+ >
55
+ <span className={styles.stateLayer} aria-hidden data-state={stateLayer} />
56
+ <span className={styles.textWrapper}>{label}</span>
57
+ </button>
58
+ );
59
+ }
@@ -0,0 +1 @@
1
+ export * from './PaginationNumberItem';
@@ -0,0 +1,82 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/pagination.module' as pagination;
3
+ @use '@cloud-ru/ds-materials' as m;
4
+
5
+ $sizes: 's', 'm';
6
+
7
+ $typography: (
8
+ 's': base.$sn-regular-label-s,
9
+ 'm': base.$sn-regular-label-m,
10
+ );
11
+
12
+ .stateLayer {
13
+ visibility: initial;
14
+ }
15
+
16
+ .root {
17
+ cursor: pointer;
18
+
19
+ position: relative;
20
+
21
+ display: inline-flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+
25
+ box-sizing: border-box;
26
+ margin: 0;
27
+ padding: 0;
28
+ border: none;
29
+
30
+ font-family: inherit;
31
+ color: base.$sn-theme-color-available-version-textTertiary;
32
+ text-decoration: none;
33
+
34
+ background-color: transparent;
35
+ outline: 0;
36
+ outline-offset: 0;
37
+
38
+ @each $size in $sizes {
39
+ &[data-size='#{$size}'] {
40
+ @include base.composite-var(
41
+ pagination.$pagination,
42
+ 'anatomy',
43
+ 'size',
44
+ $size,
45
+ 'paginationNumber',
46
+ 'itemPaginationNumber'
47
+ );
48
+
49
+ .textWrapper {
50
+ @include base.composite-var($typography, $size);
51
+
52
+ @include base.composite-var(
53
+ pagination.$pagination,
54
+ 'anatomy',
55
+ 'size',
56
+ $size,
57
+ 'paginationNumber',
58
+ 'itemPaginationNumber',
59
+ 'textWrapper'
60
+ );
61
+ }
62
+ }
63
+ }
64
+
65
+ &[data-activated] {
66
+ color: base.$sn-theme-color-primary-text;
67
+ }
68
+
69
+ &:focus-visible {
70
+ outline: base.$sn-theme-color-available-complementary solid base.$sn-primitive-strokeWeight-strokeSemiBold;
71
+ outline-offset: base.$sn-primitive-strokeWeight-strokeSemiBold;
72
+ }
73
+
74
+ @include m.has-state-layer-as-child(#{stateLayer});
75
+ }
76
+
77
+ .textWrapper {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ min-width: 0;
82
+ }
@@ -0,0 +1,50 @@
1
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
2
+
3
+ import { getSliderItemTestId, PAGINATION_SLIDER_SIZE } from '../../constants';
4
+ import { PaginationSliderSize } from '../../types';
5
+ import { getRange } from '../../utils';
6
+ import { PaginationSliderItem } from '../PaginationSliderItem';
7
+ import styles from './styles.module.scss';
8
+
9
+ export type PaginationSliderProps = WithSupportProps<{
10
+ /** Общее количество страниц */
11
+ total: number;
12
+ /** Текущая страница */
13
+ page: number;
14
+ /** Колбек смены значения */
15
+ onChange(page: number): void;
16
+ /** CSS класснейм */
17
+ className?: string;
18
+ /** Размер
19
+ * @default 's'
20
+ */
21
+ size?: PaginationSliderSize;
22
+ }>;
23
+
24
+ const FIRST_PAGE = 1;
25
+
26
+ export function PaginationSlider({
27
+ total,
28
+ page,
29
+ onChange,
30
+ className,
31
+ size = PAGINATION_SLIDER_SIZE.Xs,
32
+ ...rest
33
+ }: PaginationSliderProps) {
34
+ return (
35
+ <nav className={className} aria-label='Pagination slider' {...extractSupportProps(rest)}>
36
+ <ul className={styles.paginationSlider} data-size={size}>
37
+ {getRange(FIRST_PAGE, total).map(value => (
38
+ <li key={value}>
39
+ <PaginationSliderItem
40
+ activated={value === page}
41
+ onClick={() => onChange(value)}
42
+ size={size}
43
+ data-test-id={getSliderItemTestId(value)}
44
+ />
45
+ </li>
46
+ ))}
47
+ </ul>
48
+ </nav>
49
+ );
50
+ }
@@ -0,0 +1 @@
1
+ export * from './PaginationSlider';
@@ -0,0 +1,31 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/pagination.module' as pagination;
3
+
4
+ $sizes: 'xs', 's';
5
+
6
+ .paginationSlider {
7
+ display: flex;
8
+ flex-direction: row;
9
+ gap: 0;
10
+ align-items: center;
11
+
12
+ width: fit-content;
13
+ margin: 0;
14
+ padding: 0;
15
+
16
+ list-style-type: none;
17
+
18
+ @each $size in $sizes {
19
+ &[data-size='#{$size}'] {
20
+ @include base.composite-var(pagination.$pagination, 'anatomy', 'size', $size, 'paginationSlider');
21
+ }
22
+ }
23
+
24
+ background-color: base.$sn-theme-color-material-stateLayer-versionOnColor-default-filled;
25
+
26
+ > li {
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ }
31
+ }
@@ -0,0 +1,29 @@
1
+ import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
2
+ import { Ref } from 'react';
3
+
4
+ import { PaginationSliderSize } from '../../types';
5
+ import styles from './styles.module.scss';
6
+
7
+ export type PaginationSliderItemProps = WithSupportProps<{
8
+ activated?: boolean;
9
+ onClick(): void;
10
+ size: PaginationSliderSize;
11
+ setButtonRef?: Ref<HTMLButtonElement>;
12
+ }>;
13
+
14
+ export function PaginationSliderItem({ activated, onClick, size, setButtonRef, ...rest }: PaginationSliderItemProps) {
15
+ return (
16
+ <button
17
+ type='button'
18
+ className={styles.root}
19
+ onClick={onClick}
20
+ ref={setButtonRef}
21
+ data-size={size}
22
+ {...extractSupportProps(rest)}
23
+ data-activated={activated || undefined}
24
+ aria-current={activated ? 'true' : undefined}
25
+ >
26
+ <span className={styles.dot} aria-hidden />
27
+ </button>
28
+ );
29
+ }
@@ -0,0 +1 @@
1
+ export * from './PaginationSliderItem';
@@ -0,0 +1,137 @@
1
+ @use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
2
+ @use '@cloud-ru/ds-figma-variables/build/scss/components/pagination.module' as pagination;
3
+
4
+ $sizes: 'xs', 's';
5
+
6
+ .root {
7
+ cursor: pointer;
8
+
9
+ position: relative;
10
+
11
+ display: inline-flex;
12
+ align-items: center;
13
+ justify-content: center;
14
+
15
+ box-sizing: border-box;
16
+ margin: 0;
17
+ padding: 0;
18
+ border: none;
19
+
20
+ background: transparent;
21
+ outline: 0;
22
+ outline-offset: 0;
23
+
24
+ @each $size in $sizes {
25
+ &[data-size='#{$size}'] {
26
+ @include base.composite-var(
27
+ pagination.$pagination,
28
+ 'anatomy',
29
+ 'size',
30
+ $size,
31
+ 'paginationSlider',
32
+ 'itemPaginationSlider'
33
+ );
34
+
35
+ // Фиксированная ширина: в макете растёт только точка внутри, сам элемент — нет.
36
+ width: base.simple-var(
37
+ pagination.$pagination,
38
+ 'anatomy',
39
+ 'size',
40
+ $size,
41
+ 'paginationSlider',
42
+ 'itemPaginationSlider',
43
+ 'min-width'
44
+ );
45
+
46
+ &:not(&[data-activated]) {
47
+ .dot {
48
+ @include base.composite-var(
49
+ pagination.$pagination,
50
+ 'anatomy',
51
+ 'size',
52
+ $size,
53
+ 'paginationSlider',
54
+ 'itemPaginationSlider',
55
+ 'slider',
56
+ 'activatedFalse',
57
+ 'default'
58
+ );
59
+
60
+ background-color: base.$sn-theme-color-neutral-background2Level;
61
+ box-shadow: base.$sn-boxShadow-elevation-level1;
62
+ }
63
+
64
+ @media (hover: hover) {
65
+ &:hover .dot {
66
+ @include base.composite-var(
67
+ pagination.$pagination,
68
+ 'anatomy',
69
+ 'size',
70
+ $size,
71
+ 'paginationSlider',
72
+ 'itemPaginationSlider',
73
+ 'slider',
74
+ 'activatedFalse',
75
+ 'hovered'
76
+ );
77
+ }
78
+ }
79
+
80
+ /* stylelint-disable-next-line order/order */
81
+ &:active .dot {
82
+ @include base.composite-var(
83
+ pagination.$pagination,
84
+ 'anatomy',
85
+ 'size',
86
+ $size,
87
+ 'paginationSlider',
88
+ 'itemPaginationSlider',
89
+ 'slider',
90
+ 'activatedFalse',
91
+ 'pressed'
92
+ );
93
+ }
94
+ }
95
+
96
+ &[data-activated] {
97
+ .dot {
98
+ @include base.composite-var(
99
+ pagination.$pagination,
100
+ 'anatomy',
101
+ 'size',
102
+ $size,
103
+ 'paginationSlider',
104
+ 'itemPaginationSlider',
105
+ 'slider',
106
+ 'activatedTrue',
107
+ 'default'
108
+ );
109
+
110
+ background-color: base.$sn-theme-color-primary-accent;
111
+ box-shadow: none;
112
+ }
113
+ }
114
+
115
+ &:focus-visible .dot {
116
+ outline: base.$sn-theme-color-available-complementary solid base.$sn-primitive-strokeWeight-strokeSemiBold;
117
+ outline-offset: base.$sn-primitive-strokeWeight-strokeSemiBold;
118
+ }
119
+ }
120
+ }
121
+ }
122
+
123
+ .dot {
124
+ position: relative;
125
+
126
+ // На hover точка шире контентной области элемента и в макете выходит за паддинг.
127
+ flex-shrink: 0;
128
+
129
+ box-sizing: border-box;
130
+ margin: 0;
131
+ padding: 0;
132
+ border: none;
133
+
134
+ background: transparent;
135
+ outline: 0;
136
+ outline-offset: 0;
137
+ }
@@ -0,0 +1,2 @@
1
+ export * from './Pagination';
2
+ export * from './PaginationSlider';
@@ -0,0 +1,35 @@
1
+ export const PAGINATION_SIZE = {
2
+ S: 's',
3
+ M: 'm',
4
+ } as const;
5
+
6
+ export const PAGINATION_SLIDER_SIZE = {
7
+ Xs: 'xs',
8
+ S: 's',
9
+ } as const;
10
+
11
+ export const VARIANT = {
12
+ Link: 'link',
13
+ Button: 'button',
14
+ } as const;
15
+
16
+ export const TEST_IDS = {
17
+ /** Корень `Pagination` (ставится потребителем через `data-test-id`). */
18
+ root: 'pagination',
19
+ /** Корень `PaginationSlider` (ставится потребителем через `data-test-id`). */
20
+ sliderRoot: 'pagination-slider',
21
+ /** Кнопка «предыдущая страница». */
22
+ prev: 'page-prev-button',
23
+ /** Кнопка «следующая страница». */
24
+ next: 'page-next-button',
25
+ /** Префикс для кнопки конкретной страницы. Полный id — `page-number-button-${page}`. */
26
+ pageNumberPrefix: 'page-number-button',
27
+ /** Префикс для кнопки «многоточие» (break). Полный id — `page-more-button-${start}-${end}`. */
28
+ pageMorePrefix: 'page-more-button',
29
+ /** Префикс для кнопки страницы в `PaginationSlider`. Полный id — `page-button-slider-${value}`. */
30
+ sliderItemPrefix: 'page-button-slider',
31
+ } as const;
32
+
33
+ export const getPageNumberTestId = (page: number) => `${TEST_IDS.pageNumberPrefix}-${page}`;
34
+ export const getPageMoreTestId = (start: number, end: number) => `${TEST_IDS.pageMorePrefix}-${start}-${end}`;
35
+ export const getSliderItemTestId = (value: number) => `${TEST_IDS.sliderItemPrefix}-${value}`;