@codeleap/web 3.23.0 → 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.23.0",
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",
@@ -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'