@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeleap/web",
3
- "version": "3.22.2",
3
+ "version": "3.23.1",
4
4
  "main": "src/index.ts",
5
5
  "repository": {
6
6
  "url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
@@ -177,7 +177,7 @@ const DropzoneComponent = (props: DropzoneProps, ref: React.ForwardedRef<Dropzon
177
177
  open,
178
178
  }))
179
179
 
180
- const hasFiles = acceptedFiles.length > 0 || rejectedFiles.length > 0
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.map((file, index) => (
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.map(({ file, errors }, index) => (
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
+ }
@@ -37,5 +37,6 @@ export * from './Progress'
37
37
  export * from './Tag'
38
38
  export * from './TextEditor'
39
39
  export * from './ColorPicker'
40
+ export * from './SectionFilters'
40
41
 
41
42
  export * from './defaultStyles'
@@ -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'