This repository has been archived by the owner on Aug 21, 2023. It is now read-only.
/
DropList.utils.js
190 lines (161 loc) · 4.96 KB
/
DropList.utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import React, { useEffect } from 'react'
import { isDefined, isObject } from '../../utilities/is'
import { ITEM_TYPES } from './DropList.constants'
import { SelectTag } from './DropList.togglers'
import { ListItemUI, EmptyListUI } from './DropList.css'
// No need to test this helper
/* istanbul ignore next */
export function displayWarnings({
toggler,
withMultipleSelection,
menuCSS,
tippyOptions,
}) {
if (
process.env.NODE_ENV !== 'production' &&
process.env.NODE_ENV !== 'test'
) {
if (!React.isValidElement(toggler)) {
console.info(
'Pass one of the provided togglers or a custom one to the `toggler` prop'
)
}
if (isTogglerOfType(toggler, SelectTag) && withMultipleSelection) {
console.info(
'The Select toggler option should not have withMultipleSelection enabled, it has been disabled for you'
)
}
if (menuCSS != null && tippyOptions.appendTo === undefined) {
console.error(
'menuCSS is only needed when using tippyOptions.appendTo to portal the DropList, please use regular styled components if you need custom styles'
)
}
}
}
export function useWarnings(props) {
useEffect(() => {
displayWarnings(props)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
}
export function isTogglerOfType(toggler, type) {
return React.isValidElement(toggler) && toggler.type === type
}
export function itemToString(item) {
if (item == null) return ''
if (isObject(item)) return item[getItemContentKeyName(item)]
return item
}
export function parseSelectionFromProps({ withMultipleSelection, selection }) {
if (withMultipleSelection) {
return selection != null ? [].concat(selection) : []
}
if (selection != null) {
return selection != null ? selection : null
}
}
export function isItemSelected({ item, selectedItem, selectedItems }) {
if (selectedItem == null && selectedItems.length === 0) return false
if (isObject(item)) {
const itemContentKey = getItemContentKeyName(item)
const itemContent = item[itemContentKey]
if (selectedItem != null && selectedItems.length === 0) {
const selectedItemContentKey = getItemContentKeyName(selectedItem)
const selectedItemContent = selectedItem[selectedItemContentKey]
return selectedItemContent === itemContent
}
return Boolean(
selectedItems.find(item => item[itemContentKey] === itemContent)
)
}
return selectedItem === item || selectedItems.includes(item)
}
export function getItemContentKeyName(item) {
if (objectHasKey(item, 'label')) return 'label'
if (objectHasKey(item, 'value')) return 'value'
return undefined
}
export function objectHasKey(obj, key) {
return isObject(obj) && isDefined(obj[key])
}
export function findItemInArray({ item, arr, key = 'label' }) {
if (item == null) return undefined
return arr.find(i => {
if (isObject(i)) {
return i[key] === item[key]
}
if (isObject(item)) {
return i === item[key]
}
return i === item
})
}
export function removeItemFromArray({ item, arr, key = 'label' }) {
return arr.filter(i => {
if (isObject(i)) {
return i[key] !== item[key]
}
if (isObject(item)) {
return i !== item[key]
}
return i !== item
})
}
export function isItemADivider(item) {
return objectHasKey(item, 'type') && item.type === ITEM_TYPES.DIVIDER
}
export function isItemAGroup(item) {
return objectHasKey(item, 'type') && item.type === ITEM_TYPES.GROUP
}
export function isItemAGroupLabel(item) {
return objectHasKey(item, 'type') && item.type === ITEM_TYPES.GROUP_LABEL
}
export function flattenListItems(listItems) {
return listItems.reduce((accumulator, listItem) => {
const contentKey = getItemContentKeyName(listItem)
if (isItemAGroup(listItem)) {
const itemsInGroup = listItem.items.map(item => ({
...item,
group: listItem[contentKey],
}))
return itemsInGroup.length > 0
? accumulator
.concat({
type: ITEM_TYPES.GROUP_LABEL,
[contentKey]: listItem[contentKey],
})
.concat(itemsInGroup)
: accumulator
}
return accumulator.concat(listItem)
}, [])
}
export function renderListContents({
customEmptyList,
emptyList,
inputValue,
items,
renderListItem,
}) {
if (emptyList && !customEmptyList) return <EmptyListUI>No items</EmptyListUI>
if (emptyList && customEmptyList) {
return React.isValidElement(customEmptyList) ? (
React.cloneElement(customEmptyList)
) : (
<EmptyListUI>No items</EmptyListUI>
)
}
if (items.length > 0) {
return items.map(renderListItem)
}
return <ListItemUI>No results for {inputValue}</ListItemUI>
}
// No need to test this helper
/* istanbul ignore next */
export function requiredItemPropsCheck(props, propName, componentName) {
if (!props.label && !props.value) {
return new Error(
`One of 'label' or 'value' is required by '${componentName}' component.`
)
}
}