@rescui/dropdown 0.11.1 → 0.11.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/lib/dropdown.js +22 -35
- package/lib/dropdown.p.module.css.js +9 -12
- package/lib/index.css +27 -76
- package/lib/use-dropdown-state.d.ts +11 -1
- package/lib/use-dropdown-state.js +114 -17
- package/package.json +6 -6
package/lib/dropdown.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import React, { useState, useRef,
|
|
2
|
+
import React, { useState, useRef, useMemo, useImperativeHandle, useContext, useCallback, useEffect } from 'react';
|
|
3
3
|
import PropTypes from 'prop-types';
|
|
4
4
|
import cn from 'classnames';
|
|
5
5
|
import { usePopper } from 'react-popper';
|
|
@@ -74,7 +74,7 @@ function useOnOutsideClick(_ref3) {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
onOutsideClick();
|
|
77
|
-
}, [onOutsideClick,
|
|
77
|
+
}, [onOutsideClick, ignoreElements]); // eslint-disable-next-line consistent-return
|
|
78
78
|
|
|
79
79
|
useEffect(() => {
|
|
80
80
|
if (isEnabled && ignoreElementRefsExists && ownerDocument) {
|
|
@@ -143,26 +143,21 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
143
143
|
disableFlip = false
|
|
144
144
|
} = _ref5;
|
|
145
145
|
const [popperRef, setPopperRef] = useState(null);
|
|
146
|
-
const [triggerRef, setTriggerRef] = useState(
|
|
146
|
+
const [triggerRef, setTriggerRef] = useState(undefined);
|
|
147
147
|
const {
|
|
148
148
|
setContentEl,
|
|
149
149
|
scrollableStyles
|
|
150
150
|
} = useScrollableContainer();
|
|
151
|
-
const scrollableRef = useRef(null); //
|
|
152
|
-
// the
|
|
153
|
-
// So even if isOpen prop is used, the tooltip will not render on the server, and
|
|
154
|
-
// it's the same on the first client render, so there is no hydration issue.
|
|
151
|
+
const scrollableRef = useRef(null); // An undefined ref means that React has not committed the trigger yet. It keeps
|
|
152
|
+
// the server and first client render equal without an extra hydration state.
|
|
155
153
|
|
|
156
|
-
const
|
|
157
|
-
useEffect(() => {
|
|
158
|
-
setIsFirstRender(false);
|
|
159
|
-
}, []);
|
|
160
|
-
const isOpen = isFirstRender && !renderInTrigger ? false : isOpenFromProps;
|
|
154
|
+
const isOpen = typeof triggerRef !== 'undefined' || renderInTrigger ? isOpenFromProps : false;
|
|
161
155
|
const ownerDocument = triggerRef?.ownerDocument;
|
|
156
|
+
const outsideClickIgnoreElements = useMemo(() => [popperRef, triggerRef], [popperRef, triggerRef]);
|
|
162
157
|
useOnOutsideClick({
|
|
163
158
|
isEnabled: isOpen,
|
|
164
159
|
onOutsideClick: onRequestClose,
|
|
165
|
-
ignoreElements:
|
|
160
|
+
ignoreElements: outsideClickIgnoreElements,
|
|
166
161
|
ownerDocument
|
|
167
162
|
});
|
|
168
163
|
useKeyPress({
|
|
@@ -177,12 +172,11 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
177
172
|
onKeyPress: onRequestClose,
|
|
178
173
|
ownerDocument
|
|
179
174
|
});
|
|
180
|
-
const [handleAnimationEnd, dropdownState] = useDropdownState(isOpen, cancelCloseAnimation);
|
|
181
175
|
const {
|
|
182
176
|
styles: popperStyles,
|
|
183
177
|
attributes,
|
|
184
178
|
update
|
|
185
|
-
} = usePopper(triggerRef, popperRef, getPopperOptions({
|
|
179
|
+
} = usePopper(triggerRef ?? null, popperRef, getPopperOptions({
|
|
186
180
|
placement,
|
|
187
181
|
maxHeight,
|
|
188
182
|
leastMaxHeight,
|
|
@@ -190,11 +184,17 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
190
184
|
maxWidth,
|
|
191
185
|
disableFlip
|
|
192
186
|
}));
|
|
187
|
+
const resolvedPlacement = attributes.popper?.['data-popper-placement'] ?? null;
|
|
188
|
+
const dropdownState = useDropdownState({
|
|
189
|
+
isOpen,
|
|
190
|
+
cancelCloseAnimation,
|
|
191
|
+
contentRef: scrollableRef,
|
|
192
|
+
popperElement: popperRef,
|
|
193
|
+
resolvedPlacement
|
|
194
|
+
});
|
|
193
195
|
useImperativeHandle(externalRef, () => ({
|
|
194
196
|
scheduleUpdate: () => {
|
|
195
|
-
|
|
196
|
-
update();
|
|
197
|
-
}
|
|
197
|
+
update?.();
|
|
198
198
|
}
|
|
199
199
|
}));
|
|
200
200
|
const {
|
|
@@ -208,7 +208,8 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
208
208
|
ref: setTriggerRef
|
|
209
209
|
}, trigger);
|
|
210
210
|
const dropdownShouldBeRendered = dropdownState !== DROPDOWN_STATES.CLOSE;
|
|
211
|
-
const
|
|
211
|
+
const wrapperIsInitial = dropdownState === DROPDOWN_STATES.OPENING_PREPARE;
|
|
212
|
+
const wrapperShouldBeHidden = wrapperIsInitial || dropdownState === DROPDOWN_STATES.CLOSING;
|
|
212
213
|
let primaryColors = {};
|
|
213
214
|
let themeFromCss = 'light';
|
|
214
215
|
|
|
@@ -230,19 +231,7 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
230
231
|
noProvider
|
|
231
232
|
} = useContext(ThemeContext);
|
|
232
233
|
const theme = themeFromProps || (noProvider ? themeFromCss : themeFromContext);
|
|
233
|
-
const childrenTheme = mode === 'rock' ? getInverseTheme(theme) : theme;
|
|
234
|
-
// it may happen if the user hard-disabled all transitions in his browser
|
|
235
|
-
|
|
236
|
-
useEffect(() => {
|
|
237
|
-
if (dropdownShouldBeRendered && scrollableRef.current) {
|
|
238
|
-
const ownerWindow = scrollableRef.current.ownerDocument.defaultView;
|
|
239
|
-
const isTransitionDisabled = ownerWindow?.getComputedStyle(scrollableRef.current).transitionDuration === '0s';
|
|
240
|
-
|
|
241
|
-
if (isTransitionDisabled) {
|
|
242
|
-
handleAnimationEnd();
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}, [handleAnimationEnd, dropdownShouldBeRendered]);
|
|
234
|
+
const childrenTheme = mode === 'rock' ? getInverseTheme(theme) : theme;
|
|
246
235
|
return /*#__PURE__*/React.createElement(React.Fragment, null, triggerRenderer, dropdownShouldBeRendered && /*#__PURE__*/React.createElement(Portal, {
|
|
247
236
|
target: renderInTrigger ? triggerRef : ownerDocument?.body
|
|
248
237
|
}, /*#__PURE__*/React.createElement("div", {
|
|
@@ -261,9 +250,7 @@ const DropdownRender = (_ref5, externalRef) => {
|
|
|
261
250
|
}, /*#__PURE__*/React.createElement("div", {
|
|
262
251
|
ref: scrollableRef,
|
|
263
252
|
style: scrollableStyles,
|
|
264
|
-
className: cn(styles.contentWrapper, wrapperShouldBeHidden && styles.wrapperHidden),
|
|
265
|
-
onTransitionEnd: handleAnimationEnd,
|
|
266
|
-
onAnimationEnd: handleAnimationEnd,
|
|
253
|
+
className: cn(styles.contentWrapper, wrapperIsInitial && styles.wrapperInitial, wrapperShouldBeHidden && styles.wrapperHidden),
|
|
267
254
|
"data-test": "dropdown-content"
|
|
268
255
|
}, /*#__PURE__*/React.createElement(ThemeProvider, {
|
|
269
256
|
ignoreThemeValueForCSSApi: noProvider,
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
var styles = {
|
|
2
|
-
"themeLight": "
|
|
3
|
-
"themeDark": "
|
|
4
|
-
"dropdown": "
|
|
5
|
-
"triggerWrapper": "
|
|
6
|
-
"contentWrapper": "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"wrapperHidden": "_wrapperHidden_1il2d95_110",
|
|
12
|
-
"modeClassic": "_modeClassic_1il2d95_130",
|
|
13
|
-
"modeRock": "_modeRock_1il2d95_134"
|
|
2
|
+
"themeLight": "_themeLight_3wijjk_7",
|
|
3
|
+
"themeDark": "_themeDark_3wijjk_10",
|
|
4
|
+
"dropdown": "_dropdown_3wijjk_14",
|
|
5
|
+
"triggerWrapper": "_triggerWrapper_3wijjk_21",
|
|
6
|
+
"contentWrapper": "_contentWrapper_3wijjk_25",
|
|
7
|
+
"wrapperInitial": "_wrapperInitial_3wijjk_41",
|
|
8
|
+
"wrapperHidden": "_wrapperHidden_3wijjk_45",
|
|
9
|
+
"modeClassic": "_modeClassic_3wijjk_65",
|
|
10
|
+
"modeRock": "_modeRock_3wijjk_69"
|
|
14
11
|
};
|
|
15
12
|
export { styles as default };
|
package/lib/index.css
CHANGED
|
@@ -1,58 +1,15 @@
|
|
|
1
|
-
@
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
transform:translateY(-10px);
|
|
6
|
-
}
|
|
7
|
-
100%{
|
|
8
|
-
opacity:1;
|
|
9
|
-
|
|
10
|
-
transform:none;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
@keyframes _fadeInBottomToTop_1il2d95_1{
|
|
14
|
-
0%{
|
|
15
|
-
opacity:0;
|
|
16
|
-
|
|
17
|
-
transform:translateY(10px);
|
|
18
|
-
}
|
|
19
|
-
100%{
|
|
20
|
-
opacity:1;
|
|
21
|
-
|
|
22
|
-
transform:none;
|
|
23
|
-
}
|
|
1
|
+
@property --_rs-theme-dark-coefficient{
|
|
2
|
+
syntax:'<number>';
|
|
3
|
+
inherits:true;
|
|
4
|
+
initial-value:0;
|
|
24
5
|
}
|
|
25
|
-
|
|
26
|
-
0%{
|
|
27
|
-
opacity:0;
|
|
28
|
-
|
|
29
|
-
transform:translateX(-10px);
|
|
30
|
-
}
|
|
31
|
-
100%{
|
|
32
|
-
opacity:1;
|
|
33
|
-
|
|
34
|
-
transform:none;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
@keyframes _fadeInRightToLeft_1il2d95_1{
|
|
38
|
-
0%{
|
|
39
|
-
opacity:0;
|
|
40
|
-
|
|
41
|
-
transform:translateX(10px);
|
|
42
|
-
}
|
|
43
|
-
100%{
|
|
44
|
-
opacity:1;
|
|
45
|
-
|
|
46
|
-
transform:none;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
._themeLight_1il2d95_59{
|
|
6
|
+
._themeLight_3wijjk_7{
|
|
50
7
|
--rs-theme-dark:0;
|
|
51
8
|
}
|
|
52
|
-
.
|
|
9
|
+
._themeDark_3wijjk_10{
|
|
53
10
|
--rs-theme-dark:1;
|
|
54
11
|
}
|
|
55
|
-
.
|
|
12
|
+
._dropdown_3wijjk_14{
|
|
56
13
|
--_rs-theme-dark:var(
|
|
57
14
|
--_rs-internal-force-theme-dark-consult-rescui-before-using,
|
|
58
15
|
var(--rs-theme-dark, 0)
|
|
@@ -62,7 +19,7 @@
|
|
|
62
19
|
--_rs-theme-light-coefficient:calc(1 - var(--_rs-theme-dark-coefficient));
|
|
63
20
|
}
|
|
64
21
|
@supports (color: color-mix(in srgb, #fff 50%, #000 50%)){
|
|
65
|
-
.
|
|
22
|
+
._dropdown_3wijjk_14{
|
|
66
23
|
--_rs-color-primary:color-mix(in srgb, var(--rs-color-primary-light-theme, #6B57FF) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-dark-theme, #8473FF) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
|
|
67
24
|
--_rs-color-primary-dim:color-mix(in srgb, var(--rs-color-primary-dim-light-theme, #8979ff) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-dim-dark-theme, #6f61d2) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
|
|
68
25
|
--_rs-color-primary-fog:color-mix(in srgb, var(--rs-color-primary-fog-light-theme, #e1ddff) calc(100%*var(--_rs-theme-light-coefficient, 1)), var(--rs-color-primary-fog-dark-theme, #2e2b49) calc(100%*var(--_rs-theme-dark-coefficient, 0)));
|
|
@@ -71,7 +28,7 @@
|
|
|
71
28
|
}
|
|
72
29
|
}
|
|
73
30
|
@supports not (color: color-mix(in srgb, #fff 50%, #000 50%)){
|
|
74
|
-
.
|
|
31
|
+
._dropdown_3wijjk_14{
|
|
75
32
|
--_rs-color-primary:rgb(calc(var(--_rs-theme-light-coefficient, 1)*107 + var(--_rs-theme-dark-coefficient, 0)*132), calc(var(--_rs-theme-light-coefficient, 1)*87 + var(--_rs-theme-dark-coefficient, 0)*115), 255);
|
|
76
33
|
--_rs-color-primary-dim:rgb(calc(var(--_rs-theme-light-coefficient, 1)*137 + var(--_rs-theme-dark-coefficient, 0)*111), calc(var(--_rs-theme-light-coefficient, 1)*121 + var(--_rs-theme-dark-coefficient, 0)*97), calc(var(--_rs-theme-light-coefficient, 1)*255 + var(--_rs-theme-dark-coefficient, 0)*210));
|
|
77
34
|
--_rs-color-primary-fog:rgb(calc(var(--_rs-theme-light-coefficient, 1)*225 + var(--_rs-theme-dark-coefficient, 0)*46), calc(var(--_rs-theme-light-coefficient, 1)*221 + var(--_rs-theme-dark-coefficient, 0)*43), calc(var(--_rs-theme-light-coefficient, 1)*255 + var(--_rs-theme-dark-coefficient, 0)*73));
|
|
@@ -79,14 +36,14 @@
|
|
|
79
36
|
--_rs-color-primary-t-fog:rgba(calc(var(--_rs-theme-light-coefficient, 1)*107 + var(--_rs-theme-dark-coefficient, 0)*132), calc(var(--_rs-theme-light-coefficient, 1)*87 + var(--_rs-theme-dark-coefficient, 0)*115), 255, calc(var(--_rs-theme-light-coefficient, 1)*0.2 + var(--_rs-theme-dark-coefficient, 0)*0.3));
|
|
80
37
|
}
|
|
81
38
|
}
|
|
82
|
-
.
|
|
39
|
+
._dropdown_3wijjk_14 {
|
|
83
40
|
display:flex;
|
|
84
41
|
flex-direction:column;
|
|
85
42
|
}
|
|
86
|
-
.
|
|
43
|
+
._triggerWrapper_3wijjk_21{
|
|
87
44
|
display:inline-block;
|
|
88
45
|
}
|
|
89
|
-
.
|
|
46
|
+
._contentWrapper_3wijjk_25{
|
|
90
47
|
position:relative;
|
|
91
48
|
|
|
92
49
|
display:block;
|
|
@@ -98,42 +55,36 @@
|
|
|
98
55
|
background-clip:padding-box;
|
|
99
56
|
box-shadow:0 0 0 1px rgb(calc(209 - var(--_rs-theme-dark-coefficient, 0)*138), calc(209 - var(--_rs-theme-dark-coefficient, 0)*138), calc(210 - var(--_rs-theme-dark-coefficient, 0)*137));
|
|
100
57
|
|
|
101
|
-
transition:opacity var(--rs-transition-function,
|
|
102
|
-
}
|
|
103
|
-
[data-popper-placement^='top'] ._contentWrapper_1il2d95_77{
|
|
104
|
-
animation:_fadeInBottomToTop_1il2d95_1 var(--rs-transition-function, ease) 200ms;
|
|
105
|
-
}
|
|
106
|
-
[data-popper-placement^='bottom'] ._contentWrapper_1il2d95_77{
|
|
107
|
-
animation:_fadeInTopToBottom_1il2d95_1 var(--rs-transition-function, ease) 200ms;
|
|
58
|
+
transition:opacity var(--rs-transition-function, ease) 200ms, transform var(--rs-transition-function, ease) 200ms;
|
|
108
59
|
}
|
|
109
|
-
|
|
110
|
-
|
|
60
|
+
._wrapperInitial_3wijjk_41{
|
|
61
|
+
transition:none;
|
|
111
62
|
}
|
|
112
|
-
|
|
113
|
-
animation:_fadeInRightToLeft_1il2d95_1 var(--rs-transition-function, ease) 200ms;
|
|
114
|
-
}
|
|
115
|
-
._wrapperHidden_1il2d95_110{
|
|
63
|
+
._wrapperHidden_3wijjk_45{
|
|
116
64
|
opacity:0;
|
|
117
65
|
}
|
|
118
|
-
[data-popper-placement^='top'] .
|
|
66
|
+
[data-popper-placement^='top'] ._wrapperHidden_3wijjk_45{
|
|
119
67
|
transform:translateY(10px);
|
|
120
68
|
}
|
|
121
|
-
[data-popper-placement^='bottom'] .
|
|
69
|
+
[data-popper-placement^='bottom'] ._wrapperHidden_3wijjk_45{
|
|
122
70
|
transform:translateY(-10px);
|
|
123
71
|
}
|
|
124
|
-
[data-popper-placement^='right'] .
|
|
72
|
+
[data-popper-placement^='right'] ._wrapperHidden_3wijjk_45{
|
|
125
73
|
transform:translateX(-10px);
|
|
126
74
|
}
|
|
127
|
-
[data-popper-placement^='left'] .
|
|
75
|
+
[data-popper-placement^='left'] ._wrapperHidden_3wijjk_45{
|
|
128
76
|
transform:translateX(10px);
|
|
129
77
|
}
|
|
130
|
-
.
|
|
78
|
+
._modeClassic_3wijjk_65 ._contentWrapper_3wijjk_25{
|
|
131
79
|
background-color:rgb(calc(255 - var(--_rs-theme-dark-coefficient, 0)*230), calc(255 - var(--_rs-theme-dark-coefficient, 0)*230), calc(255 - var(--_rs-theme-dark-coefficient, 0)*227));
|
|
132
80
|
}
|
|
133
|
-
.
|
|
81
|
+
._modeRock_3wijjk_69 ._contentWrapper_3wijjk_25{
|
|
134
82
|
background-color:rgb(calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(25 + var(--_rs-theme-dark-coefficient, 0)*230), calc(28 + var(--_rs-theme-dark-coefficient, 0)*227));
|
|
135
83
|
}
|
|
136
|
-
.
|
|
84
|
+
._modeClassic_3wijjk_65._themeDark_3wijjk_10 ._contentWrapper_3wijjk_25, ._modeRock_3wijjk_69._themeLight_3wijjk_7 ._contentWrapper_3wijjk_25{
|
|
85
|
+
color-scheme:dark;
|
|
86
|
+
}
|
|
87
|
+
._modeRock_3wijjk_69{
|
|
137
88
|
--rs-parent-color-primary-light-theme:var(--rs-color-primary-light-theme);
|
|
138
89
|
--rs-parent-color-primary-dim-light-theme:var(
|
|
139
90
|
--rs-color-primary-dim-light-theme
|
|
@@ -162,7 +113,7 @@
|
|
|
162
113
|
--rs-color-primary-t-fog-dark-theme
|
|
163
114
|
);
|
|
164
115
|
}
|
|
165
|
-
.
|
|
116
|
+
._modeRock_3wijjk_69 > *{
|
|
166
117
|
--rs-color-primary-dark-theme:var(--rs-parent-color-primary-light-theme);
|
|
167
118
|
--rs-color-primary-dim-dark-theme:var(
|
|
168
119
|
--rs-parent-color-primary-dim-light-theme
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
+
import type { RefObject } from 'react';
|
|
1
2
|
export declare enum DROPDOWN_STATES {
|
|
3
|
+
OPENING_PREPARE = "OPENING_PREPARE",
|
|
2
4
|
OPENING = "OPENING",
|
|
3
5
|
OPEN = "OPEN",
|
|
4
6
|
CLOSING = "CLOSING",
|
|
5
7
|
CLOSE = "CLOSE"
|
|
6
8
|
}
|
|
7
|
-
|
|
9
|
+
type UseDropdownStateOptions = {
|
|
10
|
+
isOpen: boolean;
|
|
11
|
+
cancelCloseAnimation?: boolean;
|
|
12
|
+
contentRef: RefObject<HTMLDivElement | null>;
|
|
13
|
+
popperElement: HTMLElement | null;
|
|
14
|
+
resolvedPlacement: string | null;
|
|
15
|
+
};
|
|
16
|
+
export declare function useDropdownState({ isOpen, cancelCloseAnimation, contentRef, popperElement, resolvedPlacement }: UseDropdownStateOptions): DROPDOWN_STATES;
|
|
17
|
+
export {};
|
|
@@ -1,36 +1,133 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { useState,
|
|
2
|
+
import { useState, useCallback, useEffect, useLayoutEffect } from 'react';
|
|
3
|
+
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
4
|
+
const DROPDOWN_TRANSITION_PROPERTIES = ['opacity', 'transform'];
|
|
5
|
+
|
|
6
|
+
const getDropdownTransition = (element, property) => element.getAnimations().find(animation => animation.transitionProperty === property);
|
|
7
|
+
|
|
3
8
|
var DROPDOWN_STATES;
|
|
4
9
|
|
|
5
10
|
(function (DROPDOWN_STATES) {
|
|
11
|
+
DROPDOWN_STATES["OPENING_PREPARE"] = "OPENING_PREPARE";
|
|
6
12
|
DROPDOWN_STATES["OPENING"] = "OPENING";
|
|
7
13
|
DROPDOWN_STATES["OPEN"] = "OPEN";
|
|
8
14
|
DROPDOWN_STATES["CLOSING"] = "CLOSING";
|
|
9
15
|
DROPDOWN_STATES["CLOSE"] = "CLOSE";
|
|
10
16
|
})(DROPDOWN_STATES || (DROPDOWN_STATES = {}));
|
|
11
17
|
|
|
12
|
-
function useDropdownState(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
function useDropdownState(_ref) {
|
|
19
|
+
let {
|
|
20
|
+
isOpen,
|
|
21
|
+
cancelCloseAnimation,
|
|
22
|
+
contentRef,
|
|
23
|
+
popperElement,
|
|
24
|
+
resolvedPlacement
|
|
25
|
+
} = _ref;
|
|
26
|
+
const [dropdownState, setDropdownState] = useState(isOpen ? DROPDOWN_STATES.OPENING_PREPARE : DROPDOWN_STATES.CLOSE);
|
|
27
|
+
const completeTransition = useCallback(expectedState => {
|
|
28
|
+
setDropdownState(state => {
|
|
29
|
+
if (state !== expectedState) {
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (expectedState === DROPDOWN_STATES.CLOSING) {
|
|
34
|
+
return DROPDOWN_STATES.CLOSE;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (expectedState === DROPDOWN_STATES.OPENING) {
|
|
38
|
+
return DROPDOWN_STATES.OPEN;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return state;
|
|
42
|
+
});
|
|
43
|
+
}, []);
|
|
44
|
+
const completeOpeningPreparation = useCallback(() => {
|
|
45
|
+
setDropdownState(state => state === DROPDOWN_STATES.OPENING_PREPARE ? DROPDOWN_STATES.OPENING : state);
|
|
46
|
+
}, []);
|
|
47
|
+
useIsomorphicLayoutEffect(() => {
|
|
48
|
+
const contentElement = contentRef.current;
|
|
49
|
+
|
|
50
|
+
if (dropdownState !== DROPDOWN_STATES.OPENING_PREPARE || !popperElement || !contentElement || !resolvedPlacement) {
|
|
51
|
+
return;
|
|
52
|
+
} // Register the initial opacity/transform with the resolved placement before
|
|
53
|
+
// removing the initial class and starting the opening transition.
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
contentElement.getBoundingClientRect();
|
|
57
|
+
completeOpeningPreparation();
|
|
58
|
+
}, [completeOpeningPreparation, contentRef, dropdownState, popperElement, resolvedPlacement]); // Track the transitions that currently belong to the OPENING/CLOSING phase:
|
|
59
|
+
// - normally, wait for both opacity and transform before completing the phase;
|
|
60
|
+
// - a style/placement change can cancel or replace a transition without
|
|
61
|
+
// changing the phase; follow the latest one, including a replacement that
|
|
62
|
+
// appears just after the previous transition finishes;
|
|
63
|
+
// - if OPENING changes to CLOSING (or vice versa), cleanup invalidates these
|
|
64
|
+
// waiters and the next phase installs its own, so a stale transition cannot
|
|
65
|
+
// complete it;
|
|
66
|
+
// - if no transitions are created (for example, their duration is 0s),
|
|
67
|
+
// complete the phase immediately.
|
|
68
|
+
|
|
69
|
+
useIsomorphicLayoutEffect(() => {
|
|
70
|
+
if (dropdownState !== DROPDOWN_STATES.OPENING && dropdownState !== DROPDOWN_STATES.CLOSING) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
18
73
|
|
|
19
|
-
|
|
20
|
-
} else if (stateRef.current === DROPDOWN_STATES.OPENING && isOpen) {
|
|
21
|
-
stateRef.current = DROPDOWN_STATES.OPEN; //force rerender
|
|
74
|
+
const contentElement = contentRef.current;
|
|
22
75
|
|
|
23
|
-
|
|
76
|
+
if (!contentElement) {
|
|
77
|
+
return undefined;
|
|
24
78
|
}
|
|
25
|
-
}, [isOpen]);
|
|
26
79
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
80
|
+
let isCurrentPhase = true;
|
|
81
|
+
|
|
82
|
+
const completeCurrentPhase = () => {
|
|
83
|
+
if (isCurrentPhase) {
|
|
84
|
+
completeTransition(dropdownState);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const waitForCurrentTransition = (property, previousTransition) => {
|
|
89
|
+
if (!isCurrentPhase) {
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const transition = getDropdownTransition(contentElement, property);
|
|
94
|
+
|
|
95
|
+
if (!transition || transition === previousTransition) {
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return transition.finished.then(() => {
|
|
100
|
+
if (!isCurrentPhase) {
|
|
101
|
+
return Promise.resolve();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const replacementTransition = getDropdownTransition(contentElement, property);
|
|
105
|
+
|
|
106
|
+
if (replacementTransition && replacementTransition !== transition) {
|
|
107
|
+
return waitForCurrentTransition(property, transition);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return Promise.resolve();
|
|
111
|
+
}, () => waitForCurrentTransition(property, transition));
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
Promise.all(DROPDOWN_TRANSITION_PROPERTIES.map(property => waitForCurrentTransition(property))).then(completeCurrentPhase);
|
|
115
|
+
return () => {
|
|
116
|
+
isCurrentPhase = false;
|
|
117
|
+
};
|
|
118
|
+
}, [completeTransition, contentRef, dropdownState, popperElement, resolvedPlacement]); // React retries this component before rendering its children when the phase changes.
|
|
119
|
+
|
|
120
|
+
if (isOpen && dropdownState === DROPDOWN_STATES.CLOSE) {
|
|
121
|
+
setDropdownState(DROPDOWN_STATES.OPENING_PREPARE);
|
|
122
|
+
} else if (isOpen && dropdownState === DROPDOWN_STATES.CLOSING) {
|
|
123
|
+
setDropdownState(DROPDOWN_STATES.OPENING);
|
|
124
|
+
} else if (!isOpen && dropdownState === DROPDOWN_STATES.OPENING_PREPARE) {
|
|
125
|
+
setDropdownState(DROPDOWN_STATES.CLOSE);
|
|
126
|
+
} else if (!isOpen && (dropdownState === DROPDOWN_STATES.OPEN || dropdownState === DROPDOWN_STATES.OPENING)) {
|
|
127
|
+
setDropdownState(cancelCloseAnimation ? DROPDOWN_STATES.CLOSE : DROPDOWN_STATES.CLOSING);
|
|
31
128
|
}
|
|
32
129
|
|
|
33
|
-
return
|
|
130
|
+
return dropdownState;
|
|
34
131
|
}
|
|
35
132
|
|
|
36
133
|
export { DROPDOWN_STATES, useDropdownState };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rescui/dropdown",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "JetBrains",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@babel/runtime-corejs3": "^7.26.0",
|
|
20
20
|
"@popperjs/core": "^2.11.8",
|
|
21
|
-
"@rescui/use-resize-observer": "^0.3.
|
|
21
|
+
"@rescui/use-resize-observer": "^0.3.4",
|
|
22
22
|
"classnames": "^2.2.6",
|
|
23
23
|
"popper-max-size-modifier": "^0.2.0",
|
|
24
24
|
"react-focus-lock": "^2.8.1",
|
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
"react-dom": ">=16.8.0 <20"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"@rescui/colors": "^0.3.
|
|
35
|
-
"@rescui/icons": "^1.8.
|
|
34
|
+
"@rescui/colors": "^0.3.3",
|
|
35
|
+
"@rescui/icons": "^1.8.4",
|
|
36
36
|
"@rescui/scripts": "^0.5.2",
|
|
37
|
-
"@rescui/typography": "^0.27.
|
|
37
|
+
"@rescui/typography": "^0.27.4",
|
|
38
38
|
"@rescui/visual-regression": "^0.1.4"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "rescui-scripts build"
|
|
42
42
|
},
|
|
43
43
|
"nx": {},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "3711cbfc0c797014395421b008aa5331d6d47c6e"
|
|
45
45
|
}
|