@alfalab/core-components-select 19.1.10 → 19.2.0-alfasans
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/components/arrow/index.css +4 -4
- package/components/arrow/index.module.css.js +1 -1
- package/components/base-checkmark/index.css +9 -9
- package/components/base-checkmark/index.module.css.js +1 -1
- package/components/base-option/index.css +19 -17
- package/components/base-option/index.module.css.js +1 -1
- package/components/base-select/Component.js +36 -49
- package/components/base-select/Component.js.map +1 -1
- package/components/base-select/index.css +70 -20
- package/components/base-select/index.module.css.js +1 -1
- package/components/base-select/index.module.css.js.map +1 -1
- package/components/base-select/mobile.css +12 -11
- package/components/base-select/mobile.module.css.js +1 -1
- package/components/base-select/mobile.module.css.js.map +1 -1
- package/components/checkmark/index.css +11 -11
- package/components/checkmark/index.module.css.js +1 -1
- package/components/checkmark-mobile/index.css +6 -6
- package/components/checkmark-mobile/index.module.css.js +1 -1
- package/components/clear-button/index.css +4 -4
- package/components/clear-button/index.module.css.js +1 -1
- package/components/field/index.css +22 -18
- package/components/field/index.module.css.js +1 -1
- package/components/footer/index.css +4 -4
- package/components/footer/index.module.css.js +1 -1
- package/components/optgroup/index.css +8 -7
- package/components/optgroup/index.module.css.js +1 -1
- package/components/option/desktop/index.css +28 -22
- package/components/option/desktop/index.module.css.js +1 -1
- package/components/option/desktop/index.module.css.js.map +1 -1
- package/components/option/mobile/index.css +25 -19
- package/components/option/mobile/index.module.css.js +1 -1
- package/components/option/mobile/index.module.css.js.map +1 -1
- package/components/options-list/Component.d.ts +4 -0
- package/components/options-list/Component.js +32 -53
- package/components/options-list/Component.js.map +1 -1
- package/components/options-list/index.css +13 -33
- package/components/options-list/index.module.css.js +1 -1
- package/components/options-list/index.module.css.js.map +1 -1
- package/components/search/index.css +1 -1
- package/components/search/index.module.css.js +1 -1
- package/components/virtual-options-list/Component.d.ts +4 -0
- package/components/virtual-options-list/Component.js +61 -48
- package/components/virtual-options-list/Component.js.map +1 -1
- package/cssm/components/base-option/index.module.css +5 -3
- package/cssm/components/base-select/Component.js +36 -49
- package/cssm/components/base-select/Component.js.map +1 -1
- package/cssm/components/base-select/index.module.css +61 -11
- package/cssm/components/base-select/mobile.module.css +1 -0
- package/cssm/components/field/index.module.css +9 -5
- package/cssm/components/optgroup/index.module.css +4 -3
- package/cssm/components/option/desktop/index.module.css +9 -3
- package/cssm/components/option/mobile/index.module.css +9 -3
- package/cssm/components/options-list/Component.d.ts +4 -0
- package/cssm/components/options-list/Component.js +32 -53
- package/cssm/components/options-list/Component.js.map +1 -1
- package/cssm/components/options-list/index.module.css +6 -26
- package/cssm/components/virtual-options-list/Component.d.ts +4 -0
- package/cssm/components/virtual-options-list/Component.js +61 -48
- package/cssm/components/virtual-options-list/Component.js.map +1 -1
- package/cssm/presets/useSelectWithApply/hook.d.ts +12 -0
- package/cssm/presets/useSelectWithApply/options-list-with-apply/Component.d.ts +4 -0
- package/cssm/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css +0 -5
- package/cssm/presets/useSelectWithApply/options-list-with-apply/header/index.module.css +9 -6
- package/cssm/shared/index.js +0 -1
- package/cssm/shared/index.js.map +1 -1
- package/cssm/typings.d.ts +21 -1
- package/cssm/utils.d.ts +1 -6
- package/cssm/utils.js +19 -28
- package/cssm/utils.js.map +1 -1
- package/esm/components/arrow/index.css +4 -4
- package/esm/components/arrow/index.module.css.js +1 -1
- package/esm/components/base-checkmark/index.css +9 -9
- package/esm/components/base-checkmark/index.module.css.js +1 -1
- package/esm/components/base-option/index.css +19 -17
- package/esm/components/base-option/index.module.css.js +1 -1
- package/esm/components/base-select/Component.js +37 -50
- package/esm/components/base-select/Component.js.map +1 -1
- package/esm/components/base-select/index.css +70 -20
- package/esm/components/base-select/index.module.css.js +1 -1
- package/esm/components/base-select/index.module.css.js.map +1 -1
- package/esm/components/base-select/mobile.css +12 -11
- package/esm/components/base-select/mobile.module.css.js +1 -1
- package/esm/components/base-select/mobile.module.css.js.map +1 -1
- package/esm/components/checkmark/index.css +11 -11
- package/esm/components/checkmark/index.module.css.js +1 -1
- package/esm/components/checkmark-mobile/index.css +6 -6
- package/esm/components/checkmark-mobile/index.module.css.js +1 -1
- package/esm/components/clear-button/index.css +4 -4
- package/esm/components/clear-button/index.module.css.js +1 -1
- package/esm/components/field/index.css +22 -18
- package/esm/components/field/index.module.css.js +1 -1
- package/esm/components/footer/index.css +4 -4
- package/esm/components/footer/index.module.css.js +1 -1
- package/esm/components/optgroup/index.css +8 -7
- package/esm/components/optgroup/index.module.css.js +1 -1
- package/esm/components/option/desktop/index.css +28 -22
- package/esm/components/option/desktop/index.module.css.js +1 -1
- package/esm/components/option/desktop/index.module.css.js.map +1 -1
- package/esm/components/option/mobile/index.css +25 -19
- package/esm/components/option/mobile/index.module.css.js +1 -1
- package/esm/components/option/mobile/index.module.css.js.map +1 -1
- package/esm/components/options-list/Component.d.ts +4 -0
- package/esm/components/options-list/Component.js +33 -54
- package/esm/components/options-list/Component.js.map +1 -1
- package/esm/components/options-list/index.css +13 -33
- package/esm/components/options-list/index.module.css.js +1 -1
- package/esm/components/options-list/index.module.css.js.map +1 -1
- package/esm/components/search/index.css +1 -1
- package/esm/components/search/index.module.css.js +1 -1
- package/esm/components/virtual-options-list/Component.d.ts +4 -0
- package/esm/components/virtual-options-list/Component.js +63 -49
- package/esm/components/virtual-options-list/Component.js.map +1 -1
- package/esm/mobile/modal.mobile.css +1 -1
- package/esm/mobile/modal.mobile.module.css.js +1 -1
- package/esm/presets/useSelectWithApply/hook.d.ts +12 -0
- package/esm/presets/useSelectWithApply/options-list-with-apply/Component.d.ts +4 -0
- package/esm/presets/useSelectWithApply/options-list-with-apply/footer/index.css +2 -7
- package/esm/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js +1 -1
- package/esm/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js.map +1 -1
- package/esm/presets/useSelectWithApply/options-list-with-apply/header/index.css +12 -9
- package/esm/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js +1 -1
- package/esm/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js.map +1 -1
- package/esm/presets/useSelectWithLoading/index.css +1 -1
- package/esm/presets/useSelectWithLoading/index.module.css.js +1 -1
- package/esm/shared/index.js +1 -1
- package/esm/typings.d.ts +21 -1
- package/esm/utils.d.ts +1 -6
- package/esm/utils.js +21 -29
- package/esm/utils.js.map +1 -1
- package/mobile/modal.mobile.css +1 -1
- package/mobile/modal.mobile.module.css.js +1 -1
- package/modern/components/arrow/index.css +4 -4
- package/modern/components/arrow/index.module.css.js +1 -1
- package/modern/components/base-checkmark/index.css +9 -9
- package/modern/components/base-checkmark/index.module.css.js +1 -1
- package/modern/components/base-option/index.css +19 -17
- package/modern/components/base-option/index.module.css.js +1 -1
- package/modern/components/base-select/Component.js +34 -47
- package/modern/components/base-select/Component.js.map +1 -1
- package/modern/components/base-select/index.css +70 -20
- package/modern/components/base-select/index.module.css.js +1 -1
- package/modern/components/base-select/index.module.css.js.map +1 -1
- package/modern/components/base-select/mobile.css +12 -11
- package/modern/components/base-select/mobile.module.css.js +1 -1
- package/modern/components/base-select/mobile.module.css.js.map +1 -1
- package/modern/components/checkmark/index.css +11 -11
- package/modern/components/checkmark/index.module.css.js +1 -1
- package/modern/components/checkmark-mobile/index.css +6 -6
- package/modern/components/checkmark-mobile/index.module.css.js +1 -1
- package/modern/components/clear-button/index.css +4 -4
- package/modern/components/clear-button/index.module.css.js +1 -1
- package/modern/components/field/index.css +22 -18
- package/modern/components/field/index.module.css.js +1 -1
- package/modern/components/footer/index.css +4 -4
- package/modern/components/footer/index.module.css.js +1 -1
- package/modern/components/optgroup/index.css +8 -7
- package/modern/components/optgroup/index.module.css.js +1 -1
- package/modern/components/option/desktop/index.css +28 -22
- package/modern/components/option/desktop/index.module.css.js +1 -1
- package/modern/components/option/desktop/index.module.css.js.map +1 -1
- package/modern/components/option/mobile/index.css +25 -19
- package/modern/components/option/mobile/index.module.css.js +1 -1
- package/modern/components/option/mobile/index.module.css.js.map +1 -1
- package/modern/components/options-list/Component.d.ts +4 -0
- package/modern/components/options-list/Component.js +29 -49
- package/modern/components/options-list/Component.js.map +1 -1
- package/modern/components/options-list/index.css +13 -33
- package/modern/components/options-list/index.module.css.js +1 -1
- package/modern/components/options-list/index.module.css.js.map +1 -1
- package/modern/components/search/index.css +1 -1
- package/modern/components/search/index.module.css.js +1 -1
- package/modern/components/virtual-options-list/Component.d.ts +4 -0
- package/modern/components/virtual-options-list/Component.js +59 -45
- package/modern/components/virtual-options-list/Component.js.map +1 -1
- package/modern/mobile/modal.mobile.css +1 -1
- package/modern/mobile/modal.mobile.module.css.js +1 -1
- package/modern/presets/useSelectWithApply/hook.d.ts +12 -0
- package/modern/presets/useSelectWithApply/options-list-with-apply/Component.d.ts +4 -0
- package/modern/presets/useSelectWithApply/options-list-with-apply/footer/index.css +2 -7
- package/modern/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js +1 -1
- package/modern/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js.map +1 -1
- package/modern/presets/useSelectWithApply/options-list-with-apply/header/index.css +12 -9
- package/modern/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js +1 -1
- package/modern/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js.map +1 -1
- package/modern/presets/useSelectWithLoading/index.css +1 -1
- package/modern/presets/useSelectWithLoading/index.module.css.js +1 -1
- package/modern/shared/index.js +1 -1
- package/modern/typings.d.ts +21 -1
- package/modern/utils.d.ts +1 -6
- package/modern/utils.js +21 -29
- package/modern/utils.js.map +1 -1
- package/moderncssm/components/base-option/index.module.css +2 -1
- package/moderncssm/components/base-select/Component.js +34 -47
- package/moderncssm/components/base-select/Component.js.map +1 -1
- package/moderncssm/components/base-select/index.module.css +67 -9
- package/moderncssm/components/base-select/mobile.module.css +1 -0
- package/moderncssm/components/field/index.module.css +6 -3
- package/moderncssm/components/optgroup/index.module.css +1 -1
- package/moderncssm/components/option/desktop/index.module.css +5 -1
- package/moderncssm/components/option/mobile/index.module.css +5 -1
- package/moderncssm/components/options-list/Component.d.ts +4 -0
- package/moderncssm/components/options-list/Component.js +29 -49
- package/moderncssm/components/options-list/Component.js.map +1 -1
- package/moderncssm/components/options-list/index.module.css +7 -32
- package/moderncssm/components/virtual-options-list/Component.d.ts +4 -0
- package/moderncssm/components/virtual-options-list/Component.js +59 -45
- package/moderncssm/components/virtual-options-list/Component.js.map +1 -1
- package/moderncssm/presets/useSelectWithApply/hook.d.ts +12 -0
- package/moderncssm/presets/useSelectWithApply/options-list-with-apply/Component.d.ts +4 -0
- package/moderncssm/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css +0 -4
- package/moderncssm/presets/useSelectWithApply/options-list-with-apply/header/index.module.css +8 -6
- package/moderncssm/shared/index.js +1 -1
- package/moderncssm/typings.d.ts +21 -1
- package/moderncssm/utils.d.ts +1 -6
- package/moderncssm/utils.js +21 -29
- package/moderncssm/utils.js.map +1 -1
- package/package.json +15 -16
- package/presets/useSelectWithApply/hook.d.ts +12 -0
- package/presets/useSelectWithApply/options-list-with-apply/Component.d.ts +4 -0
- package/presets/useSelectWithApply/options-list-with-apply/footer/index.css +2 -7
- package/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js +1 -1
- package/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css.js.map +1 -1
- package/presets/useSelectWithApply/options-list-with-apply/header/index.css +12 -9
- package/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js +1 -1
- package/presets/useSelectWithApply/options-list-with-apply/header/index.module.css.js.map +1 -1
- package/presets/useSelectWithLoading/index.css +1 -1
- package/presets/useSelectWithLoading/index.module.css.js +1 -1
- package/shared/index.js +0 -1
- package/shared/index.js.map +1 -1
- package/src/components/base-select/Component.tsx +41 -55
- package/src/components/base-select/index.module.css +60 -8
- package/src/components/base-select/mobile.module.css +1 -0
- package/src/components/option/index.module.css +4 -0
- package/src/components/options-list/Component.tsx +54 -97
- package/src/components/options-list/index.module.css +7 -31
- package/src/components/virtual-options-list/Component.tsx +95 -72
- package/src/presets/useSelectWithApply/options-list-with-apply/footer/index.module.css +0 -4
- package/src/presets/useSelectWithApply/options-list-with-apply/header/index.module.css +4 -4
- package/src/typings.ts +26 -0
- package/src/utils.ts +30 -34
- package/src/vars.css +3 -0
- package/typings.d.ts +21 -1
- package/utils.d.ts +1 -6
- package/utils.js +19 -28
- package/utils.js.map +1 -1
- package/components/virtual-options-list/index.css +0 -83
- package/components/virtual-options-list/index.module.css.js +0 -8
- package/components/virtual-options-list/index.module.css.js.map +0 -1
- package/cssm/components/virtual-options-list/index.module.css +0 -83
- package/esm/components/virtual-options-list/index.css +0 -83
- package/esm/components/virtual-options-list/index.module.css.js +0 -6
- package/esm/components/virtual-options-list/index.module.css.js.map +0 -1
- package/modern/components/virtual-options-list/index.css +0 -83
- package/modern/components/virtual-options-list/index.module.css.js +0 -6
- package/modern/components/virtual-options-list/index.module.css.js.map +0 -1
- package/moderncssm/components/virtual-options-list/index.module.css +0 -81
- package/src/components/virtual-options-list/index.module.css +0 -77
|
@@ -23,13 +23,10 @@
|
|
|
23
23
|
|
|
24
24
|
.optionsListWrapper {
|
|
25
25
|
position: relative;
|
|
26
|
-
padding-top: var(--select-options-list-top-padding);
|
|
27
|
-
padding-bottom: var(--select-options-list-bottom-padding);
|
|
28
26
|
box-shadow: var(--select-options-list-box-shadow);
|
|
29
27
|
border-radius: var(--select-options-list-border-radius);
|
|
30
28
|
background: var(--select-option-background);
|
|
31
29
|
overflow: hidden;
|
|
32
|
-
will-change: transform;
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
.optionsListBorder {
|
|
@@ -67,14 +64,69 @@
|
|
|
67
64
|
}
|
|
68
65
|
|
|
69
66
|
.search {
|
|
70
|
-
margin: var(--gap-12)
|
|
67
|
+
margin: calc(var(--gap-12) - var(--select-options-list-top-padding))
|
|
68
|
+
calc(var(--gap-12) - var(--select-options-list-right-padding)) var(--gap-0)
|
|
69
|
+
calc(var(--gap-12) - var(--select-options-list-left-padding));
|
|
71
70
|
|
|
72
|
-
|
|
73
|
-
margin-
|
|
74
|
-
|
|
71
|
+
&:last-child {
|
|
72
|
+
margin-bottom: calc(var(--gap-12) - var(--select-options-list-bottom-padding));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.option {
|
|
77
|
+
border-radius: var(--select-option-border-radius);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.list {
|
|
81
|
+
display: inline-block;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.matchContent {
|
|
85
|
+
.scrollbar[data-simplebar] {
|
|
86
|
+
.scrollable {
|
|
87
|
+
display: flex;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.content {
|
|
91
|
+
flex-grow: 1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.list {
|
|
95
|
+
display: inline-block;
|
|
96
|
+
min-width: 100%;
|
|
97
|
+
}
|
|
75
98
|
}
|
|
76
99
|
|
|
100
|
+
*:not(&) .list {
|
|
101
|
+
min-width: 100%;
|
|
102
|
+
max-width: 100%;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.scrollbar {
|
|
107
|
+
padding-right: var(--select-options-list-right-padding);
|
|
108
|
+
padding-left: var(--select-options-list-left-padding);
|
|
109
|
+
&:first-child {
|
|
110
|
+
.list:before {
|
|
111
|
+
content: '';
|
|
112
|
+
display: block;
|
|
113
|
+
padding-top: var(--select-options-list-top-padding);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
77
116
|
&:last-child {
|
|
78
|
-
|
|
117
|
+
.list:after {
|
|
118
|
+
content: '';
|
|
119
|
+
display: block;
|
|
120
|
+
padding-top: var(--select-options-list-bottom-padding);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
:global(.simplebar-track):global(.simplebar-vertical) {
|
|
126
|
+
.scrollbar:first-child & {
|
|
127
|
+
top: var(--select-options-list-scrollbar-gap);
|
|
128
|
+
}
|
|
129
|
+
.scrollbar:last-child & {
|
|
130
|
+
bottom: var(--select-options-list-scrollbar-gap);
|
|
79
131
|
}
|
|
80
132
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React, { forwardRef,
|
|
1
|
+
import React, { forwardRef, useRef, useState } from 'react';
|
|
2
2
|
import mergeRefs from 'react-merge-refs';
|
|
3
3
|
import cn from 'classnames';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ScrollbarPrivate } from '@alfalab/core-components-scrollbar-private';
|
|
6
6
|
|
|
7
7
|
import { DEFAULT_VISIBLE_OPTIONS } from '../../consts';
|
|
8
8
|
import { useNativeScrollbar } from '../../hooks/use-native-scrollbar';
|
|
@@ -40,7 +40,6 @@ export const OptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
40
40
|
header,
|
|
41
41
|
footer,
|
|
42
42
|
showFooter = true,
|
|
43
|
-
optionsListWidth,
|
|
44
43
|
nativeScrollbar: nativeScrollbarProp,
|
|
45
44
|
flatOptions = [],
|
|
46
45
|
setHighlightedIndex,
|
|
@@ -50,9 +49,24 @@ export const OptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
50
49
|
multiple,
|
|
51
50
|
limitDynamicOptionGroupSize = false,
|
|
52
51
|
client,
|
|
52
|
+
scrollableNodeClassName,
|
|
53
|
+
contentNodeClassName,
|
|
54
|
+
listNodeClassName,
|
|
53
55
|
},
|
|
54
56
|
ref,
|
|
55
57
|
) => {
|
|
58
|
+
const actualOptionsCount = limitDynamicOptionGroupSize && options.length > 0;
|
|
59
|
+
const listRef = useRef<HTMLDivElement>(null);
|
|
60
|
+
const scrollableNodeRef = useRef<HTMLDivElement>(null);
|
|
61
|
+
const [, maxHeight] = useVisibleOptions({
|
|
62
|
+
visibleOptions,
|
|
63
|
+
listRef,
|
|
64
|
+
open,
|
|
65
|
+
options,
|
|
66
|
+
actualOptionsCount,
|
|
67
|
+
size: actualOptionsCount ? size : undefined,
|
|
68
|
+
});
|
|
69
|
+
const noOptions = options.length === 0;
|
|
56
70
|
const [scrollTop, setScrollTop] = useState(true);
|
|
57
71
|
const [scrollBottom, setScrollBottom] = useState(false);
|
|
58
72
|
|
|
@@ -61,27 +75,22 @@ export const OptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
61
75
|
client,
|
|
62
76
|
});
|
|
63
77
|
|
|
64
|
-
const handleScroll =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
event.currentTarget.scrollTop;
|
|
78
|
+
const handleScroll: React.MouseEventHandler<HTMLDivElement> = (event) => {
|
|
79
|
+
const scrolledToHeader = event.currentTarget.scrollTop <= 0;
|
|
80
|
+
const scrolledToFooter =
|
|
81
|
+
event.currentTarget.scrollHeight - event.currentTarget.offsetHeight <=
|
|
82
|
+
event.currentTarget.scrollTop;
|
|
70
83
|
|
|
71
|
-
|
|
72
|
-
|
|
84
|
+
setScrollTop(scrolledToHeader);
|
|
85
|
+
setScrollBottom(scrolledToFooter);
|
|
73
86
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[onScroll],
|
|
77
|
-
);
|
|
87
|
+
onScroll?.(event);
|
|
88
|
+
};
|
|
78
89
|
|
|
79
90
|
const renderOption = (option: OptionShape, index: number) => (
|
|
80
91
|
<Option key={option.key} {...getOptionProps(option, index)} />
|
|
81
92
|
);
|
|
82
93
|
|
|
83
|
-
const listRef = useRef<HTMLDivElement>(null);
|
|
84
|
-
const scrollbarRef = useRef<HTMLDivElement>(null);
|
|
85
94
|
const counter = createCounter();
|
|
86
95
|
const renderGroup = (group: GroupShape) => {
|
|
87
96
|
const groupSelectedItems = selectedItems?.filter(({ key: selectedItemKey }) =>
|
|
@@ -116,89 +125,10 @@ export const OptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
116
125
|
);
|
|
117
126
|
};
|
|
118
127
|
|
|
119
|
-
const actualOptionsCount = limitDynamicOptionGroupSize && options.length > 0;
|
|
120
|
-
|
|
121
|
-
const [measured, maxHeight] = useVisibleOptions({
|
|
122
|
-
visibleOptions,
|
|
123
|
-
listRef,
|
|
124
|
-
open,
|
|
125
|
-
options,
|
|
126
|
-
actualOptionsCount,
|
|
127
|
-
size: actualOptionsCount
|
|
128
|
-
? (() => {
|
|
129
|
-
switch (typeof size) {
|
|
130
|
-
case 'string':
|
|
131
|
-
throw new Error(
|
|
132
|
-
'OptionsList with `limitDynamicOptionGroupSize` enabled needs a `size` with number type',
|
|
133
|
-
);
|
|
134
|
-
default:
|
|
135
|
-
return size;
|
|
136
|
-
}
|
|
137
|
-
})()
|
|
138
|
-
: undefined,
|
|
139
|
-
});
|
|
140
|
-
|
|
141
128
|
if (options.length === 0 && !emptyPlaceholder && !header && !footer) {
|
|
142
129
|
return null;
|
|
143
130
|
}
|
|
144
131
|
|
|
145
|
-
const renderListItems = () => (
|
|
146
|
-
<React.Fragment>
|
|
147
|
-
{options.map((option) =>
|
|
148
|
-
isGroup(option) ? renderGroup(option) : renderOption(option, counter()),
|
|
149
|
-
)}
|
|
150
|
-
|
|
151
|
-
{emptyPlaceholder && options.length === 0 && (
|
|
152
|
-
<div className={styles.emptyPlaceholder}>{emptyPlaceholder}</div>
|
|
153
|
-
)}
|
|
154
|
-
</React.Fragment>
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const renderWithCustomScrollbar = () => {
|
|
158
|
-
const scrollableNodeProps = {
|
|
159
|
-
onScroll: handleScroll,
|
|
160
|
-
'data-test-id': dataTestId,
|
|
161
|
-
ref: ref as React.RefObject<HTMLDivElement>,
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<Scrollbar
|
|
166
|
-
className={cn(styles.scrollable, scrollbarClassName)}
|
|
167
|
-
verticalBarClassName={cn(styles.verticalBar, {
|
|
168
|
-
[styles.verticalBarWithHeader]: Boolean(header),
|
|
169
|
-
[styles.verticalBarWithFooter]: Boolean(footer),
|
|
170
|
-
})}
|
|
171
|
-
ref={scrollbarRef}
|
|
172
|
-
style={{ maxHeight }}
|
|
173
|
-
horizontalAutoStretch={optionsListWidth === 'content'}
|
|
174
|
-
scrollableNodeProps={scrollableNodeProps}
|
|
175
|
-
contentNodeProps={{ ref: listRef }}
|
|
176
|
-
maskProps={{
|
|
177
|
-
/*
|
|
178
|
-
* Для корректного подсчета высоты опций(иначе для optionsListWidth: 'field'
|
|
179
|
-
* высота опции всегда будет равна высоте одной строчки)
|
|
180
|
-
*/
|
|
181
|
-
className: cn({
|
|
182
|
-
[styles.mask]: optionsListWidth === 'content' && !measured,
|
|
183
|
-
}),
|
|
184
|
-
}}
|
|
185
|
-
>
|
|
186
|
-
{renderListItems()}
|
|
187
|
-
</Scrollbar>
|
|
188
|
-
);
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const renderWithNativeScrollbar = () => (
|
|
192
|
-
<div
|
|
193
|
-
className={cn(styles.scrollable, scrollbarClassName)}
|
|
194
|
-
ref={mergeRefs([listRef, ref])}
|
|
195
|
-
onScroll={handleScroll}
|
|
196
|
-
style={{ maxHeight }}
|
|
197
|
-
>
|
|
198
|
-
{renderListItems()}
|
|
199
|
-
</div>
|
|
200
|
-
);
|
|
201
|
-
|
|
202
132
|
const resetHighlightedIndex = () => setHighlightedIndex?.(-1);
|
|
203
133
|
|
|
204
134
|
return (
|
|
@@ -217,7 +147,34 @@ export const OptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
217
147
|
</div>
|
|
218
148
|
)}
|
|
219
149
|
|
|
220
|
-
{
|
|
150
|
+
{!noOptions && (
|
|
151
|
+
<ScrollbarPrivate
|
|
152
|
+
native={nativeScrollbar}
|
|
153
|
+
className={scrollbarClassName}
|
|
154
|
+
style={{ maxHeight }}
|
|
155
|
+
scrollableNodeProps={{
|
|
156
|
+
ref: mergeRefs([scrollableNodeRef, ref]),
|
|
157
|
+
onScroll: handleScroll,
|
|
158
|
+
className: scrollableNodeClassName,
|
|
159
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
160
|
+
// @ts-expect-error
|
|
161
|
+
'data-test-id': nativeScrollbar ? undefined : dataTestId,
|
|
162
|
+
}}
|
|
163
|
+
contentNodeProps={{ className: contentNodeClassName }}
|
|
164
|
+
>
|
|
165
|
+
<div className={listNodeClassName} ref={listRef}>
|
|
166
|
+
{options.map((option) =>
|
|
167
|
+
isGroup(option)
|
|
168
|
+
? renderGroup(option)
|
|
169
|
+
: renderOption(option, counter()),
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
</ScrollbarPrivate>
|
|
173
|
+
)}
|
|
174
|
+
|
|
175
|
+
{emptyPlaceholder && noOptions && (
|
|
176
|
+
<div className={styles.emptyPlaceholder}>{emptyPlaceholder}</div>
|
|
177
|
+
)}
|
|
221
178
|
|
|
222
179
|
{showFooter && footer && (
|
|
223
180
|
<div
|
|
@@ -6,37 +6,11 @@
|
|
|
6
6
|
outline: none;
|
|
7
7
|
box-sizing: border-box;
|
|
8
8
|
position: relative;
|
|
9
|
-
|
|
10
|
-
& .scrollable {
|
|
11
|
-
overflow: auto;
|
|
12
|
-
width: 100%;
|
|
13
|
-
|
|
14
|
-
& .verticalBar.verticalBar {
|
|
15
|
-
top: var(--select-options-list-scrollbar-gap);
|
|
16
|
-
height: calc(100% - 2 * var(--select-options-list-scrollbar-gap));
|
|
17
|
-
|
|
18
|
-
&.verticalBarWithHeader {
|
|
19
|
-
top: 0;
|
|
20
|
-
height: calc(100% - var(--select-options-list-scrollbar-gap));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
&.verticalBarWithFooter {
|
|
24
|
-
top: var(--select-options-list-scrollbar-gap);
|
|
25
|
-
height: calc(100% - var(--select-options-list-scrollbar-gap));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
&.verticalBarWithHeader.verticalBarWithFooter {
|
|
29
|
-
top: 0;
|
|
30
|
-
height: 100%;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
9
|
}
|
|
35
10
|
|
|
36
11
|
.optionsListHeader {
|
|
37
12
|
box-sizing: border-box;
|
|
38
13
|
border-bottom: 1px solid transparent;
|
|
39
|
-
transition: border-color 0.2s ease;
|
|
40
14
|
|
|
41
15
|
&.headerHighlighted {
|
|
42
16
|
border-bottom-color: var(--color-light-neutral-300);
|
|
@@ -48,22 +22,24 @@
|
|
|
48
22
|
bottom: var(--gap-0);
|
|
49
23
|
box-sizing: border-box;
|
|
50
24
|
border-top: 1px solid transparent;
|
|
51
|
-
transition: border-color 0.2s ease;
|
|
52
25
|
|
|
53
26
|
&.withBorder {
|
|
54
27
|
border-top-color: var(--color-light-neutral-300);
|
|
55
28
|
}
|
|
56
29
|
}
|
|
57
30
|
|
|
58
|
-
.mask {
|
|
59
|
-
min-width: 4000px;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
31
|
.emptyPlaceholder {
|
|
63
32
|
padding: var(--gap-16) var(--gap-12);
|
|
64
33
|
color: var(--select-options-list-empty-placeholder-color);
|
|
34
|
+
flex: 1; /* needed in mobile */
|
|
65
35
|
}
|
|
66
36
|
|
|
37
|
+
.size-64 .emptyPlaceholder,
|
|
67
38
|
.size-72 .emptyPlaceholder {
|
|
68
39
|
padding: var(--gap-24) var(--gap-16);
|
|
69
40
|
}
|
|
41
|
+
|
|
42
|
+
.list {
|
|
43
|
+
will-change: transform;
|
|
44
|
+
height: auto;
|
|
45
|
+
}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
1
|
+
import React, {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useMemo,
|
|
6
|
+
useRef,
|
|
7
|
+
useState,
|
|
8
|
+
} from 'react';
|
|
3
9
|
import { useVirtual } from 'react-virtual';
|
|
4
10
|
import cn from 'classnames';
|
|
5
11
|
|
|
6
|
-
import {
|
|
12
|
+
import { ScrollbarPrivate } from '@alfalab/core-components-scrollbar-private';
|
|
13
|
+
import { getElementWindow } from '@alfalab/core-components-shared';
|
|
7
14
|
|
|
8
15
|
import { DEFAULT_VISIBLE_OPTIONS } from '../../consts';
|
|
9
16
|
import { useNativeScrollbar } from '../../hooks/use-native-scrollbar';
|
|
10
17
|
import { type GroupShape, type OptionShape, type OptionsListProps } from '../../typings';
|
|
11
|
-
import { isGroup,
|
|
18
|
+
import { isGroup, usePrevious, useVirtualVisibleOptions } from '../../utils';
|
|
12
19
|
import { Optgroup as DefaultOptgroup } from '../optgroup';
|
|
13
20
|
|
|
14
|
-
import styles from '
|
|
21
|
+
import styles from '../options-list/index.module.css';
|
|
15
22
|
|
|
16
23
|
export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
17
24
|
(
|
|
@@ -33,7 +40,6 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
33
40
|
header,
|
|
34
41
|
footer,
|
|
35
42
|
showFooter = true,
|
|
36
|
-
optionsListWidth,
|
|
37
43
|
onScroll,
|
|
38
44
|
nativeScrollbar: nativeScrollbarProp,
|
|
39
45
|
setHighlightedIndex,
|
|
@@ -43,18 +49,43 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
43
49
|
multiple,
|
|
44
50
|
scrollbarClassName,
|
|
45
51
|
client,
|
|
52
|
+
scrollableNodeClassName,
|
|
53
|
+
contentNodeClassName,
|
|
54
|
+
listNodeClassName,
|
|
55
|
+
footerClassName,
|
|
56
|
+
ctrlRef,
|
|
46
57
|
},
|
|
47
58
|
ref,
|
|
48
59
|
) => {
|
|
49
60
|
const listRef = useRef<HTMLDivElement>(null);
|
|
50
|
-
const
|
|
51
|
-
const
|
|
61
|
+
const scrollableNodeRef = useRef<HTMLDivElement>(null);
|
|
62
|
+
const noOptions = options.length === 0;
|
|
63
|
+
const [scrollTop, setScrollTop] = useState(true);
|
|
64
|
+
const [scrollBottom, setScrollBottom] = useState(false);
|
|
52
65
|
const [visibleOptionsInvalidateKey, setVisibleOptionsInvalidateKey] = useState('');
|
|
53
66
|
const prevHighlightedIndex = usePrevious(highlightedIndex) || -1;
|
|
54
67
|
|
|
55
68
|
const rowVirtualizer = useVirtual({
|
|
69
|
+
scrollToFn(offset, defaultScrollToFn) {
|
|
70
|
+
let delta = 0;
|
|
71
|
+
const list = listRef.current;
|
|
72
|
+
|
|
73
|
+
if (list) {
|
|
74
|
+
const win = getElementWindow(list);
|
|
75
|
+
|
|
76
|
+
delta = ['::before', '::after']
|
|
77
|
+
.map((pseudo) => {
|
|
78
|
+
const { paddingTop } = win.getComputedStyle(list, pseudo);
|
|
79
|
+
|
|
80
|
+
return parseFloat(paddingTop) || 0;
|
|
81
|
+
})
|
|
82
|
+
.reduce((a, b) => a + b);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
defaultScrollToFn?.(offset === 0 ? offset : offset + delta);
|
|
86
|
+
},
|
|
56
87
|
size: flatOptions.length,
|
|
57
|
-
parentRef: (ref
|
|
88
|
+
parentRef: (ref as React.RefObject<HTMLDivElement>) || scrollableNodeRef,
|
|
58
89
|
overscan: 15,
|
|
59
90
|
});
|
|
60
91
|
|
|
@@ -71,21 +102,13 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
71
102
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
72
103
|
}, [open]);
|
|
73
104
|
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (highlightedIndex === -1) return;
|
|
77
|
-
|
|
78
|
-
if (!rowVirtualizer.virtualItems.some((option) => option.index === highlightedIndex)) {
|
|
79
|
-
rowVirtualizer.scrollToIndex(highlightedIndex, { align: 'end' });
|
|
80
|
-
}
|
|
81
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
82
|
-
}, [highlightedIndex]);
|
|
105
|
+
useImperativeHandle(ctrlRef, () => rowVirtualizer, [rowVirtualizer]);
|
|
83
106
|
|
|
84
107
|
// Циклическая навигация
|
|
85
108
|
useEffect(() => {
|
|
86
109
|
const notDisabled = (option: OptionShape) => !option.disabled;
|
|
87
110
|
const firstNonDisabled = flatOptions.findIndex(notDisabled);
|
|
88
|
-
const lastNonDisabled =
|
|
111
|
+
const lastNonDisabled = flatOptions.findLastIndex(notDisabled);
|
|
89
112
|
|
|
90
113
|
if (
|
|
91
114
|
prevHighlightedIndex <= firstNonDisabled &&
|
|
@@ -115,11 +138,10 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
115
138
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
116
139
|
}, [rowVirtualizer.virtualItems.length, flatOptions]);
|
|
117
140
|
|
|
118
|
-
useVirtualVisibleOptions({
|
|
141
|
+
const maxHeight = useVirtualVisibleOptions({
|
|
119
142
|
visibleOptions,
|
|
120
143
|
invalidate: visibleOptionsInvalidateKey,
|
|
121
144
|
listRef,
|
|
122
|
-
styleTargetRef: nativeScrollbar ? parentRef : scrollbarRef,
|
|
123
145
|
open,
|
|
124
146
|
});
|
|
125
147
|
|
|
@@ -139,6 +161,18 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
139
161
|
}, {});
|
|
140
162
|
}, [options]);
|
|
141
163
|
|
|
164
|
+
const handleScroll: React.MouseEventHandler<HTMLDivElement> = (event) => {
|
|
165
|
+
const scrolledToHeader = event.currentTarget.scrollTop <= 0;
|
|
166
|
+
const scrolledToFooter =
|
|
167
|
+
event.currentTarget.scrollHeight - event.currentTarget.offsetHeight <=
|
|
168
|
+
event.currentTarget.scrollTop;
|
|
169
|
+
|
|
170
|
+
setScrollTop(scrolledToHeader);
|
|
171
|
+
setScrollBottom(scrolledToFooter);
|
|
172
|
+
|
|
173
|
+
onScroll?.(event);
|
|
174
|
+
};
|
|
175
|
+
|
|
142
176
|
const renderList = () =>
|
|
143
177
|
rowVirtualizer.virtualItems.map((virtualRow) => {
|
|
144
178
|
const option = flatOptions[virtualRow.index];
|
|
@@ -177,16 +211,7 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
177
211
|
};
|
|
178
212
|
|
|
179
213
|
return (
|
|
180
|
-
<div
|
|
181
|
-
key={virtualRow.index}
|
|
182
|
-
ref={virtualRow.measureRef}
|
|
183
|
-
className={cn(styles.virtualRow, {
|
|
184
|
-
[styles.highlighted]: highlightedIndex === virtualRow.index,
|
|
185
|
-
})}
|
|
186
|
-
style={{
|
|
187
|
-
transform: `translateY(${virtualRow.start}px)`,
|
|
188
|
-
}}
|
|
189
|
-
>
|
|
214
|
+
<div key={virtualRow.index} ref={virtualRow.measureRef}>
|
|
190
215
|
{renderGroup()}
|
|
191
216
|
{!isGroup(option) && (
|
|
192
217
|
<Option {...getOptionProps(option, virtualRow.index)} />
|
|
@@ -195,72 +220,70 @@ export const VirtualOptionsList = forwardRef<HTMLDivElement, OptionsListProps>(
|
|
|
195
220
|
);
|
|
196
221
|
});
|
|
197
222
|
|
|
198
|
-
const contentNodeProps = {
|
|
199
|
-
className: styles.inner,
|
|
200
|
-
style: { height: `${rowVirtualizer.totalSize}px` },
|
|
201
|
-
ref: listRef,
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const renderWithCustomScrollbar = () => (
|
|
205
|
-
<Scrollbar
|
|
206
|
-
className={cn(styles.scrollable, scrollbarClassName)}
|
|
207
|
-
ref={scrollbarRef}
|
|
208
|
-
horizontalAutoStretch={optionsListWidth === 'content'}
|
|
209
|
-
scrollableNodeProps={{ onScroll, ref: parentRef }}
|
|
210
|
-
contentNodeProps={contentNodeProps}
|
|
211
|
-
>
|
|
212
|
-
{renderList()}
|
|
213
|
-
</Scrollbar>
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
const renderWithNativeScrollbar = () => {
|
|
217
|
-
if (visibleOptions) {
|
|
218
|
-
return (
|
|
219
|
-
<div
|
|
220
|
-
className={cn(styles.scrollable, scrollbarClassName)}
|
|
221
|
-
ref={mergeRefs([parentRef, ref])}
|
|
222
|
-
onScroll={onScroll}
|
|
223
|
-
>
|
|
224
|
-
<div {...contentNodeProps}>{renderList()}</div>
|
|
225
|
-
</div>
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return <div {...contentNodeProps}>{renderList()}</div>;
|
|
230
|
-
};
|
|
231
|
-
|
|
232
223
|
const resetHighlightedIndex = () => setHighlightedIndex?.(-1);
|
|
233
224
|
|
|
234
225
|
if (options.length === 0 && !emptyPlaceholder) {
|
|
235
226
|
return null;
|
|
236
227
|
}
|
|
237
228
|
|
|
229
|
+
const [firstVirualRow] = rowVirtualizer.virtualItems;
|
|
230
|
+
|
|
238
231
|
return (
|
|
239
232
|
<div
|
|
240
|
-
|
|
241
|
-
|
|
233
|
+
{...(nativeScrollbar && { 'data-test-id': dataTestId })}
|
|
234
|
+
className={cn(styles.optionsList, styles[`size-${size}`], className)}
|
|
242
235
|
>
|
|
243
236
|
{header && (
|
|
244
237
|
<div
|
|
245
|
-
className={styles.
|
|
238
|
+
className={cn(styles.optionsListHeader, {
|
|
239
|
+
[styles.headerHighlighted]: !scrollTop,
|
|
240
|
+
})}
|
|
246
241
|
onMouseEnter={resetHighlightedIndex}
|
|
247
242
|
>
|
|
248
243
|
{header}
|
|
249
244
|
</div>
|
|
250
245
|
)}
|
|
251
246
|
|
|
252
|
-
{
|
|
247
|
+
{!noOptions && (
|
|
248
|
+
<ScrollbarPrivate
|
|
249
|
+
native={nativeScrollbar}
|
|
250
|
+
className={scrollbarClassName}
|
|
251
|
+
style={{ maxHeight }}
|
|
252
|
+
scrollableNodeProps={{
|
|
253
|
+
ref: scrollableNodeRef,
|
|
254
|
+
onScroll: handleScroll,
|
|
255
|
+
className: scrollableNodeClassName,
|
|
256
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
257
|
+
// @ts-expect-error
|
|
258
|
+
'data-test-id': nativeScrollbar ? undefined : dataTestId,
|
|
259
|
+
}}
|
|
260
|
+
contentNodeProps={{
|
|
261
|
+
className: contentNodeClassName,
|
|
262
|
+
style: { minHeight: rowVirtualizer.totalSize },
|
|
263
|
+
}}
|
|
264
|
+
>
|
|
265
|
+
<div
|
|
266
|
+
className={cn(styles.list, listNodeClassName)}
|
|
267
|
+
ref={listRef}
|
|
268
|
+
style={{ transform: `translateY(${firstVirualRow?.start ?? 0}px)` }}
|
|
269
|
+
>
|
|
270
|
+
{renderList()}
|
|
271
|
+
</div>
|
|
272
|
+
</ScrollbarPrivate>
|
|
273
|
+
)}
|
|
253
274
|
|
|
254
|
-
{emptyPlaceholder &&
|
|
275
|
+
{emptyPlaceholder && noOptions && (
|
|
255
276
|
<div className={styles.emptyPlaceholder}>{emptyPlaceholder}</div>
|
|
256
277
|
)}
|
|
257
278
|
|
|
258
279
|
{showFooter && footer && (
|
|
259
280
|
<div
|
|
260
281
|
onMouseEnter={resetHighlightedIndex}
|
|
261
|
-
className={cn(styles.
|
|
282
|
+
className={cn(styles.optionsListFooter, footerClassName, {
|
|
262
283
|
[styles.withBorder]:
|
|
263
|
-
visibleOptions &&
|
|
284
|
+
visibleOptions &&
|
|
285
|
+
flatOptions.length > visibleOptions &&
|
|
286
|
+
!scrollBottom,
|
|
264
287
|
})}
|
|
265
288
|
>
|
|
266
289
|
{footer}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
@import '@alfalab/core-components-vars/src/index.css';
|
|
2
|
+
@import '../../../../vars.css';
|
|
2
3
|
|
|
3
4
|
.desktop {
|
|
4
|
-
padding: var(--gap-12)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
5
|
+
padding: calc(var(--gap-12) + var(--select-options-list-top-padding))
|
|
6
|
+
calc(var(--gap-12) + var(--select-options-list-right-padding)) var(--gap-12)
|
|
7
|
+
calc(var(--gap-12) + var(--select-options-list-left-padding));
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
.mobile {
|