@codeleap/web 3.4.0 → 3.6.0
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 +3 -1
- package/src/components/ActionIcon/index.tsx +49 -30
- package/src/components/ActionIcon/styles.ts +8 -5
- package/src/components/ActivityIndicator/index.tsx +6 -5
- package/src/components/Badge/index.tsx +135 -0
- package/src/components/Badge/styles.ts +15 -0
- package/src/components/Button/index.tsx +79 -69
- package/src/components/Button/styles.ts +10 -14
- package/src/components/Checkbox/index.tsx +9 -3
- package/src/components/Collapse/index.tsx +6 -3
- package/src/components/Drawer/index.tsx +72 -44
- package/src/components/Drawer/styles.ts +11 -3
- package/src/components/EmptyPlaceholder/index.tsx +135 -0
- package/src/components/EmptyPlaceholder/styles.ts +16 -0
- package/src/components/Grid/index.tsx +169 -0
- package/src/components/Grid/styles.ts +10 -0
- package/src/components/Grid/types.ts +24 -0
- package/src/components/Icon/index.tsx +7 -4
- package/src/components/InputBase/styles.ts +8 -56
- package/src/components/InputBase/utils.ts +63 -0
- package/src/components/List/ListLayout.tsx +98 -0
- package/src/components/List/PaginationIndicator.tsx +102 -0
- package/src/components/List/index.tsx +104 -91
- package/src/components/List/styles.ts +17 -5
- package/src/components/List/types.ts +51 -0
- package/src/components/List/useInfiniteScroll.ts +113 -0
- package/src/components/LoadingOverlay/index.tsx +5 -3
- package/src/components/Modal/index.tsx +30 -29
- package/src/components/NumberIncrement/index.tsx +3 -2
- package/src/components/Overlay/index.tsx +12 -2
- package/src/components/Pager/index.tsx +5 -1
- package/src/components/RadioInput/index.tsx +4 -3
- package/src/components/SearchInput/index.tsx +93 -0
- package/src/components/SegmentedControl/SegmentedControlOption.tsx +15 -9
- package/src/components/SegmentedControl/index.tsx +12 -5
- package/src/components/Select/index.tsx +18 -14
- package/src/components/Select/styles.ts +4 -3
- package/src/components/Select/types.ts +4 -3
- package/src/components/Slider/index.tsx +3 -2
- package/src/components/Switch/index.tsx +4 -2
- package/src/components/Text/index.tsx +98 -25
- package/src/components/Text/styles.ts +4 -4
- package/src/components/TextInput/index.tsx +6 -15
- package/src/components/Tooltip/index.tsx +163 -131
- package/src/components/Tooltip/styles.ts +13 -9
- package/src/components/Touchable/index.tsx +88 -26
- package/src/components/Touchable/styles.ts +4 -6
- package/src/components/View/index.tsx +1 -1
- package/src/components/components.ts +5 -1
- package/src/components/defaultStyles.ts +10 -3
- package/src/index.ts +3 -0
- package/src/lib/hooks.ts +27 -13
- package/src/lib/index.ts +4 -1
- package/src/lib/useBreakpointMatch.ts +33 -0
- package/src/lib/usePopState.ts +30 -0
- package/src/lib/useSearchParams.ts +54 -0
- package/src/lib/utils/stopPropagation.ts +3 -3
- package/src/types/index.ts +1 -1
- package/src/types/utility.ts +4 -0
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
/** @jsx jsx */
|
|
2
|
-
import { jsx } from '@emotion/react'
|
|
3
|
-
|
|
4
1
|
import {
|
|
5
2
|
AnyFunction,
|
|
6
3
|
ComponentVariants,
|
|
7
4
|
IconPlaceholder,
|
|
8
|
-
onUpdate,
|
|
9
5
|
TypeGuards,
|
|
10
6
|
useDefaultComponentStyle,
|
|
7
|
+
useNestedStylesByKey,
|
|
11
8
|
} from '@codeleap/common'
|
|
12
9
|
import { CSSObject } from '@emotion/react'
|
|
13
|
-
import React
|
|
10
|
+
import React from 'react'
|
|
14
11
|
import { Overlay } from '../Overlay'
|
|
15
12
|
import { View } from '../View'
|
|
16
13
|
import { Text } from '../Text'
|
|
17
|
-
import {
|
|
18
|
-
import { StylesOf } from '../../types/utility'
|
|
14
|
+
import { ComponentCommonProps, StylesOf } from '../../types/utility'
|
|
19
15
|
import { DrawerComposition, DrawerPresets } from './styles'
|
|
20
|
-
import { ActionIcon } from '../ActionIcon'
|
|
16
|
+
import { ActionIcon, ActionIconProps } from '../ActionIcon'
|
|
17
|
+
import { usePopState } from '../../lib'
|
|
21
18
|
|
|
22
19
|
const axisMap = {
|
|
23
20
|
top: [-1, 'Y'],
|
|
@@ -26,19 +23,23 @@ const axisMap = {
|
|
|
26
23
|
right: [1, 'X'],
|
|
27
24
|
} as const
|
|
28
25
|
|
|
29
|
-
export type DrawerProps =
|
|
26
|
+
export type DrawerProps = {
|
|
30
27
|
open: boolean
|
|
31
28
|
toggle: AnyFunction
|
|
32
29
|
darkenBackground?: boolean
|
|
33
|
-
size
|
|
30
|
+
size?: string | number
|
|
34
31
|
showCloseButton?: boolean
|
|
35
|
-
title?: ReactNode
|
|
36
|
-
footer?: ReactNode
|
|
32
|
+
title?: React.ReactNode | string
|
|
33
|
+
footer?: React.ReactNode
|
|
37
34
|
position?: keyof typeof axisMap
|
|
38
35
|
styles?: StylesOf<DrawerComposition>
|
|
36
|
+
style?: React.CSSProperties
|
|
39
37
|
animationDuration?: string
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
closeButtonProps?: Partial<ActionIconProps>
|
|
39
|
+
scrollLocked?: boolean
|
|
40
|
+
children?: React.ReactNode
|
|
41
|
+
} & ComponentVariants<typeof DrawerPresets> & ComponentCommonProps
|
|
42
|
+
|
|
42
43
|
const resolveHiddenDrawerPosition = (
|
|
43
44
|
position: DrawerProps['position'],
|
|
44
45
|
): [string, string, CSSObject] => {
|
|
@@ -59,7 +60,22 @@ const resolveHiddenDrawerPosition = (
|
|
|
59
60
|
return [css, translateAxis, positioning]
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
const defaultProps: Partial<DrawerProps> = {
|
|
64
|
+
animationDuration: '0.3s',
|
|
65
|
+
position: 'bottom',
|
|
66
|
+
showCloseButton: false,
|
|
67
|
+
darkenBackground: true,
|
|
68
|
+
size: '75vw',
|
|
69
|
+
title: null,
|
|
70
|
+
scrollLocked: true,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const Drawer = (props: DrawerProps) => {
|
|
74
|
+
const allProps = {
|
|
75
|
+
...Drawer.defaultProps,
|
|
76
|
+
...props,
|
|
77
|
+
}
|
|
78
|
+
|
|
63
79
|
const {
|
|
64
80
|
open,
|
|
65
81
|
toggle,
|
|
@@ -67,51 +83,58 @@ export const Drawer = ({ ...rawProps }:DrawerProps) => {
|
|
|
67
83
|
size,
|
|
68
84
|
title,
|
|
69
85
|
footer,
|
|
70
|
-
darkenBackground
|
|
86
|
+
darkenBackground,
|
|
71
87
|
showCloseButton,
|
|
72
|
-
|
|
88
|
+
closeButtonProps = {},
|
|
89
|
+
position,
|
|
73
90
|
variants = [],
|
|
74
91
|
responsiveVariants = {},
|
|
75
92
|
styles,
|
|
76
|
-
|
|
77
|
-
|
|
93
|
+
style,
|
|
94
|
+
animationDuration,
|
|
95
|
+
debugName,
|
|
96
|
+
scrollLocked,
|
|
97
|
+
} = allProps as DrawerProps
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
if (open) {
|
|
81
|
-
document.body.style.overflow = 'hidden'
|
|
82
|
-
} else {
|
|
83
|
-
document.body.style.overflow = 'auto'
|
|
84
|
-
}
|
|
85
|
-
}, [open])
|
|
99
|
+
usePopState(open, toggle, scrollLocked)
|
|
86
100
|
|
|
87
|
-
const [hiddenStyle, axis, positioning] =
|
|
88
|
-
resolveHiddenDrawerPosition(position)
|
|
101
|
+
const [hiddenStyle, axis, positioning] = resolveHiddenDrawerPosition(position)
|
|
89
102
|
|
|
90
103
|
const sizeProperty = axis === 'X' ? 'width' : 'height'
|
|
91
104
|
const fullProperty = sizeProperty === 'height' ? 'width' : 'height'
|
|
92
105
|
|
|
93
|
-
const variantStyles = useDefaultComponentStyle('Drawer', {
|
|
106
|
+
const variantStyles = useDefaultComponentStyle<'u:Drawer', typeof DrawerPresets>('u:Drawer', {
|
|
94
107
|
styles,
|
|
95
108
|
variants,
|
|
96
109
|
responsiveVariants,
|
|
97
110
|
})
|
|
98
111
|
|
|
112
|
+
const closeButtonStyles = useNestedStylesByKey('closeButton', variantStyles)
|
|
113
|
+
|
|
114
|
+
const showHeader = (!TypeGuards.isNil(title) || showCloseButton)
|
|
115
|
+
|
|
116
|
+
const wrapperStyles = React.useMemo(() => ([
|
|
117
|
+
variantStyles.wrapper,
|
|
118
|
+
{
|
|
119
|
+
transition: 'visibility 0.01s ease',
|
|
120
|
+
transitionDelay: open ? '0' : animationDuration,
|
|
121
|
+
visibility: open ? 'visible' : 'hidden'
|
|
122
|
+
},
|
|
123
|
+
style,
|
|
124
|
+
]), [open, variantStyles])
|
|
125
|
+
|
|
99
126
|
return (
|
|
100
|
-
<View
|
|
101
|
-
css={{
|
|
102
|
-
...variantStyles.wrapper,
|
|
103
|
-
transition: 'visibility 0.01s ease',
|
|
104
|
-
transitionDelay: open ? '0' : animationDuration,
|
|
105
|
-
visibility: open ? 'visible' : 'hidden',
|
|
106
|
-
}}
|
|
107
|
-
>
|
|
127
|
+
<View debugName={debugName} css={wrapperStyles}>
|
|
108
128
|
{darkenBackground && (
|
|
109
129
|
<Overlay
|
|
130
|
+
debugName={debugName}
|
|
110
131
|
visible={open}
|
|
111
132
|
css={variantStyles.overlay}
|
|
112
|
-
onPress={
|
|
133
|
+
onPress={toggle}
|
|
134
|
+
scrollLocked={scrollLocked}
|
|
113
135
|
/>
|
|
114
136
|
)}
|
|
137
|
+
|
|
115
138
|
<View
|
|
116
139
|
variants={['fixed']}
|
|
117
140
|
css={{
|
|
@@ -125,24 +148,27 @@ export const Drawer = ({ ...rawProps }:DrawerProps) => {
|
|
|
125
148
|
}}
|
|
126
149
|
>
|
|
127
150
|
{
|
|
128
|
-
|
|
151
|
+
showHeader ? (
|
|
129
152
|
<View
|
|
130
153
|
component='header'
|
|
131
|
-
|
|
132
|
-
css={variantStyles.header}
|
|
154
|
+
css={[variantStyles.header]}
|
|
133
155
|
>
|
|
134
|
-
{
|
|
156
|
+
{TypeGuards.isString(title) ? <Text css={variantStyles.title} text={title} /> : title}
|
|
135
157
|
{showCloseButton && (
|
|
136
158
|
<ActionIcon
|
|
159
|
+
debugName={debugName}
|
|
137
160
|
onPress={toggle}
|
|
138
161
|
icon={'close' as IconPlaceholder}
|
|
139
|
-
|
|
162
|
+
{...closeButtonProps}
|
|
163
|
+
styles={closeButtonStyles}
|
|
140
164
|
/>
|
|
141
165
|
)}
|
|
142
166
|
</View>
|
|
143
|
-
)
|
|
167
|
+
) : null
|
|
144
168
|
}
|
|
169
|
+
|
|
145
170
|
<View css={variantStyles.body}>{children}</View>
|
|
171
|
+
|
|
146
172
|
{footer && (
|
|
147
173
|
<View component='footer' css={variantStyles.footer}>
|
|
148
174
|
{footer}
|
|
@@ -153,4 +179,6 @@ export const Drawer = ({ ...rawProps }:DrawerProps) => {
|
|
|
153
179
|
)
|
|
154
180
|
}
|
|
155
181
|
|
|
182
|
+
Drawer.defaultProps = defaultProps
|
|
183
|
+
|
|
156
184
|
export * from './styles'
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { createDefaultVariantFactory, includePresets } from "@codeleap/common"
|
|
2
|
-
import {
|
|
1
|
+
import { createDefaultVariantFactory, includePresets } from "@codeleap/common"
|
|
2
|
+
import { ActionIconComposition } from '../ActionIcon'
|
|
3
3
|
|
|
4
|
-
export type DrawerComposition =
|
|
4
|
+
export type DrawerComposition =
|
|
5
|
+
| 'wrapper'
|
|
6
|
+
| 'overlay'
|
|
7
|
+
| 'header'
|
|
8
|
+
| 'footer'
|
|
9
|
+
| `closeButton${Capitalize<ActionIconComposition>}`
|
|
10
|
+
| 'body'
|
|
11
|
+
| 'box'
|
|
12
|
+
| 'title'
|
|
5
13
|
|
|
6
14
|
const createDrawerStyle = createDefaultVariantFactory<DrawerComposition>()
|
|
7
15
|
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Icon } from '../Icon'
|
|
3
|
+
import { View, ViewProps } from '../View'
|
|
4
|
+
import { Text } from '../Text'
|
|
5
|
+
import { ActivityIndicator, ActivityIndicatorComposition, ActivityIndicatorProps } from '../ActivityIndicator'
|
|
6
|
+
import { EmptyPlaceholderComposition, EmptyPlaceholderPresets } from './styles'
|
|
7
|
+
import {
|
|
8
|
+
ComponentVariants,
|
|
9
|
+
getNestedStylesByKey,
|
|
10
|
+
IconPlaceholder,
|
|
11
|
+
StylesOf,
|
|
12
|
+
TypeGuards,
|
|
13
|
+
useDefaultComponentStyle,
|
|
14
|
+
} from '@codeleap/common'
|
|
15
|
+
import { ComponentCommonProps } from '../../types'
|
|
16
|
+
|
|
17
|
+
export * from './styles'
|
|
18
|
+
|
|
19
|
+
type RenderEmptyProps = {
|
|
20
|
+
emptyText: string | React.ReactElement
|
|
21
|
+
emptyIconName?: IconPlaceholder
|
|
22
|
+
styles: StylesOf<EmptyPlaceholderComposition> & {
|
|
23
|
+
activityIndicatorStyles: StylesOf<ActivityIndicatorComposition>
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type EmptyPlaceholderProps = ComponentVariants<typeof EmptyPlaceholderPresets> & {
|
|
28
|
+
itemName?: string
|
|
29
|
+
title?: React.ReactElement | string
|
|
30
|
+
description?: React.ReactElement | string
|
|
31
|
+
icon?: IconPlaceholder | ((props: EmptyPlaceholderProps) => JSX.Element) | null
|
|
32
|
+
loading?: boolean
|
|
33
|
+
styles?: StylesOf<EmptyPlaceholderComposition>
|
|
34
|
+
style?: React.CSSProperties
|
|
35
|
+
renderEmpty?: (props: RenderEmptyProps) => React.ReactElement
|
|
36
|
+
wrapperProps?: Partial<typeof View>
|
|
37
|
+
imageWrapperProps?: Partial<typeof View>
|
|
38
|
+
indicatorProps?: Partial<ActivityIndicatorProps>
|
|
39
|
+
} & ComponentCommonProps
|
|
40
|
+
|
|
41
|
+
const defaultProps: Partial<EmptyPlaceholderProps> = {}
|
|
42
|
+
|
|
43
|
+
export const EmptyPlaceholder = (props: EmptyPlaceholderProps) => {
|
|
44
|
+
const allProps = {
|
|
45
|
+
...EmptyPlaceholder.defaultProps,
|
|
46
|
+
...props,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
itemName,
|
|
51
|
+
title,
|
|
52
|
+
loading,
|
|
53
|
+
description,
|
|
54
|
+
styles = {},
|
|
55
|
+
style,
|
|
56
|
+
variants = [],
|
|
57
|
+
responsiveVariants = {},
|
|
58
|
+
icon: IconEmpty = null,
|
|
59
|
+
renderEmpty: RenderEmpty = null,
|
|
60
|
+
wrapperProps = {},
|
|
61
|
+
imageWrapperProps = {},
|
|
62
|
+
indicatorProps = {},
|
|
63
|
+
debugName,
|
|
64
|
+
} = allProps
|
|
65
|
+
|
|
66
|
+
const emptyText = title || (itemName && `No ${itemName} found.`) || 'No items.'
|
|
67
|
+
|
|
68
|
+
const variantStyles = useDefaultComponentStyle<'u:EmptyPlaceholder', typeof EmptyPlaceholderPresets>('u:EmptyPlaceholder', {
|
|
69
|
+
variants,
|
|
70
|
+
responsiveVariants,
|
|
71
|
+
styles,
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const activityIndicatorStyles = React.useMemo(() => {
|
|
75
|
+
return getNestedStylesByKey('loader', variantStyles)
|
|
76
|
+
}, [variantStyles])
|
|
77
|
+
|
|
78
|
+
if (loading) {
|
|
79
|
+
return (
|
|
80
|
+
<View css={[variantStyles.wrapper, variantStyles['wrapper:loading']]}>
|
|
81
|
+
<ActivityIndicator debugName={debugName} {...indicatorProps} styles={activityIndicatorStyles}/>
|
|
82
|
+
</View>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (!TypeGuards.isNil(RenderEmpty)) {
|
|
87
|
+
const _emptyProps = {
|
|
88
|
+
emptyText,
|
|
89
|
+
emptyIconName: IconEmpty as IconPlaceholder,
|
|
90
|
+
styles: {
|
|
91
|
+
...variantStyles,
|
|
92
|
+
activityIndicatorStyles,
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<View {...wrapperProps} css={[variantStyles.wrapper, style]}>
|
|
98
|
+
<RenderEmpty {..._emptyProps}/>
|
|
99
|
+
</View>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const _Image = React.useMemo(() => {
|
|
104
|
+
if (TypeGuards.isNil(IconEmpty)) return null
|
|
105
|
+
|
|
106
|
+
if (TypeGuards.isString(IconEmpty)) {
|
|
107
|
+
return <Icon debugName={debugName} name={IconEmpty as IconPlaceholder} forceStyle={variantStyles.icon} />
|
|
108
|
+
} else if (React.isValidElement(IconEmpty)) {
|
|
109
|
+
// @ts-ignore
|
|
110
|
+
return <IconEmpty {...props} />
|
|
111
|
+
} else {
|
|
112
|
+
return <IconEmpty {...props} />
|
|
113
|
+
}
|
|
114
|
+
}, [])
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<View {...wrapperProps} css={[variantStyles.wrapper, style]}>
|
|
118
|
+
<View {...imageWrapperProps} css={variantStyles.imageWrapper}>
|
|
119
|
+
{_Image}
|
|
120
|
+
</View>
|
|
121
|
+
|
|
122
|
+
{React.isValidElement(emptyText)
|
|
123
|
+
? emptyText
|
|
124
|
+
: <Text debugName={debugName} text={emptyText} css={variantStyles.title}/>
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
{React.isValidElement(description)
|
|
128
|
+
? description
|
|
129
|
+
: TypeGuards.isString(description) && <Text debugName={debugName} text={description} css={variantStyles.description}/>
|
|
130
|
+
}
|
|
131
|
+
</View>
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
EmptyPlaceholder.defaultProps = defaultProps
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
|
|
2
|
+
import { ActivityIndicatorComposition } from '../ActivityIndicator'
|
|
3
|
+
|
|
4
|
+
export type EmptyPlaceholderComposition =
|
|
5
|
+
| 'wrapper:loading'
|
|
6
|
+
| `loader${Capitalize<ActivityIndicatorComposition>}`
|
|
7
|
+
| 'wrapper'
|
|
8
|
+
| 'title'
|
|
9
|
+
| 'description'
|
|
10
|
+
| 'image'
|
|
11
|
+
| 'imageWrapper'
|
|
12
|
+
| 'icon'
|
|
13
|
+
|
|
14
|
+
const createEmptyPlaceholderStyle = createDefaultVariantFactory<EmptyPlaceholderComposition>()
|
|
15
|
+
|
|
16
|
+
export const EmptyPlaceholderPresets = includePresets((styles) => createEmptyPlaceholderStyle(() => ({ wrapper: styles })))
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { useDefaultComponentStyle, useCallback } from '@codeleap/common'
|
|
3
|
+
import { View, ViewProps } from '../View'
|
|
4
|
+
import { EmptyPlaceholder } from '../EmptyPlaceholder'
|
|
5
|
+
import { GridPresets } from './styles'
|
|
6
|
+
import { useVirtualizer, VirtualItem } from '@tanstack/react-virtual'
|
|
7
|
+
import { GridProps } from './types'
|
|
8
|
+
import { ListLayout, useInfiniteScroll } from '../List'
|
|
9
|
+
|
|
10
|
+
export * from './styles'
|
|
11
|
+
export * from './types'
|
|
12
|
+
|
|
13
|
+
const generateColumns = (count: number) => {
|
|
14
|
+
return new Array(count).fill(0).map((_, i) => {
|
|
15
|
+
const key: string = i.toString()
|
|
16
|
+
return {
|
|
17
|
+
key,
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const RenderSeparator = (props: { separatorStyles: ViewProps<'div'>['css'] }) => {
|
|
23
|
+
return (
|
|
24
|
+
<View css={[props?.separatorStyles]}></View>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultProps: Partial<GridProps> = {
|
|
29
|
+
ListFooterComponent: null,
|
|
30
|
+
ListHeaderComponent: null,
|
|
31
|
+
ListLoadingIndicatorComponent: null,
|
|
32
|
+
ListRefreshControlComponent: null,
|
|
33
|
+
ListEmptyComponent: EmptyPlaceholder,
|
|
34
|
+
ListSeparatorComponent: RenderSeparator,
|
|
35
|
+
refreshDebounce: 3000,
|
|
36
|
+
refreshSize: 40,
|
|
37
|
+
refreshThreshold: 1,
|
|
38
|
+
refreshPosition: 2,
|
|
39
|
+
refresh: true,
|
|
40
|
+
numColumns: 2,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const GridCP = React.forwardRef<'div', GridProps>((flatGridProps, ref) => {
|
|
44
|
+
const allProps = {
|
|
45
|
+
...defaultProps,
|
|
46
|
+
...flatGridProps,
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {
|
|
50
|
+
variants = [],
|
|
51
|
+
responsiveVariants = {},
|
|
52
|
+
styles = {},
|
|
53
|
+
renderItem: RenderItem,
|
|
54
|
+
data,
|
|
55
|
+
ListLoadingIndicatorComponent,
|
|
56
|
+
ListSeparatorComponent,
|
|
57
|
+
virtualizerOptions = {},
|
|
58
|
+
numColumns,
|
|
59
|
+
separators,
|
|
60
|
+
} = allProps
|
|
61
|
+
|
|
62
|
+
const variantStyles = useDefaultComponentStyle<'u:Grid', typeof GridPresets>('u:Grid', {
|
|
63
|
+
variants,
|
|
64
|
+
responsiveVariants,
|
|
65
|
+
styles,
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const separator = separators && <ListSeparatorComponent separatorStyles={variantStyles.separator} />
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
dataVirtualizer,
|
|
72
|
+
parentRef,
|
|
73
|
+
items,
|
|
74
|
+
layoutProps,
|
|
75
|
+
} = useInfiniteScroll({
|
|
76
|
+
...allProps,
|
|
77
|
+
overscan: 5,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const columns = React.useMemo(() => {
|
|
81
|
+
return generateColumns(numColumns)
|
|
82
|
+
}, [numColumns])
|
|
83
|
+
|
|
84
|
+
const columnVirtualizer = useVirtualizer({
|
|
85
|
+
horizontal: true,
|
|
86
|
+
count: columns?.length,
|
|
87
|
+
getScrollElement: () => parentRef.current,
|
|
88
|
+
estimateSize: () => null,
|
|
89
|
+
overscan: 5,
|
|
90
|
+
...virtualizerOptions,
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const columnItems = columnVirtualizer.getVirtualItems()
|
|
94
|
+
|
|
95
|
+
const renderItem = useCallback((_item: VirtualItem) => {
|
|
96
|
+
if (!RenderItem) return null
|
|
97
|
+
|
|
98
|
+
const showIndicator = (_item?.index === (data?.length / numColumns)) && !!ListLoadingIndicatorComponent
|
|
99
|
+
|
|
100
|
+
const gridLength = data?.length || 0
|
|
101
|
+
|
|
102
|
+
return <>
|
|
103
|
+
{/* Necessary for correct list render */}
|
|
104
|
+
<div
|
|
105
|
+
css={[
|
|
106
|
+
variantStyles.itemWrapper,
|
|
107
|
+
{ transform: `translateY(${_item?.start - dataVirtualizer?.options?.scrollMargin}px)` }
|
|
108
|
+
]}
|
|
109
|
+
key={_item?.key}
|
|
110
|
+
data-index={_item?.index}
|
|
111
|
+
ref={dataVirtualizer?.measureElement}
|
|
112
|
+
>
|
|
113
|
+
{_item?.index !== 0 ? separator : null}
|
|
114
|
+
|
|
115
|
+
<View css={[variantStyles.column]}>
|
|
116
|
+
{columnItems.map(column => {
|
|
117
|
+
const rowIndex = _item?.index
|
|
118
|
+
const columnIndex = column?.index
|
|
119
|
+
const itemIndex = (rowIndex * numColumns) + columnIndex
|
|
120
|
+
|
|
121
|
+
const isFirst = itemIndex === 0
|
|
122
|
+
const isLast = itemIndex === gridLength - 1
|
|
123
|
+
const isOnly = isFirst && isLast
|
|
124
|
+
|
|
125
|
+
const isLastInRow = columnIndex === numColumns
|
|
126
|
+
const isFirstInRow = columnIndex === 0
|
|
127
|
+
const isOnlyInRow = isFirstInRow && isLastInRow
|
|
128
|
+
|
|
129
|
+
const _itemProps = {
|
|
130
|
+
..._item,
|
|
131
|
+
key: itemIndex,
|
|
132
|
+
index: itemIndex,
|
|
133
|
+
isOnly,
|
|
134
|
+
isLast,
|
|
135
|
+
isFirst,
|
|
136
|
+
column,
|
|
137
|
+
isFirstInRow,
|
|
138
|
+
isLastInRow,
|
|
139
|
+
isOnlyInRow,
|
|
140
|
+
rowIndex,
|
|
141
|
+
item: data?.[itemIndex]
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!_itemProps?.item) return null
|
|
145
|
+
|
|
146
|
+
return <RenderItem {..._itemProps} />
|
|
147
|
+
})}
|
|
148
|
+
</View>
|
|
149
|
+
|
|
150
|
+
{showIndicator && <ListLoadingIndicatorComponent />}
|
|
151
|
+
</div>
|
|
152
|
+
</>
|
|
153
|
+
}, [RenderItem, data?.length, dataVirtualizer?.measureElement])
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<ListLayout
|
|
157
|
+
{...allProps}
|
|
158
|
+
{...layoutProps}
|
|
159
|
+
variantStyles={variantStyles} // @ts-ignore
|
|
160
|
+
ref={ref}
|
|
161
|
+
>
|
|
162
|
+
{items?.map((item) => renderItem(item))}
|
|
163
|
+
</ListLayout>
|
|
164
|
+
)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
export type GridComponentType = <T extends any[] = any[]>(props: GridProps<T>) => React.ReactElement
|
|
168
|
+
|
|
169
|
+
export const Grid = GridCP as unknown as GridComponentType
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createDefaultVariantFactory, includePresets } from '@codeleap/common'
|
|
2
|
+
import { ListComposition, ListParts } from '../List'
|
|
3
|
+
|
|
4
|
+
export type GridParts = ListParts | 'column'
|
|
5
|
+
|
|
6
|
+
export type GridComposition = ListComposition | GridParts
|
|
7
|
+
|
|
8
|
+
const createGridStyle = createDefaultVariantFactory<GridComposition>()
|
|
9
|
+
|
|
10
|
+
export const GridPresets = includePresets(style => createGridStyle(() => ({ content: style })))
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ComponentVariants, StylesOf } from '@codeleap/common'
|
|
2
|
+
import { VirtualItem } from '@tanstack/react-virtual'
|
|
3
|
+
import { ComponentCommonProps } from '../../types'
|
|
4
|
+
import { AugmentedRenderItemInfo, ListProps } from '../List'
|
|
5
|
+
import { GridComposition, GridPresets } from './styles'
|
|
6
|
+
|
|
7
|
+
type GridAugmentedRenderItemInfo<T> = AugmentedRenderItemInfo<T> & {
|
|
8
|
+
column: VirtualItem
|
|
9
|
+
isLastInRow: boolean
|
|
10
|
+
isOnlyInRow: boolean
|
|
11
|
+
isFirstInRow: boolean
|
|
12
|
+
rowIndex: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type GridProps<
|
|
16
|
+
T = any[],
|
|
17
|
+
Data = T extends Array<infer D> ? D : never
|
|
18
|
+
> =
|
|
19
|
+
Omit<ListProps<T, Data>, 'variants' | 'styles' | 'renderItem'> &
|
|
20
|
+
ComponentVariants<typeof GridPresets> & {
|
|
21
|
+
styles?: StylesOf<GridComposition>
|
|
22
|
+
numColumns?: number
|
|
23
|
+
renderItem: (data: GridAugmentedRenderItemInfo<T>) => React.ReactElement
|
|
24
|
+
} & ComponentCommonProps
|
|
@@ -9,15 +9,18 @@ import {
|
|
|
9
9
|
useCodeleapContext,
|
|
10
10
|
} from '@codeleap/common'
|
|
11
11
|
import { View } from '../View'
|
|
12
|
+
import { ComponentCommonProps } from '../../types'
|
|
12
13
|
|
|
13
14
|
export * from './styles'
|
|
14
15
|
|
|
15
|
-
export type IconProps = {
|
|
16
|
+
export type IconProps = ComponentCommonProps & {
|
|
16
17
|
name: IconPlaceholder
|
|
17
|
-
style?:
|
|
18
|
+
style?: React.CSSProperties
|
|
19
|
+
size?: string | number
|
|
20
|
+
color?: string
|
|
18
21
|
renderEmptySpace?: boolean
|
|
19
22
|
forceStyle?: CSSObject | CSSInterpolation | React.CSSProperties
|
|
20
|
-
css?:
|
|
23
|
+
css?: CSSInterpolation | CSSInterpolation[]
|
|
21
24
|
} & ComponentVariants<typeof IconStyles>
|
|
22
25
|
|
|
23
26
|
const IconCP = ({ name, style, variants, renderEmptySpace, ...otherProps }:IconProps) => {
|
|
@@ -48,7 +51,7 @@ const IconCP = ({ name, style, variants, renderEmptySpace, ...otherProps }:IconP
|
|
|
48
51
|
)
|
|
49
52
|
return null
|
|
50
53
|
}
|
|
51
|
-
return <Component
|
|
54
|
+
return <Component style={variantStyles.icon} {...otherProps}/>
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
export const Icon = IconCP as ((props: IconProps) => jsx.JSX.Element)
|