Skip to content

Commit 5688b61

Browse files
committed
refactor(CDropdown): optimize menu visibility toggling
1 parent a2d3eb5 commit 5688b61

File tree

3 files changed

+60
-33
lines changed

3 files changed

+60
-33
lines changed

packages/coreui-react/src/components/dropdown/CDropdown.tsx

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -214,19 +214,6 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
214214
const allowPopperUse = popper && typeof alignment !== 'object'
215215
const Component = variant === 'nav-item' ? 'li' : as
216216

217-
const contextValues = {
218-
alignment,
219-
container,
220-
dark,
221-
dropdownMenuRef,
222-
dropdownToggleRef,
223-
popper: allowPopperUse,
224-
portal,
225-
variant,
226-
visible: _visible,
227-
setVisible,
228-
}
229-
230217
const computedPopperConfig: Partial<Options> = useMemo(() => {
231218
const defaultPopperConfig = {
232219
modifiers: [
@@ -247,14 +234,28 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
247234
}, [offset, placement, direction, alignment, popperConfig])
248235

249236
useEffect(() => {
250-
setVisible(visible)
237+
if (visible) {
238+
handleShow()
239+
} else {
240+
handleHide()
241+
}
251242
}, [visible])
252243

253244
useEffect(() => {
254245
const toggleElement = dropdownToggleElement
255246
const menuElement = dropdownMenuRef.current
247+
if (allowPopperUse && menuElement && toggleElement && _visible) {
248+
initPopper(toggleElement, menuElement, computedPopperConfig)
249+
}
250+
}, [dropdownToggleElement])
251+
252+
const handleShow = () => {
253+
const toggleElement = dropdownToggleElement
254+
const menuElement = dropdownMenuRef.current
255+
256+
if (toggleElement && menuElement) {
257+
setVisible(true)
256258

257-
if (_visible && toggleElement && menuElement) {
258259
if (allowPopperUse) {
259260
initPopper(toggleElement, menuElement, computedPopperConfig)
260261
}
@@ -268,21 +269,26 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
268269

269270
onShow?.()
270271
}
272+
}
271273

272-
return () => {
273-
if (allowPopperUse) {
274-
destroyPopper()
275-
}
276-
277-
toggleElement?.removeEventListener('keydown', handleKeydown)
278-
menuElement?.removeEventListener('keydown', handleKeydown)
274+
const handleHide = () => {
275+
setVisible(false)
279276

280-
window.removeEventListener('mouseup', handleMouseUp)
281-
window.removeEventListener('keyup', handleKeyup)
277+
const toggleElement = dropdownToggleElement
278+
const menuElement = dropdownMenuRef.current
282279

283-
onHide?.()
280+
if (allowPopperUse) {
281+
destroyPopper()
284282
}
285-
}, [dropdownToggleElement, _visible])
283+
284+
toggleElement?.removeEventListener('keydown', handleKeydown)
285+
menuElement?.removeEventListener('keydown', handleKeydown)
286+
287+
window.removeEventListener('mouseup', handleMouseUp)
288+
window.removeEventListener('keyup', handleKeyup)
289+
290+
onHide?.()
291+
}
286292

287293
const handleKeydown = (event: KeyboardEvent) => {
288294
if (
@@ -305,7 +311,7 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
305311
}
306312

307313
if (event.key === 'Escape') {
308-
setVisible(false)
314+
handleHide()
309315
}
310316
}
311317

@@ -323,11 +329,25 @@ export const CDropdown: PolymorphicRefForwardingComponent<'div', CDropdownProps>
323329
(autoClose === 'inside' && dropdownMenuRef.current.contains(event.target as HTMLElement)) ||
324330
(autoClose === 'outside' && !dropdownMenuRef.current.contains(event.target as HTMLElement))
325331
) {
326-
setTimeout(() => setVisible(false), 1)
332+
setTimeout(() => handleHide(), 1)
327333
return
328334
}
329335
}
330336

337+
const contextValues = {
338+
alignment,
339+
container,
340+
dark,
341+
dropdownMenuRef,
342+
dropdownToggleRef,
343+
handleHide,
344+
handleShow,
345+
popper: allowPopperUse,
346+
portal,
347+
variant,
348+
visible: _visible,
349+
}
350+
331351
return (
332352
<CDropdownContext.Provider value={contextValues}>
333353
{variant === 'input-group' ? (

packages/coreui-react/src/components/dropdown/CDropdownContext.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export interface CDropdownContextProps {
77
dark?: boolean
88
dropdownMenuRef: RefObject<HTMLDivElement | HTMLUListElement | null>
99
dropdownToggleRef: (node: HTMLElement | null) => void
10-
setVisible: React.Dispatch<React.SetStateAction<boolean | undefined>>
10+
handleHide?: () => void
11+
handleShow?: () => void
1112
popper?: boolean
1213
portal?: boolean
1314
variant?: 'btn-group' | 'dropdown' | 'input-group' | 'nav-item'

packages/coreui-react/src/components/dropdown/CDropdownToggle.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,24 @@ export const CDropdownToggle: FC<CDropdownToggleProps> = ({
4545
trigger = 'click',
4646
...rest
4747
}) => {
48-
const { dropdownToggleRef, variant, visible, setVisible } = useContext(CDropdownContext)
48+
const { dropdownToggleRef, handleHide, handleShow, variant, visible } =
49+
useContext(CDropdownContext)
4950

5051
const triggers = {
5152
...((trigger === 'click' || trigger.includes('click')) && {
5253
onClick: (event: React.MouseEvent<HTMLElement>) => {
5354
event.preventDefault()
54-
setVisible(!visible)
55+
56+
if (visible) {
57+
handleHide?.()
58+
} else {
59+
handleShow?.()
60+
}
5561
},
5662
}),
5763
...((trigger === 'focus' || trigger.includes('focus')) && {
58-
onFocus: () => setVisible(true),
59-
onBlur: () => setVisible(false),
64+
onFocus: () => handleShow?.(),
65+
onBlur: () => handleHide?.(),
6066
}),
6167
}
6268

0 commit comments

Comments
 (0)