Skip to content

Commit 658577e

Browse files
committed
refactor(CDropdown): improve syntax
1 parent b063902 commit 658577e

File tree

4 files changed

+92
-89
lines changed

4 files changed

+92
-89
lines changed

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

Lines changed: 5 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,8 @@ import { placementPropType } from '../../props'
1616
import type { Placements } from '../../types'
1717
import { isRTL } from '../../utils'
1818

19-
export type Directions = 'start' | 'end'
20-
21-
export type Breakpoints =
22-
| { xs: Directions }
23-
| { sm: Directions }
24-
| { md: Directions }
25-
| { lg: Directions }
26-
| { xl: Directions }
27-
| { xxl: Directions }
28-
29-
export type Alignments = Directions | Breakpoints
19+
import type { Alignments } from './types'
20+
import { getNextActiveElement, getPlacement } from './utils'
3021

3122
export interface CDropdownProps extends HTMLAttributes<HTMLDivElement | HTMLLIElement> {
3223
/**
@@ -107,59 +98,6 @@ interface ContextProps extends CDropdownProps {
10798
portal: boolean
10899
}
109100

110-
export const getNextActiveElement = (
111-
list: HTMLElement[],
112-
activeElement: HTMLElement,
113-
shouldGetNext: boolean,
114-
isCycleAllowed: boolean,
115-
) => {
116-
const listLength = list.length
117-
let index = list.indexOf(activeElement)
118-
119-
if (index === -1) {
120-
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
121-
}
122-
123-
index += shouldGetNext ? 1 : -1
124-
125-
if (isCycleAllowed) {
126-
index = (index + listLength) % listLength
127-
}
128-
129-
return list[Math.max(0, Math.min(index, listLength - 1))]
130-
}
131-
132-
const getPlacement = (
133-
placement: Placements,
134-
direction: CDropdownProps['direction'],
135-
alignment: CDropdownProps['alignment'],
136-
isRTL: boolean,
137-
): Placements => {
138-
let _placement = placement
139-
140-
if (direction === 'dropup') {
141-
_placement = isRTL ? 'top-end' : 'top-start'
142-
}
143-
144-
if (direction === 'dropup-center') {
145-
_placement = 'top'
146-
}
147-
148-
if (direction === 'dropend') {
149-
_placement = isRTL ? 'left-start' : 'right-start'
150-
}
151-
152-
if (direction === 'dropstart') {
153-
_placement = isRTL ? 'right-start' : 'left-start'
154-
}
155-
156-
if (alignment === 'end') {
157-
_placement = isRTL ? 'bottom-start' : 'bottom-end'
158-
}
159-
160-
return _placement
161-
}
162-
163101
export const CDropdownContext = createContext({} as ContextProps)
164102

165103
export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownProps>(
@@ -251,15 +189,10 @@ export const CDropdown = forwardRef<HTMLDivElement | HTMLLIElement, CDropdownPro
251189
}, [_visible])
252190

253191
const handleKeydown = (event: KeyboardEvent) => {
254-
if (_visible && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
255-
const target = event.target as HTMLElement
192+
if (_visible && dropdownMenuRef.current && (event.key === 'ArrowDown' || event.key === 'ArrowUp')) {
256193
event.preventDefault()
257-
const items = [].concat(
258-
...Element.prototype.querySelectorAll.call(
259-
dropdownMenuRef.current,
260-
'.dropdown-item:not(.disabled):not(:disabled)',
261-
),
262-
)
194+
const target = event.target as HTMLElement
195+
const items: HTMLElement[] = Array.from(dropdownMenuRef.current.querySelectorAll('.dropdown-item:not(.disabled):not(:disabled)'))
263196
getNextActiveElement(items, target, event.key === 'ArrowDown', true).focus()
264197
}
265198
}

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

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import React, { ElementType, forwardRef, HTMLAttributes, useContext } from 'reac
22
import PropTypes from 'prop-types'
33
import classNames from 'classnames'
44

5-
import { Alignments, CDropdownContext } from './CDropdown'
5+
import { CDropdownContext } from './CDropdown'
66
import { CConditionalPortal } from '../conditional-portal'
77

88
import { useForkedRef } from '../../hooks'
99

10+
import { getAlignmentClassNames } from './utils'
11+
12+
1013
export interface CDropdownMenuProps extends HTMLAttributes<HTMLDivElement | HTMLUListElement> {
1114
/**
1215
* A string of all className you want applied to the base component.
@@ -18,21 +21,6 @@ export interface CDropdownMenuProps extends HTMLAttributes<HTMLDivElement | HTML
1821
component?: string | ElementType
1922
}
2023

21-
const alignmentClassNames = (alignment: Alignments) => {
22-
const classNames: string[] = []
23-
if (typeof alignment === 'object') {
24-
Object.keys(alignment).map((key) => {
25-
classNames.push(`dropdown-menu${key === 'xs' ? '' : `-${key}`}-${alignment[key]}`)
26-
})
27-
}
28-
29-
if (typeof alignment === 'string') {
30-
classNames.push(`dropdown-menu-${alignment}`)
31-
}
32-
33-
return classNames
34-
}
35-
3624
export const CDropdownMenu = forwardRef<HTMLDivElement | HTMLUListElement, CDropdownMenuProps>(
3725
({ children, className, component: Component = 'ul', ...rest }, ref) => {
3826
const { alignment, dark, dropdownMenuRef, popper, portal, visible } =
@@ -48,7 +36,7 @@ export const CDropdownMenu = forwardRef<HTMLDivElement | HTMLUListElement, CDrop
4836
{
4937
show: visible,
5038
},
51-
alignment && alignmentClassNames(alignment),
39+
alignment && getAlignmentClassNames(alignment),
5240
className,
5341
)}
5442
ref={forkedRef}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export type Directions = 'start' | 'end'
2+
3+
export type Breakpoints =
4+
| { xs: Directions }
5+
| { sm: Directions }
6+
| { md: Directions }
7+
| { lg: Directions }
8+
| { xl: Directions }
9+
| { xxl: Directions }
10+
11+
export type Alignments = Directions | Breakpoints
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { Placement } from '@popperjs/core'
2+
import type { Placements } from '../../types'
3+
import type { Alignments } from './types'
4+
5+
export const getAlignmentClassNames = (alignment: object | string) => {
6+
const classNames: string[] = []
7+
if (typeof alignment === 'object') {
8+
Object.keys(alignment).map((key) => {
9+
classNames.push(`dropdown-menu${key === 'xs' ? '' : `-${key}`}-${alignment[key]}`)
10+
})
11+
}
12+
13+
if (typeof alignment === 'string') {
14+
classNames.push(`dropdown-menu-${alignment}`)
15+
}
16+
17+
return classNames
18+
}
19+
20+
export const getNextActiveElement = (
21+
list: HTMLElement[],
22+
activeElement: HTMLElement,
23+
shouldGetNext: boolean,
24+
isCycleAllowed: boolean,
25+
) => {
26+
const listLength = list.length
27+
let index = list.indexOf(activeElement)
28+
29+
if (index === -1) {
30+
return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
31+
}
32+
33+
index += shouldGetNext ? 1 : -1
34+
35+
if (isCycleAllowed) {
36+
index = (index + listLength) % listLength
37+
}
38+
39+
return list[Math.max(0, Math.min(index, listLength - 1))]
40+
}
41+
42+
export const getPlacement = (
43+
placement: Placement,
44+
direction: string | undefined,
45+
alignment: Alignments | string | undefined,
46+
isRTL: boolean,
47+
): Placements => {
48+
let _placement = placement
49+
50+
if (direction === 'dropup') {
51+
_placement = isRTL ? 'top-end' : 'top-start'
52+
}
53+
54+
if (direction === 'dropup-center') {
55+
_placement = 'top'
56+
}
57+
58+
if (direction === 'dropend') {
59+
_placement = isRTL ? 'left-start' : 'right-start'
60+
}
61+
62+
if (direction === 'dropstart') {
63+
_placement = isRTL ? 'right-start' : 'left-start'
64+
}
65+
66+
if (alignment === 'end') {
67+
_placement = isRTL ? 'bottom-start' : 'bottom-end'
68+
}
69+
70+
return _placement
71+
}

0 commit comments

Comments
 (0)