@cloud-ru/ds-uikit-product-header 2.1.7 → 2.1.8-preview-d9442369.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.
- package/dist/cjs/components/ListCustom/helperComponents/ContentWrapperCustom/ContentWrapperCustom.d.ts +33 -0
- package/dist/cjs/components/ListCustom/helperComponents/ContentWrapperCustom/ContentWrapperCustom.js +24 -0
- package/dist/cjs/components/ListCustom/helperComponents/ContentWrapperCustom/index.d.ts +1 -0
- package/dist/cjs/components/ListCustom/helperComponents/ContentWrapperCustom/index.js +17 -0
- package/dist/cjs/components/ListCustom/helperComponents/ContentWrapperCustom/styles.module.css +168 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/ListItemCustom.d.ts +6 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/ListItemCustom.js +42 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/constants.d.ts +6 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/constants.js +6 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/index.d.ts +3 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/index.js +19 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/styles.module.css +236 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/types.d.ts +13 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemCustom/types.js +2 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.d.ts +7 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.js +23 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemGroupCustom/index.d.ts +1 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemGroupCustom/index.js +17 -0
- package/dist/cjs/components/ListCustom/helperComponents/ListItemGroupCustom/styles.module.css +34 -0
- package/dist/cjs/components/ListCustom/index.d.ts +3 -0
- package/dist/cjs/components/ListCustom/index.js +19 -0
- package/dist/cjs/components/ListCustom/renderListCustom.d.ts +4 -0
- package/dist/cjs/components/ListCustom/renderListCustom.js +14 -0
- package/dist/cjs/components/MainMenu/helperComponents/Favorites/Favorites.js +2 -3
- package/dist/cjs/components/UserMenu/UserMenu.js +2 -4
- package/dist/cjs/components/UserMenu/hooks/useThemeItem.js +1 -4
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.js +1 -0
- package/dist/cjs/style.css +438 -0
- package/dist/esm/components/ListCustom/helperComponents/ContentWrapperCustom/ContentWrapperCustom.d.ts +33 -0
- package/dist/esm/components/ListCustom/helperComponents/ContentWrapperCustom/ContentWrapperCustom.js +18 -0
- package/dist/esm/components/ListCustom/helperComponents/ContentWrapperCustom/index.d.ts +1 -0
- package/dist/esm/components/ListCustom/helperComponents/ContentWrapperCustom/index.js +1 -0
- package/dist/esm/components/ListCustom/helperComponents/ContentWrapperCustom/styles.module.css +168 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/ListItemCustom.d.ts +6 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/ListItemCustom.js +36 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/constants.d.ts +6 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/constants.js +3 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/index.d.ts +3 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/index.js +3 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/styles.module.css +236 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/types.d.ts +13 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemCustom/types.js +1 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.d.ts +7 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.js +17 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemGroupCustom/index.d.ts +1 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemGroupCustom/index.js +1 -0
- package/dist/esm/components/ListCustom/helperComponents/ListItemGroupCustom/styles.module.css +34 -0
- package/dist/esm/components/ListCustom/index.d.ts +3 -0
- package/dist/esm/components/ListCustom/index.js +3 -0
- package/dist/esm/components/ListCustom/renderListCustom.d.ts +4 -0
- package/dist/esm/components/ListCustom/renderListCustom.js +10 -0
- package/dist/esm/components/MainMenu/helperComponents/Favorites/Favorites.js +2 -3
- package/dist/esm/components/UserMenu/UserMenu.js +2 -4
- package/dist/esm/components/UserMenu/hooks/useThemeItem.js +1 -4
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.js +1 -0
- package/dist/esm/style.css +438 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +31 -28
- package/src/components/ListCustom/helperComponents/ContentWrapperCustom/ContentWrapperCustom.tsx +106 -0
- package/src/components/ListCustom/helperComponents/ContentWrapperCustom/index.ts +1 -0
- package/src/components/ListCustom/helperComponents/ContentWrapperCustom/styles.module.scss +205 -0
- package/src/components/ListCustom/helperComponents/ListItemCustom/ListItemCustom.tsx +146 -0
- package/src/components/ListCustom/helperComponents/ListItemCustom/constants.ts +4 -0
- package/src/components/ListCustom/helperComponents/ListItemCustom/index.ts +3 -0
- package/src/components/ListCustom/helperComponents/ListItemCustom/styles.module.scss +218 -0
- package/src/components/ListCustom/helperComponents/ListItemCustom/types.ts +18 -0
- package/src/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.tsx +56 -0
- package/src/components/ListCustom/helperComponents/ListItemGroupCustom/index.ts +1 -0
- package/src/components/ListCustom/helperComponents/ListItemGroupCustom/styles.module.scss +52 -0
- package/src/components/ListCustom/index.ts +3 -0
- package/src/components/ListCustom/renderListCustom.tsx +13 -0
- package/src/components/ListCustom/specs/readme.md +197 -0
- package/src/components/MainMenu/helperComponents/Favorites/Favorites.tsx +8 -3
- package/src/components/UserMenu/UserMenu.tsx +5 -5
- package/src/components/UserMenu/hooks/useThemeItem.tsx +1 -4
- package/src/components/index.ts +1 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
@use '@cloud-ru/ds-materials' as m;
|
|
2
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/components/list.module' as list;
|
|
3
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
|
|
4
|
+
|
|
5
|
+
// Figma listItemCustom (17001:42063): selectionMode × checked × disabled на anatomy size/s.
|
|
6
|
+
|
|
7
|
+
$level: var(--level, 0);
|
|
8
|
+
$itemPaddingHorizontal: list.$sn-list-anatomy-size-s-listItem-paddingHorizontal;
|
|
9
|
+
$itemGap: list.$sn-list-anatomy-size-s-listItem-gap;
|
|
10
|
+
$nestingLevelStep: list.$sn-list-anatomy-size-s-listItem-nestingLevel-paddingLeft;
|
|
11
|
+
|
|
12
|
+
.stateLayer {
|
|
13
|
+
visibility: initial;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.listItem {
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
|
|
19
|
+
position: relative;
|
|
20
|
+
z-index: 1;
|
|
21
|
+
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: row;
|
|
24
|
+
gap: $itemGap;
|
|
25
|
+
align-items: flex-start;
|
|
26
|
+
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
width: max-content;
|
|
29
|
+
min-width: 100%;
|
|
30
|
+
margin: 0;
|
|
31
|
+
padding: base.$sn-primitive-dimension-0 $itemPaddingHorizontal;
|
|
32
|
+
border: none;
|
|
33
|
+
|
|
34
|
+
color: base.$sn-theme-color-available-version-textMain;
|
|
35
|
+
text-align: start;
|
|
36
|
+
|
|
37
|
+
appearance: unset;
|
|
38
|
+
background-color: transparent;
|
|
39
|
+
outline: none;
|
|
40
|
+
|
|
41
|
+
&[data-level-one],
|
|
42
|
+
&[data-level-more-one] {
|
|
43
|
+
padding-left: calc(#{$itemPaddingHorizontal} + #{$level} * #{$nestingLevelStep} + #{$itemGap});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
&:not([data-inactive], [data-disabled]) {
|
|
47
|
+
&[data-focused],
|
|
48
|
+
&:focus-visible {
|
|
49
|
+
outline: base.$sn-theme-color-available-complementary solid base.$sn-primitive-strokeWeight-strokeSemiBold;
|
|
50
|
+
outline-offset: calc(0px - #{base.$sn-primitive-strokeWeight-strokeSemiBold});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
&[data-non-pointer] {
|
|
55
|
+
cursor: inherit;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Figma markerWrapper: absolute left strip, only when selectionMode=single && checked.
|
|
60
|
+
.marker {
|
|
61
|
+
pointer-events: none;
|
|
62
|
+
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 0;
|
|
65
|
+
bottom: 0;
|
|
66
|
+
left: 0;
|
|
67
|
+
|
|
68
|
+
display: flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
width: list.$sn-list-anatomy-size-s-listItem-markerWrapper-width;
|
|
73
|
+
min-width: list.$sn-list-anatomy-size-s-listItem-markerWrapper-width;
|
|
74
|
+
max-width: list.$sn-list-anatomy-size-s-listItem-markerWrapper-width;
|
|
75
|
+
padding-top: list.$sn-list-anatomy-size-s-listItem-markerWrapper-marker-paddingVertical;
|
|
76
|
+
padding-bottom: list.$sn-list-anatomy-size-s-listItem-markerWrapper-marker-paddingVertical;
|
|
77
|
+
|
|
78
|
+
&::before {
|
|
79
|
+
content: '';
|
|
80
|
+
|
|
81
|
+
flex: 1 0 0;
|
|
82
|
+
|
|
83
|
+
width: 100%;
|
|
84
|
+
min-width: base.$sn-primitive-dimension-0;
|
|
85
|
+
height: 100%;
|
|
86
|
+
border-top-right-radius: list.$sn-list-anatomy-size-s-listItem-markerWrapper-borderTopRightRadius;
|
|
87
|
+
border-bottom-right-radius: list.$sn-list-anatomy-size-s-listItem-markerWrapper-borderBottomRightRadius;
|
|
88
|
+
|
|
89
|
+
background-color: base.$sn-theme-color-primary-accent;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
&[data-disabled] {
|
|
93
|
+
opacity: base.$sn-theme-effect-opacity-disabled;
|
|
94
|
+
|
|
95
|
+
&::before {
|
|
96
|
+
background-color: base.$sn-theme-color-neutral-accent;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Figma centeredWrapper вокруг checkboxS: padding 4.
|
|
102
|
+
.checkbox {
|
|
103
|
+
display: flex;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
align-items: flex-start;
|
|
106
|
+
padding: base.$sn-primitive-dimension-4;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Figma centeredWrapper вокруг groupIndicator (17001:42268): padding 4 4 4 0.
|
|
110
|
+
.expandCenteredWrapper {
|
|
111
|
+
display: flex;
|
|
112
|
+
flex-shrink: 0;
|
|
113
|
+
align-items: center;
|
|
114
|
+
padding: base.$sn-primitive-dimension-4 base.$sn-primitive-dimension-4 base.$sn-primitive-dimension-4
|
|
115
|
+
base.$sn-primitive-dimension-0;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Figma groupIndicator (17001:42269) на size/s: сетка icon-s (24).
|
|
119
|
+
.groupIndicator {
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
|
|
122
|
+
display: flex;
|
|
123
|
+
flex-shrink: 0;
|
|
124
|
+
align-items: center;
|
|
125
|
+
justify-content: center;
|
|
126
|
+
|
|
127
|
+
box-sizing: border-box;
|
|
128
|
+
width: base.$sn-density-icon-s;
|
|
129
|
+
height: base.$sn-density-icon-s;
|
|
130
|
+
|
|
131
|
+
color: base.$sn-theme-color-available-version-textSecondary;
|
|
132
|
+
|
|
133
|
+
fill: currentcolor;
|
|
134
|
+
|
|
135
|
+
svg {
|
|
136
|
+
width: 100%;
|
|
137
|
+
max-width: 100%;
|
|
138
|
+
height: 100%;
|
|
139
|
+
max-height: 100%;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
&[data-interactive] {
|
|
143
|
+
pointer-events: auto;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
&[data-open] {
|
|
148
|
+
border-radius: base.$sn-density-radius-s;
|
|
149
|
+
color: base.$sn-theme-color-available-version-textMain;
|
|
150
|
+
background: base.$sn-theme-color-neutral-decorTransparent;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
&[data-disabled] {
|
|
154
|
+
opacity: base.$sn-theme-effect-opacity-disabled;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.itemWrapper {
|
|
159
|
+
position: relative;
|
|
160
|
+
z-index: 0;
|
|
161
|
+
|
|
162
|
+
min-width: 100%;
|
|
163
|
+
margin: 0;
|
|
164
|
+
padding: 0;
|
|
165
|
+
border-radius: list.$sn-list-anatomy-size-s-listItem-borderRadius;
|
|
166
|
+
|
|
167
|
+
@include m.has-state-layer-as-child(#{stateLayer});
|
|
168
|
+
|
|
169
|
+
&[data-disabled],
|
|
170
|
+
&:disabled {
|
|
171
|
+
cursor: not-allowed;
|
|
172
|
+
background-color: transparent;
|
|
173
|
+
outline: none;
|
|
174
|
+
|
|
175
|
+
.listItem {
|
|
176
|
+
cursor: not-allowed;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
&:not([data-inactive], [data-disabled]) {
|
|
181
|
+
&[data-open],
|
|
182
|
+
&[data-focused] {
|
|
183
|
+
.stateLayer[data-state='emptyNeutralOnBackground'] {
|
|
184
|
+
background: base.$sn-theme-color-material-stateLayer-neutral-onBackground-hovered-filled;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.stateLayer[data-state='activatedOnBackground'] {
|
|
188
|
+
background: base.$sn-theme-color-material-stateLayer-activated-hovered-filled;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
&:active {
|
|
193
|
+
.stateLayer[data-state='emptyNeutralOnBackground'] {
|
|
194
|
+
background: base.$sn-theme-color-material-stateLayer-neutral-onBackground-pressed-filled;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.stateLayer[data-state='activatedOnBackground'] {
|
|
198
|
+
background: base.$sn-theme-color-material-stateLayer-activated-pressed-filled;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
&[data-inactive] {
|
|
204
|
+
&:hover,
|
|
205
|
+
&:active {
|
|
206
|
+
.stateLayer[data-state='emptyNeutralOnBackground'],
|
|
207
|
+
.stateLayer[data-state='activatedOnBackground'] {
|
|
208
|
+
background: base.$sn-theme-color-material-stateLayer-neutral-onBackground-defaultEmpty-filled;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
&[data-checked][data-selection-mode='single']:not([data-disabled]) {
|
|
214
|
+
.listItem:focus-visible {
|
|
215
|
+
outline-color: base.$sn-theme-color-primary-accent;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ItemRenderProps, Mode } from '@cloud-ru/ds-list';
|
|
2
|
+
|
|
3
|
+
export type SelectionMode = Mode;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Пропы оболочки. Совместимы с `ItemRenderProps` списка (адаптер — spread).
|
|
7
|
+
* Для Playground часть полей опциональна: `contentNode` / `selectionMode` / `size` / `tabIndex` / `level`.
|
|
8
|
+
*/
|
|
9
|
+
export type ListItemCustomProps = Omit<
|
|
10
|
+
ItemRenderProps,
|
|
11
|
+
'contentNode' | 'selectionMode' | 'size' | 'tabIndex' | 'level'
|
|
12
|
+
> & {
|
|
13
|
+
contentNode?: ItemRenderProps['contentNode'];
|
|
14
|
+
selectionMode?: Mode;
|
|
15
|
+
size?: ItemRenderProps['size'];
|
|
16
|
+
tabIndex?: number;
|
|
17
|
+
level?: number;
|
|
18
|
+
};
|
package/src/components/ListCustom/helperComponents/ListItemGroupCustom/ListItemGroupCustom.tsx
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Divider, VARIANT } from '@cloud-ru/ds-divider';
|
|
2
|
+
import { GroupItemRenderProps } from '@cloud-ru/ds-list';
|
|
3
|
+
import { CSSProperties } from 'react';
|
|
4
|
+
|
|
5
|
+
import { ContentWrapperCustom } from '../ContentWrapperCustom';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
|
|
8
|
+
export type ListItemGroupCustomProps = GroupItemRenderProps;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Кастомная оболочка заголовка группы `List`/`Droplist` (Figma `listItemGroupCustom`).
|
|
12
|
+
* Передаётся в `groupItemRender`: subtitle / subtitleTertiary / divider.
|
|
13
|
+
*/
|
|
14
|
+
export function ListItemGroupCustom({
|
|
15
|
+
label,
|
|
16
|
+
beforeContent,
|
|
17
|
+
truncate,
|
|
18
|
+
groupVariant = 'subtitleTertiary',
|
|
19
|
+
divider,
|
|
20
|
+
selectButtonNode,
|
|
21
|
+
level,
|
|
22
|
+
}: ListItemGroupCustomProps) {
|
|
23
|
+
if (label) {
|
|
24
|
+
return (
|
|
25
|
+
<div
|
|
26
|
+
className={styles.root}
|
|
27
|
+
data-level-one={level === 1 || undefined}
|
|
28
|
+
data-level-more-one={level > 1 || undefined}
|
|
29
|
+
style={{ '--level': level } as CSSProperties}
|
|
30
|
+
>
|
|
31
|
+
<ContentWrapperCustom
|
|
32
|
+
layout='group'
|
|
33
|
+
beforeContent={beforeContent}
|
|
34
|
+
label={label}
|
|
35
|
+
truncate={truncate ? { variant: truncate.variant, label: 1 } : undefined}
|
|
36
|
+
groupVariant={groupVariant}
|
|
37
|
+
selectButton={selectButtonNode}
|
|
38
|
+
/>
|
|
39
|
+
|
|
40
|
+
{divider && (
|
|
41
|
+
<Divider className={styles.divider} variant={groupVariant === 'subtitle' ? VARIANT.Regular : VARIANT.Thin} />
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (divider) {
|
|
48
|
+
return (
|
|
49
|
+
<div className={styles.dividerOnly}>
|
|
50
|
+
<Divider variant={VARIANT.Regular} />
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ListItemGroupCustom';
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/components/list.module' as list;
|
|
2
|
+
@use '@cloud-ru/ds-figma-variables/build/scss/styles/styles.module' as base;
|
|
3
|
+
|
|
4
|
+
// Figma listItemGroupCustom (17001:43680): subtitle / subtitleTertiary / divider.
|
|
5
|
+
|
|
6
|
+
$level: var(--level, 0);
|
|
7
|
+
$groupPaddingHorizontal: base.$sn-primitive-dimension-8;
|
|
8
|
+
$itemGap: list.$sn-list-anatomy-size-s-listItem-gap;
|
|
9
|
+
$nestingLevelStep: list.$sn-list-anatomy-size-s-listItem-nestingLevel-paddingLeft;
|
|
10
|
+
|
|
11
|
+
.root {
|
|
12
|
+
position: relative;
|
|
13
|
+
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: $itemGap;
|
|
16
|
+
align-items: center;
|
|
17
|
+
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
width: 100%;
|
|
20
|
+
min-width: 100%;
|
|
21
|
+
padding: list.$sn-list-anatomy-size-s-subtitle-paddingTop $groupPaddingHorizontal
|
|
22
|
+
list.$sn-list-anatomy-size-s-listItem-paddingVertical;
|
|
23
|
+
|
|
24
|
+
&:focus-visible {
|
|
25
|
+
outline: base.$sn-theme-color-available-complementary solid base.$sn-primitive-strokeWeight-strokeSemiBold;
|
|
26
|
+
outline-offset: calc(0px - #{base.$sn-primitive-strokeWeight-strokeSemiBold});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
&[data-level-one],
|
|
30
|
+
&[data-level-more-one] {
|
|
31
|
+
padding-left: calc(#{$groupPaddingHorizontal} + #{$level} * #{$nestingLevelStep} + #{$itemGap});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.dividerOnly {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: flex-end;
|
|
38
|
+
|
|
39
|
+
box-sizing: border-box;
|
|
40
|
+
width: 100%;
|
|
41
|
+
padding: list.$sn-list-anatomy-size-s-listItem-paddingVertical $groupPaddingHorizontal;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.divider {
|
|
45
|
+
position: absolute;
|
|
46
|
+
top: 0;
|
|
47
|
+
right: 0;
|
|
48
|
+
left: 0;
|
|
49
|
+
|
|
50
|
+
width: auto;
|
|
51
|
+
min-width: 0;
|
|
52
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { GroupItemRenderProps, ItemRenderProps } from '@cloud-ru/ds-list';
|
|
2
|
+
|
|
3
|
+
import { ListItemCustom } from './helperComponents/ListItemCustom';
|
|
4
|
+
import { ListItemGroupCustom } from './helperComponents/ListItemGroupCustom';
|
|
5
|
+
|
|
6
|
+
/** Стабильные рендеры для `itemRender` / `groupItemRender` у `List`/`Droplist`. */
|
|
7
|
+
export function renderListItemCustom(props: ItemRenderProps) {
|
|
8
|
+
return <ListItemCustom {...props} />;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function renderListItemGroupCustom(props: GroupItemRenderProps) {
|
|
12
|
+
return <ListItemGroupCustom {...props} />;
|
|
13
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
# ListCustom — устройство и логика
|
|
2
|
+
|
|
3
|
+
Спецификация кастомных оболочек строки и заголовка группы для `List` / `Droplist` в хедере. Ось размера мастера — только `s` (Figma `list/anatomy/size/s`).
|
|
4
|
+
|
|
5
|
+
## Зачем
|
|
6
|
+
|
|
7
|
+
Стандартный `BaseItem` / `Separator` из `@cloud-ru/ds-list` рисуют раскладку списка. Пропы `contentRender` и `itemWrapRender` не позволяют заменить отступы и типографику оболочки строки, не затрагивая остальных потребителей List.
|
|
8
|
+
|
|
9
|
+
В хедере нужна раскладка мастеров `listItemCustom` / `listItemGroupCustom` / `contentWrapperCustom` (файл Figma `VcK2QJzt2cWJiHzTzpKAuZ`). Для этого в List добавлены опциональные слоты:
|
|
10
|
+
|
|
11
|
+
| Проп List | Когда задан | Когда не задан |
|
|
12
|
+
|-----------|-------------|----------------|
|
|
13
|
+
| `itemRender` | вместо JSX строки `BaseItem` | дефолтная разметка `BaseItem` |
|
|
14
|
+
| `groupItemRender` | вместо разметки `Separator` | дефолтный `Separator` |
|
|
15
|
+
|
|
16
|
+
Логика выбора, клавиатуры, вложенных списков и закрытия дроплиста остаётся в List. Оболочки отвечают только за визуальную раскладку и индикаторы выбора (маркер, чекбокс, подложка) по макету хедера.
|
|
17
|
+
|
|
18
|
+
`contentRender` и `itemWrapRender` не меняют сигнатуру и место в дереве: `itemWrapRender` по-прежнему оборачивает результат `itemRender` (например, ссылка профиля в UserMenu).
|
|
19
|
+
|
|
20
|
+
## Состав модуля
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
ListCustom/
|
|
24
|
+
├── index.ts # публичный экспорт
|
|
25
|
+
├── renderListCustom.tsx # адаптеры ItemRenderProps → оболочки
|
|
26
|
+
├── specs/readme.md # этот файл
|
|
27
|
+
└── helperComponents/
|
|
28
|
+
├── ContentWrapperCustom/ # приватный слот before + текст + after
|
|
29
|
+
├── ListItemCustom/ # публичная оболочка строки
|
|
30
|
+
└── ListItemGroupCustom/ # публичная оболочка группы
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Публичный API пакета:** `ListItemCustom`, `ListItemGroupCustom`, `renderListItemCustom`, `renderListItemGroupCustom`, константа `SELECTION_MODE` / тип `SelectionMode`.
|
|
34
|
+
|
|
35
|
+
`ContentWrapperCustom` наружу не экспортируется.
|
|
36
|
+
|
|
37
|
+
## Поток данных
|
|
38
|
+
|
|
39
|
+
```mermaid
|
|
40
|
+
flowchart TD
|
|
41
|
+
Droplist["List / Droplist"] --> BaseItem
|
|
42
|
+
Droplist --> Separator
|
|
43
|
+
BaseItem -->|"itemRender не задан"| DefaultRow["дефолтная строка BaseItem"]
|
|
44
|
+
BaseItem -->|"itemRender задан"| Adapter["renderListItemCustom"]
|
|
45
|
+
Adapter --> ListItemCustom
|
|
46
|
+
ListItemCustom --> CWCItem["ContentWrapperCustom layout=item"]
|
|
47
|
+
Separator -->|"groupItemRender не задан"| DefaultGroup["дефолтный Separator"]
|
|
48
|
+
Separator -->|"groupItemRender задан"| AdapterG["renderListItemGroupCustom"]
|
|
49
|
+
AdapterG --> ListItemGroupCustom
|
|
50
|
+
ListItemGroupCustom --> CWCGroup["ContentWrapperCustom layout=group"]
|
|
51
|
+
ListItemCustom --> Wrap["itemWrapRender если задан"]
|
|
52
|
+
DefaultRow --> Wrap
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Подключение в продукте:
|
|
56
|
+
|
|
57
|
+
- `UserMenu` — `itemRender` / `groupItemRender` + `size="s"` на Droplist.
|
|
58
|
+
- `Favorites` — то же на дроплисте действий избранного.
|
|
59
|
+
|
|
60
|
+
## ContentWrapperCustom (приватный)
|
|
61
|
+
|
|
62
|
+
Общий внутренний блок `beforeContent` + текст + `afterContent`. Аналог связки `contentWrapper` + `ItemContent` списка, с отступами мастера хедера (gap `primitive/6`, padding `4` / `8` для `layout=item`).
|
|
63
|
+
|
|
64
|
+
| Проп | Назначение |
|
|
65
|
+
|------|------------|
|
|
66
|
+
| `layout` | `'item'` — строка списка; `'group'` — заголовок группы (другие отступы и типографика) |
|
|
67
|
+
| `label` / `caption` / `description` | структурный текст; усечение через `TruncateString` |
|
|
68
|
+
| `contentNode` | готовый узел вместо структурного текста (например результат `contentRender`) |
|
|
69
|
+
| `beforeContent` / `afterContent` | иконочные / произвольные слоты |
|
|
70
|
+
| `selectButton` | кнопка массового выбора в заголовке группы |
|
|
71
|
+
| `groupVariant` | `'subtitle'` \| `'subtitleTertiary'` — цвет подписи группы |
|
|
72
|
+
| `disabled` | пониженная opacity слотов |
|
|
73
|
+
|
|
74
|
+
Структурный текст рендерится только если есть `label` и нет `contentNode`. `caption` и `description` показываются только при `layout=item`.
|
|
75
|
+
|
|
76
|
+
## ListItemCustom
|
|
77
|
+
|
|
78
|
+
Оболочка строки (Figma `listItemCustom`, узел `17001:42063`). Визуальный аналог `BaseItem`, но индикаторы выбора и state layer собраны внутри по декларативным пропам мастера.
|
|
79
|
+
|
|
80
|
+
### Пропы выбора (Figma)
|
|
81
|
+
|
|
82
|
+
| Проп | Значения | Эффект |
|
|
83
|
+
|------|----------|--------|
|
|
84
|
+
| `selectionMode` | `'none'` \| `'single'` \| `'multiple'` (дефолт `none`; из List приходит явно) | single → маркер; multiple → чекбокс; none — без индикаторов |
|
|
85
|
+
| `checked` | `boolean` | выбранность; для single ещё включает activated-подложку |
|
|
86
|
+
| `disabled` | `boolean` | курсор, opacity контента, цвет маркера; state layer не рисуется |
|
|
87
|
+
|
|
88
|
+
Тип пропсов совместим с `ItemRenderProps` из `@cloud-ru/ds-list` (`SELECTION_MODE` = `MODE` списка).
|
|
89
|
+
|
|
90
|
+
### Логика индикаторов выбора
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
showMarker = single && checked && !isParentNode && !switchControl
|
|
94
|
+
showCheckbox = multiple && !switchControl
|
|
95
|
+
|
|
96
|
+
stateLayer = activatedOnBackground только если single && checked && !disabled && !isParentNode && !switchControl
|
|
97
|
+
иначе emptyNeutralOnBackground (и только если !disabled)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- **single + checked** — вертикальный маркер слева (`primary/accent`; при `disabled` — `neutral/accent` + opacity).
|
|
101
|
+
- **multiple** — `Checkbox` size `s` (мастер `checkboxS`), всегда в потоке строки, независимо от `checked`.
|
|
102
|
+
- **activated**-подложка только у single-выбранной активной строки; у multiple фон не меняется при выборе.
|
|
103
|
+
- Маркер и activated не показываются у parent-строки и при наличии `switchControl` (паритет с правилами `BaseItem`).
|
|
104
|
+
|
|
105
|
+
Фокус — `outline` на интерактивном `<li>`, без переноса `focusedFrame` из Figma.
|
|
106
|
+
|
|
107
|
+
### Контент строки
|
|
108
|
+
|
|
109
|
+
1. Если `contentFromRender` — структурный разбор пропускается, в слот текста идёт `contentNode`.
|
|
110
|
+
2. Иначе `content` вида `{ label, caption?, description? }` или примитив → поля в `ContentWrapperCustom`.
|
|
111
|
+
3. Слоты снаружи текста: `dragHandle` → маркер/чекбокс → `ContentWrapperCustom` → `switchControl` → `expandIcon`.
|
|
112
|
+
|
|
113
|
+
Обработчики (`onClick`, `onKeyDown`, …), `itemRef`, `tabIndex`, `data-test-id` вешаются на `<li>`; выбор и клавиатура по-прежнему живут в `BaseItem`, оболочка только принимает уже подготовленные колбэки.
|
|
114
|
+
|
|
115
|
+
### Вложенность
|
|
116
|
+
|
|
117
|
+
`level` задаёт CSS-переменную `--level` и атрибуты `data-level-one` / `data-level-more-one` для отступа nesting из токенов списка size `s`.
|
|
118
|
+
|
|
119
|
+
## ListItemGroupCustom
|
|
120
|
+
|
|
121
|
+
Оболочка заголовка группы (Figma `listItemGroupCustom`, узел `17001:43680`). Тип пропсов = `GroupItemRenderProps` из `@cloud-ru/ds-list`.
|
|
122
|
+
|
|
123
|
+
### Figma-триада vs API: почему нет `groupVariant: 'divider'`
|
|
124
|
+
|
|
125
|
+
В мастере Figma у группы три **презентации**: подпись `subtitle`, подпись `subtitleTertiary`, только линия (divider-only). В коде это **не** один enum из трёх значений.
|
|
126
|
+
|
|
127
|
+
| Ось | API | Роль |
|
|
128
|
+
|-----|-----|------|
|
|
129
|
+
| Стиль подписи | `groupVariant?: 'subtitle' \| 'subtitleTertiary'` | цвет / вес текста заголовка |
|
|
130
|
+
| Линия | `divider?: boolean` | разделитель над/под группой (в оболочке хедера — под заголовком) |
|
|
131
|
+
|
|
132
|
+
Третий вид макета (только линия) — ветка рендера **`divider: true` без `label`**, а не третье значение `groupVariant`.
|
|
133
|
+
|
|
134
|
+
Почему так, а не 1:1 с Figma:
|
|
135
|
+
|
|
136
|
+
1. **Оси ортогональны.** Уже есть кейс «подпись + линия»: `groupVariant: 'subtitle'` и одновременно `divider: true`. Один enum `'divider'` эту композицию не выражает без второго пропа.
|
|
137
|
+
2. **Совпадение с `@cloud-ru/ds-list`.** `Separator`, `CommonGroupItem` и `GroupItemRenderProps` уже разделяют `groupVariant` и `divider`; оболочка хедера повторяет тот же контракт.
|
|
138
|
+
3. **Divider-only — другая структура, не стиль текста.** Без `label` не применяется типографика `subtitle` / `subtitleTertiary`; рисуется только `Divider` (`variant=Regular`). Класть это в `groupVariant` смешивало бы «цвет подписи» и «тип строки».
|
|
139
|
+
|
|
140
|
+
Итого: в Controls / данных списка достаточно связки **`groupVariant` + `divider`** (+ наличие или отсутствие `label`). Значение `'divider'` в `groupVariant` в API не добавляется.
|
|
141
|
+
|
|
142
|
+
Ветки рендера:
|
|
143
|
+
|
|
144
|
+
| Условие | Результат |
|
|
145
|
+
|---------|-----------|
|
|
146
|
+
| есть `label` | `ContentWrapperCustom` с `layout=group`; при `divider` — ещё `Divider` под заголовком (`Regular` для `subtitle`, `Thin` для `subtitleTertiary`) |
|
|
147
|
+
| нет `label`, есть `divider` | только разделитель (`dividerOnly`) |
|
|
148
|
+
| иначе | `null` |
|
|
149
|
+
|
|
150
|
+
`selectButtonNode` из List передаётся в `ContentWrapperCustom` как `selectButton`. Уровень вложенности — тот же механизм `--level`, что у строки.
|
|
151
|
+
|
|
152
|
+
## Адаптеры `renderList*`
|
|
153
|
+
|
|
154
|
+
Стабильные функции для пропсов List (ссылка не меняется между рендерами). Оба — прямой spread:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
export function renderListItemCustom(props: ItemRenderProps) {
|
|
158
|
+
return <ListItemCustom {...props} />;
|
|
159
|
+
}
|
|
160
|
+
export function renderListItemGroupCustom(props: GroupItemRenderProps) {
|
|
161
|
+
return <ListItemGroupCustom {...props} />;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
`BaseItem` кладёт в `ItemRenderProps.selectionMode` значение из `SelectionProvider` (`none` / `single` / `multiple`) и **не** передаёт готовые узлы маркера/чекбокса — их рисует оболочка.
|
|
166
|
+
|
|
167
|
+
## Связь с List
|
|
168
|
+
|
|
169
|
+
| Слой | Ответственность |
|
|
170
|
+
|------|-----------------|
|
|
171
|
+
| `SelectionProvider` / `BaseItem` | режим выбора, значение, клик; сбор `ItemRenderProps` с `selectionMode` |
|
|
172
|
+
| `renderListItemCustom` | `<ListItemCustom {...props} />` |
|
|
173
|
+
| `ListItemCustom` | раскладка и индикаторы выбора по `selectionMode` / `checked` / `disabled` |
|
|
174
|
+
| `itemWrapRender` | опциональная обёртка поверх уже собранной оболочки (ссылка и т.п.) |
|
|
175
|
+
|
|
176
|
+
Ось `size` у оболочек не дублируется: отступы зашиты под мастер `s`. Потребители в хедере ставят `size="s"` на List/Droplist, чтобы служебные узлы списка (если останутся) совпадали по шкале.
|
|
177
|
+
|
|
178
|
+
## Стори
|
|
179
|
+
|
|
180
|
+
| Story | Назначение |
|
|
181
|
+
|-------|------------|
|
|
182
|
+
| `ListItemCustom.Playground` | Controls: `selectionMode`, `checked`, `disabled`, `asParentGroup`, `asNested` |
|
|
183
|
+
| `ListItemCustom.VisualMatrix` | две таблицы: `single` и `multiple`; строки enabled/disabled × колонки unchecked/checked |
|
|
184
|
+
| `ListItemGroupCustom.Playground` | Controls: `groupVariant` + `divider` (+ story-only `withLabel` для divider-only) |
|
|
185
|
+
| `ListItemGroupCustom.VisualMatrix` | вертикально три презентации Figma: subtitle / subtitleTertiary / divider-only |
|
|
186
|
+
| `ListCustom.Playground` | smoke: List + оба рендера (legacy, будет удалена позже) |
|
|
187
|
+
| `ListCustom.VisualMatrix` | объединённая матрица (legacy, будет удалена позже) |
|
|
188
|
+
|
|
189
|
+
## Figma (ориентиры)
|
|
190
|
+
|
|
191
|
+
| Мастер | Node |
|
|
192
|
+
|--------|------|
|
|
193
|
+
| `listItemCustom` | `17001:42063` |
|
|
194
|
+
| `contentWrapperCustom` | `17001:43602` |
|
|
195
|
+
| `listItemGroupCustom` | `17001:43680` |
|
|
196
|
+
|
|
197
|
+
Варианты строки: `selectionMode` × `checked` × `disabled` (восемь комбинаций single/multiple).
|
|
@@ -6,8 +6,8 @@ import { useEventHandler, useValueControl } from '@cloud-ru/ds-utils';
|
|
|
6
6
|
import cn from 'classnames';
|
|
7
7
|
import { MouseEvent, useMemo } from 'react';
|
|
8
8
|
|
|
9
|
-
import { useDesktopComfortClassName } from '../../../../hooks/useDesktopComfortClassName';
|
|
10
9
|
import { headerLocale } from '../../../../locale';
|
|
10
|
+
import { renderListItemCustom, renderListItemGroupCustom } from '../../../ListCustom';
|
|
11
11
|
import { FavoriteProps, InnerLink } from '../../types';
|
|
12
12
|
import { resolveInnerLinksByIds } from '../../utils';
|
|
13
13
|
import { Content, FavoritesItemsSkeleton } from './components';
|
|
@@ -28,7 +28,6 @@ export type FavoritesProps = {
|
|
|
28
28
|
export function Favorites({ favorite, servicesById, headerClassName, isMobile }: FavoritesProps) {
|
|
29
29
|
const { t } = headerLocale.useTranslations();
|
|
30
30
|
const loading = favorite.loading;
|
|
31
|
-
const comfortClassName = useDesktopComfortClassName();
|
|
32
31
|
|
|
33
32
|
const [segment, setSegment] = useValueControl<FavoritesSegment>({
|
|
34
33
|
value: favorite.segment,
|
|
@@ -82,7 +81,13 @@ export function Favorites({ favorite, servicesById, headerClassName, isMobile }:
|
|
|
82
81
|
/>
|
|
83
82
|
|
|
84
83
|
{favorite.actions && (
|
|
85
|
-
<Droplist
|
|
84
|
+
<Droplist
|
|
85
|
+
{...favorite.actions}
|
|
86
|
+
size='s'
|
|
87
|
+
closeDroplistOnItemClick
|
|
88
|
+
itemRender={renderListItemCustom}
|
|
89
|
+
groupItemRender={renderListItemGroupCustom}
|
|
90
|
+
>
|
|
86
91
|
<Button
|
|
87
92
|
view='simple'
|
|
88
93
|
size='m'
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { Avatar } from '@cloud-ru/ds-avatar';
|
|
2
2
|
import { BaseItemProps, Droplist, DroplistProps, SNAP_POINTS_PRESET } from '@cloud-ru/ds-list';
|
|
3
3
|
import { useValueControl } from '@cloud-ru/ds-utils';
|
|
4
|
-
import cn from 'classnames';
|
|
5
4
|
import { useCallback, useMemo } from 'react';
|
|
6
5
|
|
|
7
6
|
import { TEST_IDS } from '../../constants';
|
|
8
|
-
import { useDesktopComfortClassName } from '../../hooks/useDesktopComfortClassName';
|
|
9
7
|
import { useMobileLayout } from '../../hooks/useMobileLayout';
|
|
10
8
|
import { headerLocale } from '../../locale';
|
|
11
9
|
import { HeaderButton } from '../HeaderButton';
|
|
10
|
+
import { renderListItemCustom, renderListItemGroupCustom } from '../ListCustom';
|
|
12
11
|
import { useUserMenuItems } from './hooks/useUserMenuItems';
|
|
13
12
|
import styles from './styles.module.scss';
|
|
14
13
|
import { ThemeProps, UserProfileProps } from './types';
|
|
@@ -75,7 +74,6 @@ export function UserMenu({
|
|
|
75
74
|
}: UserMenuProps) {
|
|
76
75
|
const { t } = headerLocale.useTranslations();
|
|
77
76
|
const isMobile = useMobileLayout();
|
|
78
|
-
const comfortClassName = useDesktopComfortClassName();
|
|
79
77
|
|
|
80
78
|
const [open = false, setOpen] = useValueControl<boolean>({ value: openProp, onChange: setOpenProp });
|
|
81
79
|
|
|
@@ -130,7 +128,7 @@ export function UserMenu({
|
|
|
130
128
|
<Droplist
|
|
131
129
|
open={open}
|
|
132
130
|
onOpenChange={setOpen}
|
|
133
|
-
size='
|
|
131
|
+
size='s'
|
|
134
132
|
selection={SELECTION_STUB}
|
|
135
133
|
pinTop={pinTop}
|
|
136
134
|
items={menuItems}
|
|
@@ -138,13 +136,15 @@ export function UserMenu({
|
|
|
138
136
|
scroll={!isMobile}
|
|
139
137
|
trigger='click'
|
|
140
138
|
placement='bottom-end'
|
|
141
|
-
className={
|
|
139
|
+
className={styles.userMenuDroplist}
|
|
142
140
|
closeOnPopstate
|
|
143
141
|
data-test-id={TEST_IDS.userMenu.root}
|
|
144
142
|
label={t('user')}
|
|
145
143
|
snapPoints={SNAP_POINTS_PRESET.full}
|
|
146
144
|
withDividers={false}
|
|
147
145
|
triggerClassName={styles.userMenuDroplistTrigger}
|
|
146
|
+
itemRender={renderListItemCustom}
|
|
147
|
+
groupItemRender={renderListItemGroupCustom}
|
|
148
148
|
>
|
|
149
149
|
{trigger}
|
|
150
150
|
</Droplist>
|
|
@@ -3,7 +3,6 @@ import { DroplistProps } from '@cloud-ru/ds-list';
|
|
|
3
3
|
import { useCallback, useMemo } from 'react';
|
|
4
4
|
|
|
5
5
|
import { TEST_IDS } from '../../../constants';
|
|
6
|
-
import { useDesktopComfortClassName } from '../../../hooks/useDesktopComfortClassName';
|
|
7
6
|
import { headerLocale } from '../../../locale';
|
|
8
7
|
import { THEME_MODE, ThemeMode, ThemeProps } from '../types';
|
|
9
8
|
|
|
@@ -14,7 +13,6 @@ export function useThemeItem({
|
|
|
14
13
|
onClose,
|
|
15
14
|
}: ThemeProps & { isMobile?: boolean; onClose?(): void }): DroplistProps['items'][0] {
|
|
16
15
|
const { t } = headerLocale.useTranslations();
|
|
17
|
-
const comfortClassName = useDesktopComfortClassName();
|
|
18
16
|
|
|
19
17
|
const handleClick = useCallback(
|
|
20
18
|
(themeMode: ThemeMode) => () => {
|
|
@@ -67,8 +65,7 @@ export function useThemeItem({
|
|
|
67
65
|
beforeContent: <ThemeContrastSVG />,
|
|
68
66
|
'data-test-id': TEST_IDS.userMenu.themeMode,
|
|
69
67
|
items: themeModeOptions,
|
|
70
|
-
listClassName: comfortClassName,
|
|
71
68
|
}),
|
|
72
|
-
[
|
|
69
|
+
[t, themeModeOptions],
|
|
73
70
|
);
|
|
74
71
|
}
|