@codeleap/web 3.23.0 → 3.23.2

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