@codeleap/web 3.22.2 → 3.23.1
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/package.json +1 -1
- package/src/components/Dropzone/index.tsx +3 -3
- package/src/components/SectionFilters/index.tsx +235 -0
- package/src/components/SectionFilters/styles.ts +16 -0
- package/src/components/SectionFilters/types.ts +68 -0
- package/src/components/components.ts +1 -0
- package/src/components/defaultStyles.ts +2 -0
package/package.json
CHANGED
|
@@ -177,7 +177,7 @@ const DropzoneComponent = (props: DropzoneProps, ref: React.ForwardedRef<Dropzon
|
|
|
177
177
|
open,
|
|
178
178
|
}))
|
|
179
179
|
|
|
180
|
-
const hasFiles = acceptedFiles
|
|
180
|
+
const hasFiles = acceptedFiles?.length > 0 || rejectedFiles?.length > 0
|
|
181
181
|
|
|
182
182
|
const fileProps = {
|
|
183
183
|
fileLeftIcon,
|
|
@@ -199,7 +199,7 @@ const DropzoneComponent = (props: DropzoneProps, ref: React.ForwardedRef<Dropzon
|
|
|
199
199
|
|
|
200
200
|
{hasFiles && (
|
|
201
201
|
<View css={variantStyles.filesWrapper}>
|
|
202
|
-
{acceptedFiles
|
|
202
|
+
{acceptedFiles?.map?.((file, index) => (
|
|
203
203
|
<FilePreview
|
|
204
204
|
{...fileProps}
|
|
205
205
|
index={index}
|
|
@@ -209,7 +209,7 @@ const DropzoneComponent = (props: DropzoneProps, ref: React.ForwardedRef<Dropzon
|
|
|
209
209
|
FilePreviewComponent={FilePreviewComponent}
|
|
210
210
|
/>))}
|
|
211
211
|
|
|
212
|
-
{rejectedFiles
|
|
212
|
+
{rejectedFiles?.map?.(({ file, errors }, index) => (
|
|
213
213
|
<FilePreview
|
|
214
214
|
{...fileProps}
|
|
215
215
|
index={index}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { TypeGuards, getNestedStylesByKey, useCallback, useDefaultComponentStyle, useMemo, useConditionalState } from '@codeleap/common'
|
|
5
|
+
import { SectionFilterPresets } from './styles'
|
|
6
|
+
import { ItemOptionProps, ItemProps, OnPressOptionProps, OptionProps, SectionFiltersProps, SectionFilterFooterProps, onSelectItemProps } from './types'
|
|
7
|
+
import { View } from '../View'
|
|
8
|
+
import { Text } from '../Text'
|
|
9
|
+
import { Button } from '../Button'
|
|
10
|
+
|
|
11
|
+
export * from './styles'
|
|
12
|
+
export * from './types'
|
|
13
|
+
|
|
14
|
+
const ItemOption = (props: OptionProps) => {
|
|
15
|
+
|
|
16
|
+
const {
|
|
17
|
+
option,
|
|
18
|
+
item,
|
|
19
|
+
selectedItems,
|
|
20
|
+
styles,
|
|
21
|
+
onPress,
|
|
22
|
+
canSelectMultiple,
|
|
23
|
+
} = props
|
|
24
|
+
|
|
25
|
+
const isItemSelected = useMemo(() => {
|
|
26
|
+
if (item?.options && selectedItems) {
|
|
27
|
+
if (canSelectMultiple) {
|
|
28
|
+
return TypeGuards.isArray(selectedItems[item?.id]) && selectedItems[item?.id]?.find?.((item) => JSON.stringify(item) === JSON.stringify(option))
|
|
29
|
+
} else {
|
|
30
|
+
return JSON.stringify(option) === JSON.stringify(selectedItems[item?.id])
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
return selectedItems[item?.id]
|
|
34
|
+
}
|
|
35
|
+
}, [item?.options, option, selectedItems, item?.id, canSelectMultiple])
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
<Button
|
|
39
|
+
debugName='Item option'
|
|
40
|
+
text={option?.label}
|
|
41
|
+
onPress={onPress}
|
|
42
|
+
selected={isItemSelected}
|
|
43
|
+
styles={styles}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const SectionFilters = (props: SectionFiltersProps) => {
|
|
49
|
+
|
|
50
|
+
const {
|
|
51
|
+
data,
|
|
52
|
+
onSelectItem,
|
|
53
|
+
renderFooterComponent,
|
|
54
|
+
responsiveVariants,
|
|
55
|
+
variants,
|
|
56
|
+
styles,
|
|
57
|
+
applyFilterButtonProps,
|
|
58
|
+
clearFilterButtonProps,
|
|
59
|
+
applyButtonText,
|
|
60
|
+
clearButtonText,
|
|
61
|
+
filterOnOptionPress,
|
|
62
|
+
} = {
|
|
63
|
+
...SectionFilters.defaultProps,
|
|
64
|
+
...props,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const variantStyles = useDefaultComponentStyle<'u:SectionFilters', typeof SectionFilterPresets>(
|
|
68
|
+
'u:SectionFilters',
|
|
69
|
+
{
|
|
70
|
+
responsiveVariants,
|
|
71
|
+
variants,
|
|
72
|
+
styles,
|
|
73
|
+
rootElement: 'wrapper',
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
const applyButtonStyles = getNestedStylesByKey('applyButton', variantStyles)
|
|
78
|
+
const clearButtonStyles = getNestedStylesByKey('clearButton', variantStyles)
|
|
79
|
+
const itemOptionButtonStyles = getNestedStylesByKey('itemOptionButton', variantStyles)
|
|
80
|
+
|
|
81
|
+
const [_selectedItems, _setSelectedItems] = useConditionalState(props?.selectedItems, props?.setSelectedItems, { initialValue: {}})
|
|
82
|
+
const [_draft, _setDraft] = useConditionalState(props?.draftItems, props?.setDraftItems, { initialValue: {}})
|
|
83
|
+
|
|
84
|
+
const isEmpty = data?.length <= 0
|
|
85
|
+
const shouldDisableActions = Object.keys(_draft)?.length === 0 && Object.keys(_selectedItems)?.length === 0
|
|
86
|
+
|
|
87
|
+
const onPressOption = useCallback((params: OnPressOptionProps) => {
|
|
88
|
+
|
|
89
|
+
const { item, option, canSelectMultiple, hasMultipleOptions } = params
|
|
90
|
+
|
|
91
|
+
_setDraft((state) => {
|
|
92
|
+
|
|
93
|
+
const items = { ...state }
|
|
94
|
+
|
|
95
|
+
const multipleOptionsSelected = TypeGuards.isArray(state?.[item.id]) ? state[item.id] : []
|
|
96
|
+
|
|
97
|
+
let isItemAlreadySelected = null
|
|
98
|
+
|
|
99
|
+
if (canSelectMultiple) {
|
|
100
|
+
isItemAlreadySelected = multipleOptionsSelected?.find((item) => JSON.stringify(item) === JSON.stringify(option))
|
|
101
|
+
} else {
|
|
102
|
+
isItemAlreadySelected = JSON.stringify(items[item?.id]) === JSON.stringify(option)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isItemAlreadySelected) {
|
|
106
|
+
if (hasMultipleOptions) {
|
|
107
|
+
const newChosenOptions = items[item.id]?.filter?.(value => JSON.stringify(value) !== JSON.stringify(option))
|
|
108
|
+
if (newChosenOptions?.length === 0) {
|
|
109
|
+
delete items[item?.id]
|
|
110
|
+
} else {
|
|
111
|
+
items[item.id] = newChosenOptions
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
delete items[item?.id]
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
items[item.id] = canSelectMultiple ? [...multipleOptionsSelected, option] : option
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (filterOnOptionPress) {
|
|
121
|
+
_setSelectedItems(items)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return items
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
onSelectItem?.({ id: item?.id, option })
|
|
128
|
+
|
|
129
|
+
}, [_draft, onSelectItem])
|
|
130
|
+
|
|
131
|
+
const renderItem = useCallback((item: ItemProps) => {
|
|
132
|
+
|
|
133
|
+
const {
|
|
134
|
+
showDescriptionLabel = true,
|
|
135
|
+
} = item
|
|
136
|
+
|
|
137
|
+
const hasMultipleOptions = !!item?.options?.length
|
|
138
|
+
const canSelectMultiple = item?.canSelectMultiple && hasMultipleOptions
|
|
139
|
+
|
|
140
|
+
const description = TypeGuards.isString(item?.descriptionLabel) ? item?.descriptionLabel : item?.label
|
|
141
|
+
|
|
142
|
+
const Option = ({ option }: { option: ItemOptionProps}) => {
|
|
143
|
+
|
|
144
|
+
if (TypeGuards.isNil(item?.id)) {
|
|
145
|
+
return null
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<ItemOption
|
|
150
|
+
option={option}
|
|
151
|
+
item={item}
|
|
152
|
+
selectedItems={_draft}
|
|
153
|
+
styles={itemOptionButtonStyles}
|
|
154
|
+
onPress={() => onPressOption({ option, item, canSelectMultiple, hasMultipleOptions })}
|
|
155
|
+
canSelectMultiple={canSelectMultiple}
|
|
156
|
+
/>
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<View style={variantStyles.optionWrapper}>
|
|
162
|
+
{showDescriptionLabel ? <Text style={variantStyles.label} text={description} /> : null}
|
|
163
|
+
<View style={variantStyles.optionInnerWrapper}>
|
|
164
|
+
{hasMultipleOptions ? (
|
|
165
|
+
item?.options?.map?.((option) => <Option option={option} />)
|
|
166
|
+
) : (
|
|
167
|
+
<Option
|
|
168
|
+
option={{
|
|
169
|
+
label: TypeGuards.isNil(item?.label) ? String(item?.id) : item?.label,
|
|
170
|
+
value: TypeGuards.isNil(item?.label) ? item?.id : item?.label,
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
)}
|
|
174
|
+
</View>
|
|
175
|
+
</View>
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
}, [_draft, variantStyles, itemOptionButtonStyles])
|
|
179
|
+
|
|
180
|
+
const DefaultFooter = ({ onApply, onClear, shouldDisableActions }: SectionFilterFooterProps) => {
|
|
181
|
+
return (
|
|
182
|
+
<View style={variantStyles.footerWrapper}>
|
|
183
|
+
<Button
|
|
184
|
+
styles={applyButtonStyles}
|
|
185
|
+
text={applyButtonText}
|
|
186
|
+
debugName={`Section Filters Footer - Apply items`}
|
|
187
|
+
onPress={onApply}
|
|
188
|
+
disabled={shouldDisableActions}
|
|
189
|
+
{...applyFilterButtonProps}
|
|
190
|
+
/>
|
|
191
|
+
<Button
|
|
192
|
+
styles={clearButtonStyles}
|
|
193
|
+
text={clearButtonText}
|
|
194
|
+
debugName={`Section Filters Footer - Apply items`}
|
|
195
|
+
onPress={onClear}
|
|
196
|
+
disabled={shouldDisableActions}
|
|
197
|
+
{...clearFilterButtonProps}
|
|
198
|
+
/>
|
|
199
|
+
</View>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const onClearItems = () => {
|
|
204
|
+
_setDraft({})
|
|
205
|
+
_setSelectedItems({})
|
|
206
|
+
props?.onClearItems?.()
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const onApplyItems = () => {
|
|
210
|
+
_setSelectedItems(_draft)
|
|
211
|
+
props?.onApplyItems?.(_selectedItems as ItemProps[])
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const Footer = renderFooterComponent || DefaultFooter
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<View style={variantStyles.wrapper}>
|
|
218
|
+
<View style={variantStyles.innerWrapper}>
|
|
219
|
+
{isEmpty ? null : data.map((item) => renderItem(item))}
|
|
220
|
+
</View>
|
|
221
|
+
|
|
222
|
+
<Footer
|
|
223
|
+
onApply={onApplyItems}
|
|
224
|
+
onClear={onClearItems}
|
|
225
|
+
shouldDisableActions={shouldDisableActions}
|
|
226
|
+
/>
|
|
227
|
+
</View>
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
SectionFilters.defaultProps = {
|
|
232
|
+
applyButtonText: 'Filter',
|
|
233
|
+
clearButtonText: 'Clear',
|
|
234
|
+
filterOnOptionPress: false,
|
|
235
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ButtonComposition, createDefaultVariantFactory, includePresets } from '@codeleap/common'
|
|
2
|
+
|
|
3
|
+
export type SectionFiltersComposition =
|
|
4
|
+
'wrapper' |
|
|
5
|
+
'innerWrapper' |
|
|
6
|
+
'label' |
|
|
7
|
+
'optionWrapper' |
|
|
8
|
+
'optionInnerWrapper' |
|
|
9
|
+
`itemOptionButton${Capitalize<ButtonComposition>}` |
|
|
10
|
+
'footerWrapper' |
|
|
11
|
+
`applyButton${Capitalize<ButtonComposition>}` |
|
|
12
|
+
`clearButton${Capitalize<ButtonComposition>}`
|
|
13
|
+
|
|
14
|
+
const createSectionFiltersStyle = createDefaultVariantFactory<SectionFiltersComposition>()
|
|
15
|
+
|
|
16
|
+
export const SectionFilterPresets = includePresets((styles) => createSectionFiltersStyle(() => ({ wrapper: styles })))
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Button, ButtonComposition } from '../Button'
|
|
2
|
+
import { AnyFunction, ComponentVariants, PropsOf, StylesOf } from '@codeleap/common'
|
|
3
|
+
import { SectionFiltersComposition, SectionFilterPresets } from './styles'
|
|
4
|
+
|
|
5
|
+
export type ItemOptionProps = {
|
|
6
|
+
label?: string
|
|
7
|
+
value: string | number
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export type OnPressOptionProps = {
|
|
11
|
+
item: ItemProps
|
|
12
|
+
option: ItemOptionProps
|
|
13
|
+
canSelectMultiple: boolean
|
|
14
|
+
hasMultipleOptions: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ItemProps = {
|
|
18
|
+
id: string | number
|
|
19
|
+
label?: string
|
|
20
|
+
canSelectMultiple?: boolean
|
|
21
|
+
descriptionLabel?: string
|
|
22
|
+
showDescriptionLabel?: boolean
|
|
23
|
+
options?: ItemOptionProps[]
|
|
24
|
+
itemButtonProps?: Omit<PropsOf<typeof Button>, 'debugName'>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type onSelectItemProps = {
|
|
28
|
+
id: ItemProps['id']
|
|
29
|
+
option: ItemOptionProps
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type ApplyFunction = (items?: ItemProps[]) => void
|
|
33
|
+
type ClearFunction = () => void
|
|
34
|
+
|
|
35
|
+
export type SectionFilterFooterProps = {
|
|
36
|
+
onClear: ClearFunction
|
|
37
|
+
onApply: ApplyFunction
|
|
38
|
+
shouldDisableActions: boolean
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type Item = Record<string, any> | Array<any>
|
|
42
|
+
|
|
43
|
+
export type SectionFiltersProps = {
|
|
44
|
+
data?: ItemProps[]
|
|
45
|
+
selectedItems?: Item
|
|
46
|
+
setSelectedItems?: (value: Item) => void
|
|
47
|
+
draftItems?: Item
|
|
48
|
+
setDraftItems?: (value: Item) => void
|
|
49
|
+
onSelectItem?: (item: onSelectItemProps) => void
|
|
50
|
+
onClearItems?: ClearFunction
|
|
51
|
+
onApplyItems?: ApplyFunction
|
|
52
|
+
renderFooterComponent?: (props: SectionFilterFooterProps) => JSX.Element
|
|
53
|
+
applyFilterButtonProps?: Omit<PropsOf<typeof Button>, 'debugName'>
|
|
54
|
+
clearFilterButtonProps?: Omit<PropsOf<typeof Button>, 'debugName'>
|
|
55
|
+
filterOnOptionPress?: boolean
|
|
56
|
+
applyButtonText?: string
|
|
57
|
+
clearButtonText?: string
|
|
58
|
+
styles?: StylesOf<SectionFiltersComposition>
|
|
59
|
+
} & ComponentVariants<typeof SectionFilterPresets>
|
|
60
|
+
|
|
61
|
+
export type OptionProps = {
|
|
62
|
+
option: ItemOptionProps
|
|
63
|
+
item: ItemProps
|
|
64
|
+
styles: Partial<StylesOf<ButtonComposition>>
|
|
65
|
+
selectedItems: object
|
|
66
|
+
onPress: () => void
|
|
67
|
+
canSelectMultiple: boolean
|
|
68
|
+
}
|
|
@@ -28,6 +28,7 @@ import { GridPresets } from './Grid/styles'
|
|
|
28
28
|
import { BadgePresets } from './Badge/styles'
|
|
29
29
|
import { CropPickerPresets } from './CropPicker'
|
|
30
30
|
import { TagPresets } from './Tag/styles'
|
|
31
|
+
import { SectionFilterPresets } from './SectionFilters/styles'
|
|
31
32
|
|
|
32
33
|
export const defaultStyles = {
|
|
33
34
|
View: ViewPresets,
|
|
@@ -63,6 +64,7 @@ export const defaultStyles = {
|
|
|
63
64
|
Dropzone: DropzonePresets,
|
|
64
65
|
CropPicker: CropPickerPresets,
|
|
65
66
|
Tag: TagPresets,
|
|
67
|
+
SectionFilters: SectionFilterPresets,
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
import createCache from '@emotion/cache'
|