Skip to content

Commit a3f1915

Browse files
committed
refactor(CDropdown): allow to select the next/previous element by press the up/down arrow
1 parent 4765bd5 commit a3f1915

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

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

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,23 @@ interface ContextProps extends CDropdownProps {
107107
portal: boolean
108108
}
109109

110+
export const getNextActiveElement = (list: HTMLElement[], activeElement: HTMLElement, shouldGetNext: boolean, isCycleAllowed: boolean) => {
111+
const listLength = list.length
112+
let index = list.indexOf(activeElement)
113+
114+
if (index === -1) {
115+
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
116+
}
117+
118+
index += shouldGetNext ? 1 : -1
119+
120+
if (isCycleAllowed) {
121+
index = (index + listLength) % listLength
122+
}
123+
124+
return list[Math.max(0, Math.min(index, listLength - 1))]
125+
}
126+
110127
const getPlacement = (
111128
placement: Placements,
112129
direction: CDropdownProps['direction'],
@@ -206,21 +223,33 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
206223
}, [visible])
207224

208225
useEffect(() => {
209-
if (_visible && dropdownToggleRef.current && dropdownMenuRef.current) {
226+
if (_visible && dropdownRef.current && dropdownToggleRef.current && dropdownMenuRef.current) {
227+
dropdownToggleRef.current.focus()
210228
popper && initPopper(dropdownToggleRef.current, dropdownMenuRef.current, popperConfig)
211229
window.addEventListener('mouseup', handleMouseUp)
212230
window.addEventListener('keyup', handleKeyup)
231+
dropdownRef.current.addEventListener('keydown', handleKeydown)
213232
onShow && onShow()
214233
}
215234

216235
return () => {
217236
popper && destroyPopper()
218237
window.removeEventListener('mouseup', handleMouseUp)
219238
window.removeEventListener('keyup', handleKeyup)
239+
dropdownRef.current && dropdownRef.current.removeEventListener('keydown', handleKeydown)
220240
onHide && onHide()
221241
}
222242
}, [_visible])
223243

244+
const handleKeydown = (event: KeyboardEvent) => {
245+
if (_visible && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
246+
const target = event.target as HTMLElement
247+
event.preventDefault()
248+
const items = [].concat(...Element.prototype.querySelectorAll.call(dropdownRef.current, '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'))
249+
getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
250+
}
251+
}
252+
224253
const handleKeyup = (event: KeyboardEvent) => {
225254
if (autoClose === false) {
226255
return

0 commit comments

Comments
 (0)