@cloud-ru/ds-list 2.0.1 → 2.0.2
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 +6 -0
- package/README.md +1 -1
- package/dist/cjs/components/Items/BaseItem/BaseItem.js +1 -1
- package/dist/cjs/components/Items/BaseItem/styles.module.css +42 -0
- package/dist/cjs/components/Items/SearchItem/SearchItem.js +1 -1
- package/dist/cjs/components/Items/SimpleItem/utils.d.ts +1 -1
- package/dist/cjs/components/Items/SimpleItem/utils.js +1 -1
- package/dist/cjs/components/Items/styles.module.css +9 -0
- package/dist/cjs/components/Items/types.d.ts +2 -2
- package/dist/cjs/components/Items/utils.js +1 -1
- package/dist/cjs/components/Lists/ListPrivate/ListPrivate.js +1 -1
- package/dist/cjs/components/Lists/ListPrivate/styles.module.css +2 -3
- package/dist/cjs/components/Lists/hooks.d.ts +1 -1
- package/dist/cjs/components/Lists/hooks.js +2 -2
- package/dist/cjs/helperComponents/DesktopDroplist/DesktopDroplist.js +1 -1
- package/dist/cjs/helperComponents/ItemContent/ItemContent.js +1 -1
- package/dist/cjs/helperComponents/ItemContent/styles.module.css +45 -24
- package/dist/cjs/helperComponents/Separator/Separator.js +3 -4
- package/dist/cjs/helperComponents/Separator/styles.module.css +69 -49
- package/dist/cjs/style.css +167 -76
- package/dist/esm/components/Items/BaseItem/BaseItem.js +1 -1
- package/dist/esm/components/Items/BaseItem/styles.module.css +42 -0
- package/dist/esm/components/Items/SearchItem/SearchItem.js +1 -1
- package/dist/esm/components/Items/SimpleItem/utils.d.ts +1 -1
- package/dist/esm/components/Items/SimpleItem/utils.js +1 -1
- package/dist/esm/components/Items/styles.module.css +9 -0
- package/dist/esm/components/Items/types.d.ts +2 -2
- package/dist/esm/components/Items/utils.js +1 -1
- package/dist/esm/components/Lists/ListPrivate/ListPrivate.js +1 -1
- package/dist/esm/components/Lists/ListPrivate/styles.module.css +2 -3
- package/dist/esm/components/Lists/hooks.d.ts +1 -1
- package/dist/esm/components/Lists/hooks.js +2 -2
- package/dist/esm/helperComponents/DesktopDroplist/DesktopDroplist.js +1 -1
- package/dist/esm/helperComponents/ItemContent/ItemContent.js +1 -1
- package/dist/esm/helperComponents/ItemContent/styles.module.css +45 -24
- package/dist/esm/helperComponents/Separator/Separator.js +3 -4
- package/dist/esm/helperComponents/Separator/styles.module.css +69 -49
- package/dist/esm/style.css +167 -76
- package/dist/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/components/Items/BaseItem/BaseItem.tsx +17 -10
- package/src/components/Items/BaseItem/styles.module.scss +57 -4
- package/src/components/Items/SearchItem/SearchItem.tsx +1 -1
- package/src/components/Items/SimpleItem/utils.ts +1 -1
- package/src/components/Items/styles.module.scss +10 -2
- package/src/components/Items/types.ts +3 -7
- package/src/components/Items/utils.ts +1 -1
- package/src/components/Lists/ListPrivate/ListPrivate.tsx +1 -1
- package/src/components/Lists/ListPrivate/styles.module.scss +5 -4
- package/src/components/Lists/hooks.ts +2 -2
- package/src/helperComponents/DesktopDroplist/DesktopDroplist.tsx +1 -1
- package/src/helperComponents/ItemContent/ItemContent.tsx +22 -20
- package/src/helperComponents/ItemContent/styles.module.scss +25 -15
- package/src/helperComponents/Separator/Separator.tsx +23 -22
- package/src/helperComponents/Separator/styles.module.scss +132 -101
- package/dist/cjs/helperComponents/Separator/constants.d.ts +0 -3
- package/dist/cjs/helperComponents/Separator/constants.js +0 -10
- package/dist/esm/helperComponents/Separator/constants.d.ts +0 -3
- package/dist/esm/helperComponents/Separator/constants.js +0 -7
- package/src/helperComponents/Separator/constants.ts +0 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloud-ru/ds-list",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Списочный UI — компонент List для плоских/вложенных списков с выбором, группами и поиском и Droplist — тот же список в поповере.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -48,22 +48,22 @@
|
|
|
48
48
|
"@tanstack/react-virtual": "^3.14.8",
|
|
49
49
|
"classnames": "^2.5.1",
|
|
50
50
|
"merge-refs": "^2.0.0",
|
|
51
|
-
"@cloud-ru/ds-
|
|
51
|
+
"@cloud-ru/ds-button": "^1.0.1",
|
|
52
52
|
"@cloud-ru/ds-divider": "^1.0.1",
|
|
53
|
-
"@cloud-ru/ds-
|
|
53
|
+
"@cloud-ru/ds-drag-and-drop": "^1.1.1",
|
|
54
|
+
"@cloud-ru/ds-dropdown": "^1.0.4",
|
|
54
55
|
"@cloud-ru/ds-info-block": "^1.0.1",
|
|
55
|
-
"@cloud-ru/ds-materials": "^1.0.1",
|
|
56
|
-
"@cloud-ru/ds-dropdown": "^1.0.3",
|
|
57
56
|
"@cloud-ru/ds-loader": "^1.0.1",
|
|
57
|
+
"@cloud-ru/ds-bottom-sheet": "^1.0.3",
|
|
58
|
+
"@cloud-ru/ds-adaptive": "^1.0.1",
|
|
58
59
|
"@cloud-ru/ds-portal-context": "^1.0.1",
|
|
60
|
+
"@cloud-ru/ds-materials": "^1.0.1",
|
|
59
61
|
"@cloud-ru/ds-icons": "^1.0.2",
|
|
60
|
-
"@cloud-ru/ds-bottom-sheet": "^1.0.3",
|
|
61
|
-
"@cloud-ru/ds-scroll": "^1.0.2",
|
|
62
|
-
"@cloud-ru/ds-search-private": "^1.0.3",
|
|
63
|
-
"@cloud-ru/ds-toggles": "^1.1.2",
|
|
64
62
|
"@cloud-ru/ds-truncate-string": "^1.0.3",
|
|
63
|
+
"@cloud-ru/ds-scroll": "^1.0.2",
|
|
65
64
|
"@cloud-ru/ds-utils": "^1.1.0",
|
|
66
|
-
"@cloud-ru/ds-
|
|
65
|
+
"@cloud-ru/ds-search-private": "^1.0.3",
|
|
66
|
+
"@cloud-ru/ds-toggles": "^1.1.2"
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"@cloud-ru/ds-locale": "^1.0.0"
|
|
@@ -179,6 +179,7 @@ export function BaseItem({
|
|
|
179
179
|
const itemJSX = (
|
|
180
180
|
<div
|
|
181
181
|
className={cn(commonStyles.itemWrapper, styles.innerWrapper, className)}
|
|
182
|
+
data-size={size}
|
|
182
183
|
data-inactive={inactive || undefined}
|
|
183
184
|
data-disabled={disabled || undefined}
|
|
184
185
|
data-variant={mode || undefined}
|
|
@@ -227,18 +228,24 @@ export function BaseItem({
|
|
|
227
228
|
</div>
|
|
228
229
|
)}
|
|
229
230
|
|
|
230
|
-
{
|
|
231
|
-
|
|
232
|
-
|
|
231
|
+
{/* contentWrapper мастера: иконочные слоты и текст лежат в одном узле без gap —
|
|
232
|
+
их разделяют только горизонтальные поля textContent. */}
|
|
233
|
+
<div className={styles.contentWrapper}>
|
|
234
|
+
{beforeContent && <div className={styles.beforeContent}>{beforeContent}</div>}
|
|
235
|
+
{contentNode}
|
|
236
|
+
{afterContent && <div className={styles.afterContent}>{afterContent}</div>}
|
|
237
|
+
</div>
|
|
233
238
|
|
|
234
239
|
{switchProp && interactive && (
|
|
235
|
-
<
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
<span className={styles.switchWrapper}>
|
|
241
|
+
<Switch
|
|
242
|
+
size={TOGGLE_SIZE_MAP[size]}
|
|
243
|
+
disabled={disabled}
|
|
244
|
+
checked={isChecked}
|
|
245
|
+
data-test-id={TEST_IDS.baseItemSwitch}
|
|
246
|
+
showIcon={showSwitchIcon}
|
|
247
|
+
/>
|
|
248
|
+
</span>
|
|
242
249
|
)}
|
|
243
250
|
|
|
244
251
|
{!switchProp && expandIcon && (
|
|
@@ -185,6 +185,37 @@ $level: var(--level, 0);
|
|
|
185
185
|
min-height: base.simple-var(icons.$iconSizeMap, $size);
|
|
186
186
|
padding-top: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'centeredWrapper', 'padding-top');
|
|
187
187
|
padding-bottom: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'centeredWrapper', 'padding-bottom');
|
|
188
|
+
|
|
189
|
+
// Иконка в слоте ограничена icon-сеткой размера. Через max-*, потому что у svg
|
|
190
|
+
// из @cloud-ru/ds-icons размер приходит инлайновым style и обычный width его не перебивает.
|
|
191
|
+
// Произвольный контент (counter, tag) правило не задевает — адресуется только прямой svg.
|
|
192
|
+
> svg {
|
|
193
|
+
max-width: base.simple-var(icons.$iconSizeMap, $size);
|
|
194
|
+
max-height: base.simple-var(icons.$iconSizeMap, $size);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Switch выравнивается по центру первой строки текста тем же centeredWrapper-паддингом,
|
|
199
|
+
// что иконочные слоты: строка выровнена по flex-start, иначе контрол липнет к верхней кромке.
|
|
200
|
+
.switchWrapper {
|
|
201
|
+
padding-top: base.simple-var(
|
|
202
|
+
list.$list,
|
|
203
|
+
'anatomy',
|
|
204
|
+
'size',
|
|
205
|
+
$size,
|
|
206
|
+
'listItem',
|
|
207
|
+
'centeredWrapper',
|
|
208
|
+
'padding-top'
|
|
209
|
+
);
|
|
210
|
+
padding-bottom: base.simple-var(
|
|
211
|
+
list.$list,
|
|
212
|
+
'anatomy',
|
|
213
|
+
'size',
|
|
214
|
+
$size,
|
|
215
|
+
'listItem',
|
|
216
|
+
'centeredWrapper',
|
|
217
|
+
'padding-bottom'
|
|
218
|
+
);
|
|
188
219
|
}
|
|
189
220
|
|
|
190
221
|
.groupIndicator {
|
|
@@ -256,10 +287,14 @@ $level: var(--level, 0);
|
|
|
256
287
|
}
|
|
257
288
|
|
|
258
289
|
&[data-disabled] {
|
|
259
|
-
.markerContainer
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
290
|
+
.markerContainer {
|
|
291
|
+
opacity: base.$sn-theme-effect-opacity-disabled;
|
|
292
|
+
|
|
293
|
+
&::before {
|
|
294
|
+
// В мастере выключенный маркер — сплошной `neutral/accent`, а не полупрозрачный
|
|
295
|
+
// текстовый токен: полоса остаётся читаемой на любом фоне.
|
|
296
|
+
background-color: base.$sn-theme-color-neutral-accent;
|
|
297
|
+
}
|
|
263
298
|
}
|
|
264
299
|
}
|
|
265
300
|
}
|
|
@@ -271,3 +306,21 @@ $level: var(--level, 0);
|
|
|
271
306
|
flex-shrink: 1;
|
|
272
307
|
box-sizing: border-box;
|
|
273
308
|
}
|
|
309
|
+
|
|
310
|
+
// contentWrapper мастера: собственного зазора не имеет — иконочные слоты отделены от текста
|
|
311
|
+
// горизонтальными полями textContent, поэтому gap здесь не задаётся.
|
|
312
|
+
.switchWrapper {
|
|
313
|
+
display: flex;
|
|
314
|
+
flex-shrink: 0;
|
|
315
|
+
align-items: center;
|
|
316
|
+
box-sizing: content-box;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.contentWrapper {
|
|
320
|
+
display: flex;
|
|
321
|
+
flex-grow: 1;
|
|
322
|
+
flex-shrink: 1;
|
|
323
|
+
align-items: flex-start;
|
|
324
|
+
|
|
325
|
+
min-width: 0;
|
|
326
|
+
}
|
|
@@ -28,7 +28,7 @@ export function SearchItem({ search, itemRef, onNavKeyDown, onFocus, inTopBar }:
|
|
|
28
28
|
const { firstItemId, size = 'm' } = useNewListContext();
|
|
29
29
|
|
|
30
30
|
const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
|
|
31
|
-
// Поиск — текстовый input:
|
|
31
|
+
// Поиск — текстовый input: перехватываются только Up/Down (навигация по списку), горизонтальные
|
|
32
32
|
// стрелки оставляем каретке.
|
|
33
33
|
preventScrollOnVerticalArrows(e);
|
|
34
34
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Отключает FLIP-анимацию перестановки после отпускания (общий контракт `SimpleItem` и `SimpleGroupBlock`).
|
|
3
3
|
* Порядок применяется синхронно через `onItemsReorder`; без флага `@dnd-kit` доигрывает `transform 200ms`
|
|
4
4
|
* из старого слота в новый — отпущенная строка дёргается обратно и уезжает на место. Расступание соседей
|
|
5
|
-
* на время переноса флаг не
|
|
5
|
+
* на время переноса флаг не изменяется: его ведёт стратегия `SortableContext`.
|
|
6
6
|
*/
|
|
7
7
|
export function animateLayoutChanges(): boolean {
|
|
8
8
|
return false;
|
|
@@ -24,6 +24,14 @@ $sizes: 's', 'm', 'l';
|
|
|
24
24
|
// data-state переключается в BaseItem.tsx: emptyNeutralOnBackground ↔ activatedOnBackground.
|
|
25
25
|
@include m.has-state-layer-as-child(#{stateLayer});
|
|
26
26
|
|
|
27
|
+
// Радиус дублируется здесь, а не задан только на `.listItem`: подложка state-layer
|
|
28
|
+
// отрисовывается на этом узле и без радиуса выходит за скругление строки.
|
|
29
|
+
@each $size in $sizes {
|
|
30
|
+
&[data-size='#{$size}'] {
|
|
31
|
+
border-radius: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'border-radius');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
27
35
|
&[data-disabled],
|
|
28
36
|
&:disabled {
|
|
29
37
|
cursor: not-allowed;
|
|
@@ -54,9 +62,9 @@ $sizes: 's', 'm', 'l';
|
|
|
54
62
|
}
|
|
55
63
|
}
|
|
56
64
|
|
|
57
|
-
// Миксин has-state-layer-as-child
|
|
65
|
+
// Миксин has-state-layer-as-child отключает hover/active state-layer только для
|
|
58
66
|
// data-disabled/data-loading. data-inactive — list-специфичный «неинтерактивный» флаг,
|
|
59
|
-
// миксин про него не знает →
|
|
67
|
+
// миксин про него не знает → state-layer отключается и для него (перебивается по специфичности).
|
|
60
68
|
&[data-inactive] {
|
|
61
69
|
&:hover,
|
|
62
70
|
&:active {
|
|
@@ -40,7 +40,7 @@ export type BaseItem = WithSupportProps<{
|
|
|
40
40
|
id?: ItemId;
|
|
41
41
|
/** Флаг неактивности элемента */
|
|
42
42
|
disabled?: boolean;
|
|
43
|
-
/** Скрыть элемент из списка (не рендерится и
|
|
43
|
+
/** Скрыть элемент из списка (не рендерится и исключается из навигации) */
|
|
44
44
|
hidden?: boolean;
|
|
45
45
|
/** Ссылка на DOM-элемент айтема */
|
|
46
46
|
itemRef?: RefObject<HTMLElement>;
|
|
@@ -49,7 +49,7 @@ export type BaseItem = WithSupportProps<{
|
|
|
49
49
|
/**
|
|
50
50
|
* Флаг отображения отключения реакции на любое css состояние (hover/focus и тд)
|
|
51
51
|
* <br>
|
|
52
|
-
*
|
|
52
|
+
* Также элемент исключается из навигации с клавиатуры и не может быть выбран (selection)
|
|
53
53
|
*/
|
|
54
54
|
inactive?: boolean;
|
|
55
55
|
/**
|
|
@@ -174,11 +174,7 @@ export type FlattenSimpleItem = FlattenBaseItem & CommonFlattenProps;
|
|
|
174
174
|
export type FlattenItem =
|
|
175
175
|
| FlattenBaseItem
|
|
176
176
|
| ((
|
|
177
|
-
|
|
|
178
|
-
| FlattenGroupListItem
|
|
179
|
-
| FlattenAccordionItem
|
|
180
|
-
| FlattenGroupSelectListItem
|
|
181
|
-
| FlattenSimpleItem
|
|
177
|
+
FlattenNextListItem | FlattenGroupListItem | FlattenAccordionItem | FlattenGroupSelectListItem | FlattenSimpleItem
|
|
182
178
|
) &
|
|
183
179
|
CommonFlattenProps);
|
|
184
180
|
|
|
@@ -134,7 +134,7 @@ export function kindFlattenItems({ items, prefix, parentId, sortable }: KindFlat
|
|
|
134
134
|
key: itemId,
|
|
135
135
|
originalId: itemId,
|
|
136
136
|
id: autoId,
|
|
137
|
-
// `inactive` элемент
|
|
137
|
+
// `inactive` элемент исключается из клавиатурной навигации наравне с `disabled`
|
|
138
138
|
// (см. JSDoc `BaseItem.inactive`). Гейтит только nav/bulk-select через
|
|
139
139
|
// `extractActiveItems`; рендерный `disabled` в `flattenItems` остаётся исходным.
|
|
140
140
|
disabled: item.disabled || item.inactive,
|
|
@@ -127,7 +127,7 @@ export const ListPrivate = forwardRef(
|
|
|
127
127
|
useSensor(TouchSensor, {}),
|
|
128
128
|
useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
|
|
129
129
|
);
|
|
130
|
-
// Id перетаскиваемой строки — для рендера её копии в `DragOverlay`. Копия
|
|
130
|
+
// Id перетаскиваемой строки — для рендера её копии в `DragOverlay`. Копия рендерится в портале
|
|
131
131
|
// над страницей и не режется `overflow: hidden` контейнера `List` (сама строка на время
|
|
132
132
|
// переноса уступает место — её слот пустеет). `null`, когда перетаскивание не идёт.
|
|
133
133
|
const [activeDragId, setActiveDragId] = useState<ItemId | null>(null);
|
|
@@ -62,12 +62,13 @@ $loader-height: (
|
|
|
62
62
|
gap: popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-headlineWrapper-gap;
|
|
63
63
|
|
|
64
64
|
box-sizing: border-box;
|
|
65
|
-
padding: 0 popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-headlineWrapper-paddingHorizontal
|
|
66
|
-
popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-gap;
|
|
65
|
+
padding: 0 popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-headlineWrapper-paddingHorizontal;
|
|
67
66
|
|
|
67
|
+
// В мастере (popupDropdownHeader) шапка — вертикальный стек, в котором divider такой же
|
|
68
|
+
// элемент, как titleWrapper: `header-gap` отделяет заголовок от линии, а под линией отступа
|
|
69
|
+
// нет — контейнер dropdown размещает шапку и список без зазора.
|
|
68
70
|
&[data-divider] {
|
|
69
|
-
|
|
70
|
-
padding-bottom: popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-headlineWrapper-paddingHorizontal;
|
|
71
|
+
padding-bottom: popupPrivate.$sn-popupPrivate-anatomy-dropdown-header-gap;
|
|
71
72
|
border-bottom: base.$sn-primitive-strokeWeight-strokeRegular solid base.$sn-theme-color-available-borderColor;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
@@ -54,7 +54,7 @@ type UseListItemsModelProps = {
|
|
|
54
54
|
* Единая модель данных списка для `List` и `Droplist`: collapse-состояние,
|
|
55
55
|
* плоские карты айтемов (`flattenItems` / `focusFlattenItems`) и производный
|
|
56
56
|
* порядок навигации (`ids` / `expandedIds`). Оба компонента собирают одну и ту же
|
|
57
|
-
* структуру, поэтому она
|
|
57
|
+
* структуру, поэтому она собрана в одном хуке — расхождение между списком и дроплистом
|
|
58
58
|
* физически невозможно.
|
|
59
59
|
*/
|
|
60
60
|
export function useListItemsModel({
|
|
@@ -217,7 +217,7 @@ export function useNewKeyboardNavigation<T extends HTMLElement>({
|
|
|
217
217
|
|
|
218
218
|
const handleListKeyDownFactory = useCallback(
|
|
219
219
|
(ids: ItemId[], expandedIds: ItemId[]) => (e: KeyboardEvent<T>) => {
|
|
220
|
-
// Стрелки навигации по списку
|
|
220
|
+
// Стрелки навигации по списку перехватываются централизованно — иначе нативное поведение скроллит
|
|
221
221
|
// страницу (элементы — кнопки/li, не текст, поэтому безопасны все четыре стрелки).
|
|
222
222
|
preventScrollOnArrowKeys(e);
|
|
223
223
|
|
|
@@ -145,7 +145,7 @@ export function DesktopDroplist({
|
|
|
145
145
|
|
|
146
146
|
const onKeyDown = useCallback(
|
|
147
147
|
(e: KeyboardEvent<HTMLElement>, cb?: (e: KeyboardEvent<HTMLElement>) => void) => {
|
|
148
|
-
// Up/Down открывают/закрывают дроплист —
|
|
148
|
+
// Up/Down открывают/закрывают дроплист — их нативный скролл страницы подавляется. Только
|
|
149
149
|
// вертикальные: триггером может быть текстовое поле, где Left/Right двигают каретку.
|
|
150
150
|
preventScrollOnVerticalArrows(e);
|
|
151
151
|
|
|
@@ -49,32 +49,34 @@ export function ItemContent({
|
|
|
49
49
|
|
|
50
50
|
return (
|
|
51
51
|
<div
|
|
52
|
-
className={cn(styles.
|
|
52
|
+
className={cn(styles.textWrapper, className)}
|
|
53
53
|
{...extractSupportProps(rest)}
|
|
54
54
|
data-size={size ?? 's'}
|
|
55
55
|
data-disabled={disabled || undefined}
|
|
56
56
|
>
|
|
57
|
-
<div className={styles.
|
|
58
|
-
<div className={styles.
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
57
|
+
<div className={styles.textContent}>
|
|
58
|
+
<div className={styles.headline}>
|
|
59
|
+
<div className={styles.label}>
|
|
60
|
+
<TruncateString
|
|
61
|
+
variant={truncate.variant}
|
|
62
|
+
text={String(label)}
|
|
63
|
+
maxLines={truncate.label}
|
|
64
|
+
data-test-id={TEST_IDS.baseItemLabel}
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
{caption && <span className={styles.caption}>{caption}</span>}
|
|
65
68
|
</div>
|
|
66
|
-
{caption && <span className={styles.caption}>{caption}</span>}
|
|
67
|
-
</div>
|
|
68
69
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
70
|
+
{description && (
|
|
71
|
+
<div className={styles.description}>
|
|
72
|
+
<TruncateString
|
|
73
|
+
text={description}
|
|
74
|
+
maxLines={truncate.description}
|
|
75
|
+
data-test-id={TEST_IDS.baseItemDescription}
|
|
76
|
+
/>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
78
80
|
</div>
|
|
79
81
|
);
|
|
80
82
|
}
|
|
@@ -48,10 +48,11 @@ $description-size-map: (
|
|
|
48
48
|
color: base.$sn-theme-color-available-version-textSecondary;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
// Структура повторяет мастер: contentWrapper → textWrapper (вертикальные поля) →
|
|
52
|
+
// textContent (горизонтальные поля и зазор headline ↔ description).
|
|
53
|
+
.textWrapper {
|
|
52
54
|
overflow: hidden;
|
|
53
55
|
display: flex;
|
|
54
|
-
flex-direction: column;
|
|
55
56
|
flex-grow: 1;
|
|
56
57
|
flex-shrink: 1;
|
|
57
58
|
|
|
@@ -59,14 +60,15 @@ $description-size-map: (
|
|
|
59
60
|
|
|
60
61
|
@each $size in $sizes {
|
|
61
62
|
&[data-size='#{$size}'] {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', '
|
|
68
|
-
base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'padding-
|
|
69
|
-
base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', 'padding-left');
|
|
63
|
+
padding-top: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'padding-top');
|
|
64
|
+
padding-bottom: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'padding-bottom');
|
|
65
|
+
|
|
66
|
+
.textContent {
|
|
67
|
+
// textContent.gap — вертикальный зазор headline ↔ description. s/m = 0, l = 2.
|
|
68
|
+
gap: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', 'gap');
|
|
69
|
+
padding-right: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', 'padding-right');
|
|
70
|
+
padding-left: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', 'padding-left');
|
|
71
|
+
}
|
|
70
72
|
|
|
71
73
|
.headline {
|
|
72
74
|
gap: base.simple-var(list.$list, 'anatomy', 'size', $size, 'listItem', 'contentWrapper', 'textWrapper', 'textContent', 'headline', 'gap');
|
|
@@ -90,11 +92,19 @@ $description-size-map: (
|
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
94
|
|
|
95
|
+
// В мастере выключенное состояние — общая opacity на contentWrapper, без смены цвета текста:
|
|
96
|
+
// так различие label/caption/description сохраняется и в disabled.
|
|
93
97
|
&[data-disabled] {
|
|
94
|
-
|
|
95
|
-
.description,
|
|
96
|
-
.caption {
|
|
97
|
-
color: base.$sn-theme-color-available-version-textDisabled;
|
|
98
|
-
}
|
|
98
|
+
opacity: base.$sn-theme-effect-opacity-disabled;
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
|
|
102
|
+
.textContent {
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
display: flex;
|
|
105
|
+
flex-direction: column;
|
|
106
|
+
flex-grow: 1;
|
|
107
|
+
flex-shrink: 1;
|
|
108
|
+
|
|
109
|
+
box-sizing: border-box;
|
|
110
|
+
}
|
|
@@ -7,7 +7,6 @@ import { useCollapseLevelContext, useNewListContext } from '../../components/Lis
|
|
|
7
7
|
import { TEST_IDS } from '../../constants';
|
|
8
8
|
import { listLocale } from '../../locale';
|
|
9
9
|
import { stopPropagation } from '../../utils';
|
|
10
|
-
import { SELECT_BUTTON_SIZE_MAP } from './constants';
|
|
11
10
|
import styles from './styles.module.scss';
|
|
12
11
|
|
|
13
12
|
export type SeparatorProps = {
|
|
@@ -49,11 +48,11 @@ export function Separator({
|
|
|
49
48
|
const { onClick, checked, itemRef, label } = selectButton;
|
|
50
49
|
|
|
51
50
|
return (
|
|
52
|
-
<span className={styles.selectButton} data-size={size}
|
|
51
|
+
<span className={styles.selectButton} data-size={size}>
|
|
53
52
|
<Button
|
|
54
53
|
view='function'
|
|
55
54
|
appearance='neutral'
|
|
56
|
-
size={
|
|
55
|
+
size={size}
|
|
57
56
|
tabIndex={0}
|
|
58
57
|
data-test-id={TEST_IDS.bulkSelectButton}
|
|
59
58
|
onClick={(e: MouseEvent<HTMLElement>) => {
|
|
@@ -67,7 +66,7 @@ export function Separator({
|
|
|
67
66
|
/>
|
|
68
67
|
</span>
|
|
69
68
|
);
|
|
70
|
-
}, [
|
|
69
|
+
}, [selectButton, size, t]);
|
|
71
70
|
|
|
72
71
|
if (label) {
|
|
73
72
|
return (
|
|
@@ -78,27 +77,29 @@ export function Separator({
|
|
|
78
77
|
data-level-more-one={level > 1 || undefined}
|
|
79
78
|
style={{ '--level': level } as CSSProperties}
|
|
80
79
|
>
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
{
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<span className={styles.label} data-group-variant={groupVariant}>
|
|
88
|
-
<TruncateString variant={truncate?.variant} text={label} maxLines={1} />
|
|
89
|
-
</span>
|
|
80
|
+
<div className={styles.contentWrapper}>
|
|
81
|
+
{beforeContent && (
|
|
82
|
+
<span className={styles.centeredWrapper} aria-hidden>
|
|
83
|
+
{beforeContent}
|
|
84
|
+
</span>
|
|
85
|
+
)}
|
|
90
86
|
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
<div className={styles.textWrapper}>
|
|
88
|
+
<div className={styles.textContent}>
|
|
89
|
+
<span className={styles.headline} data-group-variant={groupVariant}>
|
|
90
|
+
<TruncateString variant={truncate?.variant} text={label} maxLines={1} />
|
|
91
|
+
</span>
|
|
92
|
+
</div>
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<Divider
|
|
97
|
-
className={styles.divider}
|
|
98
|
-
variant={groupVariant === 'subtitle' ? VARIANT.Regular : VARIANT.Thin}
|
|
99
|
-
/>
|
|
100
|
-
)}
|
|
94
|
+
{selectButtonJSX}
|
|
95
|
+
</div>
|
|
101
96
|
</div>
|
|
97
|
+
|
|
98
|
+
{/* Figma weight-ось дивайдера = @cloud-ru/ds-divider `variant`: subtitle → regular, subtitleTertiary → thin (легаси light).
|
|
99
|
+
Линия подчёркивает строку заголовка целиком, поэтому вынесена из потока. */}
|
|
100
|
+
{divider && (
|
|
101
|
+
<Divider className={styles.divider} variant={groupVariant === 'subtitle' ? VARIANT.Regular : VARIANT.Thin} />
|
|
102
|
+
)}
|
|
102
103
|
</div>
|
|
103
104
|
);
|
|
104
105
|
}
|