@cloud-ru/ds-pagination 1.0.5 → 1.0.6-preview-d50a3da1.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.
@@ -1,4 +1,3 @@
1
- import { Button } from '@cloud-ru/ds-button';
2
1
  import { ChevronLeftSVG, ChevronRightSVG } from '@cloud-ru/ds-icons/interface/system';
3
2
  import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
4
3
  import { MouseEvent, useEffect, useRef, useState } from 'react';
@@ -118,8 +117,6 @@ export function Pagination({
118
117
  return null;
119
118
  };
120
119
 
121
- const buttonSize = size === PAGINATION_SIZE.M ? 'm' : 's';
122
-
123
120
  return (
124
121
  <PaginationContext.Provider value={{ size, variant }}>
125
122
  <nav
@@ -131,26 +128,20 @@ export function Pagination({
131
128
  >
132
129
  <ul className={styles.pagination} data-size={size}>
133
130
  <li>
134
- <Button
135
- view='simple'
136
- appearance='neutral'
131
+ <PaginationNumberItem
137
132
  icon={<ChevronLeftSVG />}
138
133
  onClick={handlePreviousPageButtonClick}
139
134
  disabled={page === FIRST_PAGE}
140
- size={buttonSize}
141
135
  aria-label='Previous page'
142
136
  data-test-id={TEST_IDS.prev}
143
137
  />
144
138
  </li>
145
139
  {entries.map(renderEntry)}
146
140
  <li>
147
- <Button
148
- view='simple'
149
- appearance='neutral'
141
+ <PaginationNumberItem
150
142
  icon={<ChevronRightSVG />}
151
143
  onClick={handleNextPageButtonClick}
152
144
  disabled={page === total}
153
- size={buttonSize}
154
145
  aria-label='Next page'
155
146
  data-test-id={TEST_IDS.next}
156
147
  />
@@ -1,11 +1,15 @@
1
1
  import { extractSupportProps, WithSupportProps } from '@cloud-ru/ds-utils';
2
- import { MouseEvent, Ref } from 'react';
2
+ import { MouseEvent, ReactNode, Ref } from 'react';
3
3
 
4
4
  import { usePaginationContext } from '../../contexts';
5
5
  import styles from './styles.module.scss';
6
6
 
7
7
  export type PaginationNumberItemProps = WithSupportProps<{
8
- label: number | string;
8
+ label?: number | string;
9
+ /** Иконка вместо текста — кнопки «назад» / «вперёд» (Figma `buttonPaginationS/M`). */
10
+ icon?: ReactNode;
11
+ disabled?: boolean;
12
+ 'aria-label'?: string;
9
13
  activated?: boolean;
10
14
  onClick(event: MouseEvent<HTMLButtonElement | HTMLAnchorElement>): void;
11
15
  href?: string;
@@ -14,6 +18,9 @@ export type PaginationNumberItemProps = WithSupportProps<{
14
18
 
15
19
  export function PaginationNumberItem({
16
20
  label,
21
+ icon,
22
+ disabled,
23
+ 'aria-label': ariaLabel,
17
24
  activated,
18
25
  onClick,
19
26
  href,
@@ -24,6 +31,26 @@ export function PaginationNumberItem({
24
31
 
25
32
  const stateLayer = activated ? 'activatedOnBackground' : 'emptyNeutralOnBackground';
26
33
 
34
+ if (icon) {
35
+ return (
36
+ <button
37
+ type='button'
38
+ className={styles.root}
39
+ onClick={onClick}
40
+ ref={setButtonRef as Ref<HTMLButtonElement>}
41
+ data-size={size}
42
+ {...extractSupportProps(rest)}
43
+ disabled={disabled}
44
+ data-disabled={disabled || undefined}
45
+ data-icon
46
+ aria-label={ariaLabel}
47
+ >
48
+ {!disabled && <span className={styles.stateLayer} aria-hidden data-state={stateLayer} />}
49
+ <span className={styles.icon}>{icon}</span>
50
+ </button>
51
+ );
52
+ }
53
+
27
54
  if (variant === 'link') {
28
55
  return (
29
56
  <a
@@ -4,6 +4,11 @@
4
4
 
5
5
  $sizes: 's', 'm';
6
6
 
7
+ $icon-sizes: (
8
+ 's': base.$sn-density-icon-xs,
9
+ 'm': base.$sn-density-icon-s,
10
+ );
11
+
7
12
  $typography: (
8
13
  's': base.$sn-regular-label-s,
9
14
  'm': base.$sn-regular-label-m,
@@ -35,6 +40,28 @@ $typography: (
35
40
  outline: 0;
36
41
  outline-offset: 0;
37
42
 
43
+ // Область нажатия на mobile ≥ `density.touchTarget.minSize` (44) по высоте; на desktop токен 0.
44
+ // По горизонтали не расширяем — кнопки стоят вплотную и зоны наехали бы на соседние.
45
+ &::after {
46
+ content: '';
47
+ position: absolute;
48
+ inset: min(0px, calc((100% - #{base.simple-var(base.$sn-density, 'touchTarget', 'minSize')}) / 2)) 0;
49
+ }
50
+
51
+ &[data-activated] {
52
+ color: base.$sn-theme-color-primary-text;
53
+ }
54
+
55
+ &[data-disabled] {
56
+ cursor: not-allowed;
57
+ opacity: base.$sn-theme-effect-opacity-disabled;
58
+ }
59
+
60
+ &:focus-visible {
61
+ outline: base.$sn-theme-color-available-complementary solid base.$sn-primitive-strokeWeight-strokeSemiBold;
62
+ outline-offset: base.$sn-primitive-strokeWeight-strokeSemiBold;
63
+ }
64
+
38
65
  @each $size in $sizes {
39
66
  &[data-size='#{$size}'] {
40
67
  @include base.composite-var(
@@ -46,6 +73,11 @@ $typography: (
46
73
  'itemPaginationNumber'
47
74
  );
48
75
 
76
+ .icon {
77
+ width: base.simple-var($icon-sizes, $size);
78
+ height: base.simple-var($icon-sizes, $size);
79
+ }
80
+
49
81
  .textWrapper {
50
82
  @include base.composite-var($typography, $size);
51
83
 
@@ -62,15 +94,6 @@ $typography: (
62
94
  }
63
95
  }
64
96
 
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
97
  @include m.has-state-layer-as-child(#{stateLayer});
75
98
  }
76
99
 
@@ -80,3 +103,15 @@ $typography: (
80
103
  justify-content: center;
81
104
  min-width: 0;
82
105
  }
106
+
107
+ .icon {
108
+ display: inline-flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+
112
+ // Иконки `@cloud-ru/ds-icons` ставят размер инлайн-стилем — ограничиваем размером слота.
113
+ svg {
114
+ max-width: 100%;
115
+ max-height: 100%;
116
+ }
117
+ }