@codeleap/web 3.4.0 → 3.5.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 +2 -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
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { onUpdate } from '@codeleap/common'
|
|
2
|
+
import { useVirtualizer, VirtualItem, Virtualizer, VirtualizerOptions } from '@tanstack/react-virtual'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { ListProps } from '.'
|
|
5
|
+
import { GridProps } from '../Grid'
|
|
6
|
+
|
|
7
|
+
export type UseInfiniteScrollProps<TS extends Element = any, T extends Element = any> =
|
|
8
|
+
ListProps &
|
|
9
|
+
GridProps &
|
|
10
|
+
Pick<VirtualizerOptions<TS, T>, 'overscan'>
|
|
11
|
+
|
|
12
|
+
export type UseInfiniteScrollReturn<TS extends Element = any, T extends Element = any> = {
|
|
13
|
+
dataVirtualizer: Virtualizer<TS, T>
|
|
14
|
+
count: number
|
|
15
|
+
items: VirtualItem[]
|
|
16
|
+
isRefresh: boolean
|
|
17
|
+
parentRef: React.MutableRefObject<undefined>
|
|
18
|
+
layoutProps: {
|
|
19
|
+
isEmpty: boolean
|
|
20
|
+
refreshing: boolean
|
|
21
|
+
parentRef: React.MutableRefObject<undefined>
|
|
22
|
+
dataVirtualizer: Virtualizer<TS, T>
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const useInfiniteScroll = (props: UseInfiniteScrollProps): UseInfiniteScrollReturn => {
|
|
27
|
+
const {
|
|
28
|
+
onRefresh,
|
|
29
|
+
data,
|
|
30
|
+
hasNextPage,
|
|
31
|
+
isFetchingNextPage,
|
|
32
|
+
fetchNextPage,
|
|
33
|
+
virtualizerOptions = {},
|
|
34
|
+
refreshDebounce,
|
|
35
|
+
refreshThreshold,
|
|
36
|
+
overscan = 10,
|
|
37
|
+
numColumns = 1,
|
|
38
|
+
} = props
|
|
39
|
+
|
|
40
|
+
const parentRef = React.useRef()
|
|
41
|
+
|
|
42
|
+
const [refreshing, setRefreshing] = React.useState(false)
|
|
43
|
+
|
|
44
|
+
const count = hasNextPage ? data?.length + 1 : data?.length
|
|
45
|
+
|
|
46
|
+
const dataVirtualizer = useVirtualizer({
|
|
47
|
+
count,
|
|
48
|
+
getScrollElement: () => parentRef.current,
|
|
49
|
+
estimateSize: () => null,
|
|
50
|
+
overscan,
|
|
51
|
+
...virtualizerOptions,
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const isRefresh = React.useMemo(() => {
|
|
55
|
+
const _offset = dataVirtualizer?.scrollOffset
|
|
56
|
+
const _refresh = _offset <= refreshThreshold && dataVirtualizer?.isScrolling
|
|
57
|
+
|
|
58
|
+
return _refresh
|
|
59
|
+
}, [dataVirtualizer?.scrollOffset, dataVirtualizer?.isScrolling])
|
|
60
|
+
|
|
61
|
+
const isEmpty = !data || !data?.length
|
|
62
|
+
|
|
63
|
+
const items = dataVirtualizer?.getVirtualItems()
|
|
64
|
+
|
|
65
|
+
onUpdate(() => {
|
|
66
|
+
if (isRefresh) {
|
|
67
|
+
setRefreshing(true)
|
|
68
|
+
onRefresh?.()
|
|
69
|
+
|
|
70
|
+
setTimeout(() => {
|
|
71
|
+
setRefreshing(false)
|
|
72
|
+
}, refreshDebounce)
|
|
73
|
+
}
|
|
74
|
+
}, [!!isRefresh])
|
|
75
|
+
|
|
76
|
+
onUpdate(() => {
|
|
77
|
+
const [lastItem] = [...(items ?? [])]?.reverse()
|
|
78
|
+
|
|
79
|
+
if (!lastItem) {
|
|
80
|
+
return
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const itemsLength = (data?.length / numColumns) - 1
|
|
84
|
+
|
|
85
|
+
if (
|
|
86
|
+
lastItem?.index >= itemsLength &&
|
|
87
|
+
hasNextPage &&
|
|
88
|
+
!isFetchingNextPage
|
|
89
|
+
) {
|
|
90
|
+
fetchNextPage?.()
|
|
91
|
+
}
|
|
92
|
+
}, [
|
|
93
|
+
hasNextPage,
|
|
94
|
+
fetchNextPage,
|
|
95
|
+
data?.length,
|
|
96
|
+
isFetchingNextPage,
|
|
97
|
+
items,
|
|
98
|
+
])
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
items,
|
|
102
|
+
dataVirtualizer,
|
|
103
|
+
isRefresh,
|
|
104
|
+
count,
|
|
105
|
+
parentRef,
|
|
106
|
+
layoutProps: {
|
|
107
|
+
parentRef,
|
|
108
|
+
refreshing,
|
|
109
|
+
isEmpty,
|
|
110
|
+
dataVirtualizer
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -4,6 +4,7 @@ import { StylesOf } from '../..'
|
|
|
4
4
|
import { LoadingOverlayComposition, LoadingOverlayPresets } from './styles'
|
|
5
5
|
import { View, ViewProps } from '../View'
|
|
6
6
|
import { ActivityIndicator, ActivityIndicatorProps } from '../ActivityIndicator'
|
|
7
|
+
import { ComponentCommonProps } from '../../types/utility'
|
|
7
8
|
|
|
8
9
|
export type LoadingOverlayProps = Partial<ViewProps<'div'>> & {
|
|
9
10
|
visible?: boolean
|
|
@@ -11,7 +12,7 @@ export type LoadingOverlayProps = Partial<ViewProps<'div'>> & {
|
|
|
11
12
|
style?: React.CSSProperties
|
|
12
13
|
indicatorProps?: ActivityIndicatorProps
|
|
13
14
|
children?: React.ReactNode
|
|
14
|
-
} & ComponentVariants<typeof LoadingOverlayPresets>
|
|
15
|
+
} & ComponentVariants<typeof LoadingOverlayPresets> & ComponentCommonProps
|
|
15
16
|
|
|
16
17
|
export const LoadingOverlay = (props: LoadingOverlayProps) => {
|
|
17
18
|
const {
|
|
@@ -21,7 +22,8 @@ export const LoadingOverlay = (props: LoadingOverlayProps) => {
|
|
|
21
22
|
variants = [],
|
|
22
23
|
responsiveVariants = {},
|
|
23
24
|
style = {},
|
|
24
|
-
indicatorProps,
|
|
25
|
+
indicatorProps,
|
|
26
|
+
debugName,
|
|
25
27
|
...rest
|
|
26
28
|
} = props
|
|
27
29
|
|
|
@@ -38,7 +40,7 @@ export const LoadingOverlay = (props: LoadingOverlayProps) => {
|
|
|
38
40
|
|
|
39
41
|
return (
|
|
40
42
|
<View css={[variantStyles.wrapper, visible && variantStyles['wrapper:visible'], style]} {...rest}>
|
|
41
|
-
<ActivityIndicator {...indicatorProps} styles={indicatorStyles} />
|
|
43
|
+
<ActivityIndicator debugName={debugName} {...indicatorProps} styles={indicatorStyles} />
|
|
42
44
|
{children}
|
|
43
45
|
</View>
|
|
44
46
|
)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** @jsx jsx */
|
|
2
|
+
import { jsx, CSSObject } from '@emotion/react'
|
|
1
3
|
import {
|
|
2
4
|
AnyFunction,
|
|
3
5
|
ComponentVariants,
|
|
@@ -22,6 +24,8 @@ import { Overlay, OverlayProps } from '../Overlay'
|
|
|
22
24
|
import { ModalComposition, ModalPresets } from './styles'
|
|
23
25
|
import { ActionIcon, ActionIconProps } from '../ActionIcon'
|
|
24
26
|
import { Scroll } from '../Scroll'
|
|
27
|
+
import { usePopState } from '../../lib/usePopState'
|
|
28
|
+
import { ComponentCommonProps } from '../../types'
|
|
25
29
|
|
|
26
30
|
export * from './styles'
|
|
27
31
|
|
|
@@ -52,8 +56,9 @@ export type ModalProps =
|
|
|
52
56
|
onClose?: () => void
|
|
53
57
|
overlayProps?: Partial<OverlayProps>
|
|
54
58
|
zIndex?: number
|
|
55
|
-
|
|
56
|
-
|
|
59
|
+
withScrollContainer?: boolean
|
|
60
|
+
scrollLocked?: boolean
|
|
61
|
+
} & ComponentVariants<typeof ModalPresets> & ComponentCommonProps
|
|
57
62
|
|
|
58
63
|
function focusModal(event: FocusEvent, id: string) {
|
|
59
64
|
event.preventDefault()
|
|
@@ -83,6 +88,7 @@ const ModalDefaultHeader = (props: ModalHeaderProps) => {
|
|
|
83
88
|
onPressClose,
|
|
84
89
|
closeButtonProps = {},
|
|
85
90
|
description,
|
|
91
|
+
debugName,
|
|
86
92
|
} = props
|
|
87
93
|
|
|
88
94
|
const closeButtonStyles = useNestedStylesByKey('closeButton', variantStyles)
|
|
@@ -102,13 +108,14 @@ const ModalDefaultHeader = (props: ModalHeaderProps) => {
|
|
|
102
108
|
>
|
|
103
109
|
<View id={`${id}-title`} css={variantStyles.titleWrapper}>
|
|
104
110
|
{TypeGuards.isString(title) ? (
|
|
105
|
-
<Text text={title} css={variantStyles.title} />
|
|
111
|
+
<Text debugName={debugName} text={title} css={variantStyles.title} />
|
|
106
112
|
) : (
|
|
107
113
|
title
|
|
108
114
|
)}
|
|
109
115
|
|
|
110
116
|
{showCloseButton && (
|
|
111
117
|
<ActionIcon
|
|
118
|
+
debugName={debugName}
|
|
112
119
|
icon={closeIconName as IconPlaceholder}
|
|
113
120
|
onPress={onPressClose}
|
|
114
121
|
{...closeButtonProps}
|
|
@@ -118,7 +125,7 @@ const ModalDefaultHeader = (props: ModalHeaderProps) => {
|
|
|
118
125
|
</View>
|
|
119
126
|
|
|
120
127
|
{TypeGuards.isString(description) ? (
|
|
121
|
-
<Text text={description} style={variantStyles.description} />
|
|
128
|
+
<Text debugName={debugName} text={description} style={variantStyles.description} />
|
|
122
129
|
) : (
|
|
123
130
|
description
|
|
124
131
|
)}
|
|
@@ -139,11 +146,12 @@ const defaultProps: Partial<ModalProps> = {
|
|
|
139
146
|
dismissOnBackdrop: true,
|
|
140
147
|
zIndex: null,
|
|
141
148
|
description: null,
|
|
142
|
-
|
|
149
|
+
withScrollContainer: false,
|
|
150
|
+
scrollLocked: true,
|
|
143
151
|
}
|
|
144
152
|
|
|
145
|
-
export const ModalContent
|
|
146
|
-
modalProps,
|
|
153
|
+
export const ModalContent = (
|
|
154
|
+
modalProps: ModalProps & { id: string },
|
|
147
155
|
) => {
|
|
148
156
|
const {
|
|
149
157
|
children,
|
|
@@ -167,7 +175,9 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
167
175
|
overlayProps = {},
|
|
168
176
|
dismissOnBackdrop,
|
|
169
177
|
zIndex,
|
|
170
|
-
|
|
178
|
+
withScrollContainer,
|
|
179
|
+
debugName,
|
|
180
|
+
scrollLocked,
|
|
171
181
|
...props
|
|
172
182
|
} = modalProps
|
|
173
183
|
|
|
@@ -186,20 +196,7 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
186
196
|
if (TypeGuards.isFunction(onClose)) onClose()
|
|
187
197
|
}
|
|
188
198
|
|
|
189
|
-
|
|
190
|
-
if (visible) {
|
|
191
|
-
document.body.style.overflow = 'hidden'
|
|
192
|
-
window.history.pushState(null, null, window.location.pathname)
|
|
193
|
-
window.addEventListener('popstate', toggle)
|
|
194
|
-
} else {
|
|
195
|
-
document.body.style.overflow = 'auto'
|
|
196
|
-
window.removeEventListener('popstate', toggle)
|
|
197
|
-
}
|
|
198
|
-
}, [visible])
|
|
199
|
-
|
|
200
|
-
useUnmount(() => {
|
|
201
|
-
window.removeEventListener('popstate', toggle)
|
|
202
|
-
})
|
|
199
|
+
usePopState(visible, toggle, scrollLocked)
|
|
203
200
|
|
|
204
201
|
function closeOnEscPress(e: React.KeyboardEvent<HTMLDivElement>) {
|
|
205
202
|
if (!closeOnEscape) return null
|
|
@@ -214,18 +211,18 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
214
211
|
if (modal) modal.focus()
|
|
215
212
|
}, [id])
|
|
216
213
|
|
|
217
|
-
const close = (closable && dismissOnBackdrop) ? toggleAndReturn : () => {}
|
|
214
|
+
const close = (closable && dismissOnBackdrop) ? toggleAndReturn : () => { }
|
|
218
215
|
|
|
219
216
|
const ModalBody = renderModalBody || (scroll ? Scroll : View)
|
|
220
217
|
|
|
221
|
-
const ModalArea =
|
|
218
|
+
const ModalArea = withScrollContainer ? Scroll : View
|
|
222
219
|
|
|
223
220
|
const _zIndex = React.useMemo(() => {
|
|
224
221
|
return TypeGuards.isNumber(zIndex) ? { zIndex } : {}
|
|
225
222
|
}, [zIndex])
|
|
226
223
|
|
|
227
224
|
return (
|
|
228
|
-
<
|
|
225
|
+
<View
|
|
229
226
|
aria-hidden={!visible}
|
|
230
227
|
css={[
|
|
231
228
|
variantStyles.wrapper,
|
|
@@ -236,6 +233,7 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
236
233
|
]}
|
|
237
234
|
>
|
|
238
235
|
<Overlay
|
|
236
|
+
debugName={debugName}
|
|
239
237
|
visible={withOverlay ? visible : false}
|
|
240
238
|
css={[
|
|
241
239
|
variantStyles.backdrop,
|
|
@@ -243,10 +241,11 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
243
241
|
? variantStyles['backdrop:visible']
|
|
244
242
|
: variantStyles['backdrop:hidden'],
|
|
245
243
|
]}
|
|
244
|
+
scrollLocked={scrollLocked}
|
|
246
245
|
{...overlayProps}
|
|
247
246
|
/>
|
|
248
247
|
|
|
249
|
-
<
|
|
248
|
+
<ModalArea css={variantStyles.innerWrapper}>
|
|
250
249
|
<View css={variantStyles.backdropPressable} onClick={close} />
|
|
251
250
|
<View
|
|
252
251
|
component='section'
|
|
@@ -272,6 +271,7 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
272
271
|
variantStyles={variantStyles}
|
|
273
272
|
id={id}
|
|
274
273
|
onPressClose={toggleAndReturn}
|
|
274
|
+
debugName={debugName}
|
|
275
275
|
/>
|
|
276
276
|
|
|
277
277
|
<ModalBody
|
|
@@ -288,12 +288,12 @@ export const ModalContent: React.FC<ModalProps & { id: string }> = (
|
|
|
288
288
|
</View>
|
|
289
289
|
)}
|
|
290
290
|
</View>
|
|
291
|
-
</
|
|
292
|
-
</
|
|
291
|
+
</ModalArea>
|
|
292
|
+
</View>
|
|
293
293
|
)
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
export const Modal
|
|
296
|
+
export const Modal = (props) => {
|
|
297
297
|
const allProps = {
|
|
298
298
|
...Modal.defaultProps,
|
|
299
299
|
...props,
|
|
@@ -359,3 +359,4 @@ export const Modal: React.FC<ModalProps> = (props) => {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
Modal.defaultProps = defaultProps
|
|
362
|
+
|
|
@@ -34,7 +34,6 @@ export type NumberIncrementProps = Pick<
|
|
|
34
34
|
InputBaseProps,
|
|
35
35
|
'debugName' | 'disabled' | 'label'
|
|
36
36
|
> & {
|
|
37
|
-
variants?: ComponentVariants<typeof NumberIncrementPresets>['variants']
|
|
38
37
|
styles?: StylesOf<NumberIncrementComposition>
|
|
39
38
|
value: number
|
|
40
39
|
onValueChange: (value: number) => void
|
|
@@ -54,7 +53,7 @@ export type NumberIncrementProps = Pick<
|
|
|
54
53
|
_error?: string
|
|
55
54
|
formatter?: FormatInputValueFunction
|
|
56
55
|
placeholder?: string
|
|
57
|
-
}
|
|
56
|
+
} & ComponentVariants<typeof NumberIncrementPresets>
|
|
58
57
|
|
|
59
58
|
export const NumberIncrement = (props: NumberIncrementProps) => {
|
|
60
59
|
const {
|
|
@@ -63,6 +62,7 @@ export const NumberIncrement = (props: NumberIncrementProps) => {
|
|
|
63
62
|
} = selectInputBaseProps(props)
|
|
64
63
|
|
|
65
64
|
const {
|
|
65
|
+
responsiveVariants = {},
|
|
66
66
|
variants = [],
|
|
67
67
|
style = {},
|
|
68
68
|
styles = {},
|
|
@@ -113,6 +113,7 @@ export const NumberIncrement = (props: NumberIncrementProps) => {
|
|
|
113
113
|
const variantStyles = useDefaultComponentStyle<'u:NumberIncrement', typeof NumberIncrementPresets>(
|
|
114
114
|
'u:NumberIncrement',
|
|
115
115
|
{
|
|
116
|
+
responsiveVariants,
|
|
116
117
|
variants,
|
|
117
118
|
styles,
|
|
118
119
|
rootElement: 'wrapper',
|
|
@@ -8,18 +8,26 @@ import { Touchable, TouchableProps } from '../Touchable'
|
|
|
8
8
|
import { View, ViewProps } from '../View'
|
|
9
9
|
import { OverlayComposition, OverlayPresets } from './styles'
|
|
10
10
|
import { NativeHTMLElement } from '../../types'
|
|
11
|
+
import { usePopState } from '../../lib'
|
|
11
12
|
|
|
12
13
|
export type OverlayProps<T extends NativeHTMLElement = 'div'> = {
|
|
13
14
|
visible?: boolean
|
|
14
15
|
styles?: StylesOf<OverlayComposition>
|
|
15
16
|
onPress?: TouchableProps<'div'>['onClick']
|
|
17
|
+
scrollLocked?: boolean
|
|
16
18
|
} & ComponentVariants<typeof OverlayPresets> & Omit<ViewProps<T>, 'variants' | 'responsiveVariants'>
|
|
17
19
|
|
|
18
20
|
export * from './styles'
|
|
19
21
|
|
|
20
22
|
export const Overlay = <T extends NativeHTMLElement>(overlayProps:OverlayProps<T>) => {
|
|
21
|
-
const {
|
|
22
|
-
|
|
23
|
+
const {
|
|
24
|
+
visible,
|
|
25
|
+
responsiveVariants,
|
|
26
|
+
variants,
|
|
27
|
+
styles,
|
|
28
|
+
scrollLocked = true,
|
|
29
|
+
...props
|
|
30
|
+
} = overlayProps
|
|
23
31
|
|
|
24
32
|
const variantStyles = useDefaultComponentStyle('Overlay', {
|
|
25
33
|
variants,
|
|
@@ -27,6 +35,8 @@ export const Overlay = <T extends NativeHTMLElement>(overlayProps:OverlayProps<T
|
|
|
27
35
|
styles,
|
|
28
36
|
})
|
|
29
37
|
|
|
38
|
+
usePopState(visible, props.onPress, scrollLocked)
|
|
39
|
+
|
|
30
40
|
const Component = props.onClick || props.onPress ? Touchable : View
|
|
31
41
|
|
|
32
42
|
return (
|
|
@@ -20,6 +20,7 @@ import 'slick-carousel/slick/slick.css'
|
|
|
20
20
|
import 'slick-carousel/slick/slick-theme.css'
|
|
21
21
|
import { View, ViewProps } from '../View'
|
|
22
22
|
import { Touchable } from '../Touchable'
|
|
23
|
+
import { ComponentCommonProps } from '../../types'
|
|
23
24
|
|
|
24
25
|
export type PagerRef = {
|
|
25
26
|
goTo: (page: number) => void
|
|
@@ -37,7 +38,8 @@ export type PagerProps = Settings &
|
|
|
37
38
|
dotsProps?: DotsProps
|
|
38
39
|
pageWrapperProps?: ViewProps<'div'>
|
|
39
40
|
dotsDisabled?:boolean
|
|
40
|
-
|
|
41
|
+
disableSwipe?: boolean
|
|
42
|
+
} & ComponentCommonProps
|
|
41
43
|
|
|
42
44
|
type DotsProps = Pick<PagerProps, 'page' | 'dotsDisabled'> & {
|
|
43
45
|
childArray: ReactNode[]
|
|
@@ -84,6 +86,7 @@ const PagerComponent = (
|
|
|
84
86
|
dots = false,
|
|
85
87
|
dotsDisabled = false,
|
|
86
88
|
infinite = false,
|
|
89
|
+
disableSwipe = false,
|
|
87
90
|
onChange,
|
|
88
91
|
footer,
|
|
89
92
|
dotsProps,
|
|
@@ -126,6 +129,7 @@ const PagerComponent = (
|
|
|
126
129
|
arrows={false}
|
|
127
130
|
ref={sliderRef}
|
|
128
131
|
dots={false}
|
|
132
|
+
swipe={!disableSwipe}
|
|
129
133
|
infinite={infinite}
|
|
130
134
|
accessibility={false}
|
|
131
135
|
afterChange={onChange}
|
|
@@ -29,8 +29,7 @@ export type RadioGroupProps<T extends string|number> = WrapperProps & {
|
|
|
29
29
|
onValueChange(value: T): void
|
|
30
30
|
label: ReactNode
|
|
31
31
|
styles?: StylesOf<RadioInputComposition>
|
|
32
|
-
|
|
33
|
-
}
|
|
32
|
+
} & ComponentVariants<typeof RadioInputPresets>
|
|
34
33
|
|
|
35
34
|
type OptionProps<T extends string|number> = {
|
|
36
35
|
item: RadioOption<T>
|
|
@@ -119,13 +118,15 @@ export const RadioGroup = <T extends string|number>(
|
|
|
119
118
|
options,
|
|
120
119
|
value,
|
|
121
120
|
onValueChange,
|
|
122
|
-
|
|
121
|
+
responsiveVariants = {},
|
|
122
|
+
variants = [],
|
|
123
123
|
styles,
|
|
124
124
|
disabled,
|
|
125
125
|
debugName,
|
|
126
126
|
} = others
|
|
127
127
|
|
|
128
128
|
const variantStyles = useDefaultComponentStyle<'u:RadioInput', typeof RadioInputPresets>('u:RadioInput', {
|
|
129
|
+
responsiveVariants,
|
|
129
130
|
variants,
|
|
130
131
|
styles,
|
|
131
132
|
})
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { AnyFunction, IconPlaceholder, TypeGuards } from '@codeleap/common'
|
|
2
|
+
import React, { useState } from 'react'
|
|
3
|
+
import { TextInput, TextInputProps } from '../TextInput'
|
|
4
|
+
import { ComponentWithDefaultProps } from '../../types/utility'
|
|
5
|
+
|
|
6
|
+
export type SearchInputProps = {
|
|
7
|
+
placeholder: string
|
|
8
|
+
clearable?: boolean
|
|
9
|
+
debugName: string
|
|
10
|
+
clearIcon?: IconPlaceholder
|
|
11
|
+
searchIcon?: IconPlaceholder
|
|
12
|
+
debounce?: number
|
|
13
|
+
onSearchChange: (search: string) => void
|
|
14
|
+
onTypingChange?: (isTyping: boolean) => void
|
|
15
|
+
onValueChange?: (search: string) => void
|
|
16
|
+
onClear?: AnyFunction
|
|
17
|
+
} & Partial<TextInputProps>
|
|
18
|
+
|
|
19
|
+
export const SearchInput: ComponentWithDefaultProps<SearchInputProps> = (props) => {
|
|
20
|
+
const {
|
|
21
|
+
debugName,
|
|
22
|
+
onSearchChange,
|
|
23
|
+
onTypingChange,
|
|
24
|
+
onClear,
|
|
25
|
+
clearable,
|
|
26
|
+
placeholder,
|
|
27
|
+
clearIcon,
|
|
28
|
+
searchIcon,
|
|
29
|
+
debounce,
|
|
30
|
+
value,
|
|
31
|
+
onValueChange,
|
|
32
|
+
...rest
|
|
33
|
+
} = {
|
|
34
|
+
...SearchInput.defaultProps,
|
|
35
|
+
...props,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const hasStateProps = !TypeGuards.isNil(value) && TypeGuards.isFunction(onValueChange)
|
|
39
|
+
|
|
40
|
+
const [search, setSearch] = hasStateProps ? [value, onValueChange] : useState('')
|
|
41
|
+
const setSearchTimeout = React.useRef<NodeJS.Timeout|null>(null)
|
|
42
|
+
|
|
43
|
+
const handleChangeSearch = (value: string) => {
|
|
44
|
+
setSearch(value)
|
|
45
|
+
|
|
46
|
+
if (TypeGuards.isNil(debounce)) {
|
|
47
|
+
onSearchChange?.(value)
|
|
48
|
+
} else {
|
|
49
|
+
if (!TypeGuards.isNil(setSearchTimeout.current)) {
|
|
50
|
+
clearTimeout(setSearchTimeout.current)
|
|
51
|
+
}
|
|
52
|
+
setSearchTimeout.current = setTimeout(() => {
|
|
53
|
+
onSearchChange(value)
|
|
54
|
+
onTypingChange?.(false)
|
|
55
|
+
}, debounce ?? 0)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const handleClear = () => {
|
|
60
|
+
setSearch('')
|
|
61
|
+
onSearchChange?.('')
|
|
62
|
+
if (TypeGuards.isFunction(onClear)) onClear?.()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const showClearIcon = !!search?.trim() && clearable
|
|
66
|
+
|
|
67
|
+
return (
|
|
68
|
+
<TextInput
|
|
69
|
+
value={search}
|
|
70
|
+
placeholder={placeholder}
|
|
71
|
+
onChangeText={(value) => {
|
|
72
|
+
onTypingChange?.(true)
|
|
73
|
+
handleChangeSearch(value)
|
|
74
|
+
}}
|
|
75
|
+
debugName={`Search ${debugName}`}
|
|
76
|
+
rightIcon={showClearIcon && {
|
|
77
|
+
name: clearIcon,
|
|
78
|
+
onPress: handleClear,
|
|
79
|
+
}}
|
|
80
|
+
leftIcon={{
|
|
81
|
+
name: searchIcon,
|
|
82
|
+
}}
|
|
83
|
+
{...rest}
|
|
84
|
+
/>
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
SearchInput.defaultProps = {
|
|
89
|
+
debounce: null,
|
|
90
|
+
clearable: true,
|
|
91
|
+
clearIcon: 'close' as IconPlaceholder,
|
|
92
|
+
searchIcon: 'search' as IconPlaceholder,
|
|
93
|
+
}
|
|
@@ -7,7 +7,7 @@ import { StylesOf } from '../../types'
|
|
|
7
7
|
import { Text } from '../Text'
|
|
8
8
|
import { Touchable } from '../Touchable'
|
|
9
9
|
import { SegmentedControlComposition } from './styles'
|
|
10
|
-
import { Icon } from '../Icon'
|
|
10
|
+
import { Icon, IconProps } from '../Icon'
|
|
11
11
|
|
|
12
12
|
type OptionRef = PropsOf<typeof Touchable>['ref']
|
|
13
13
|
|
|
@@ -17,6 +17,7 @@ export type SegmentedControlOptionProps = PropsOf<typeof Touchable> & {
|
|
|
17
17
|
variantStyles?: StylesOf<SegmentedControlComposition>
|
|
18
18
|
value?: any
|
|
19
19
|
textProps?: Omit<PropsOf<typeof Text>, 'key'>
|
|
20
|
+
iconProps?: Partial<IconProps>
|
|
20
21
|
icon?: IconPlaceholder
|
|
21
22
|
ref?: OptionRef
|
|
22
23
|
}
|
|
@@ -26,18 +27,24 @@ const SegmentedControlOptionCP = (props: SegmentedControlOptionProps, ref: Optio
|
|
|
26
27
|
selected,
|
|
27
28
|
onPress,
|
|
28
29
|
style,
|
|
29
|
-
variantStyles,
|
|
30
|
+
variantStyles = {},
|
|
31
|
+
iconProps = {},
|
|
30
32
|
label,
|
|
31
33
|
icon,
|
|
32
34
|
textProps,
|
|
33
|
-
|
|
34
35
|
disabled,
|
|
35
36
|
...touchableProps
|
|
36
37
|
} = props
|
|
37
38
|
|
|
39
|
+
|
|
40
|
+
const iconStyles = {
|
|
41
|
+
...variantStyles.icon as object,
|
|
42
|
+
...(selected ? variantStyles['icon:selected'] as object : {}),
|
|
43
|
+
...(disabled ? variantStyles['icon:disabled'] as object : {}),
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
return (
|
|
39
47
|
<Touchable
|
|
40
|
-
|
|
41
48
|
key={touchableProps.key}
|
|
42
49
|
ref={ref}
|
|
43
50
|
css={[
|
|
@@ -53,17 +60,16 @@ const SegmentedControlOptionCP = (props: SegmentedControlOptionProps, ref: Optio
|
|
|
53
60
|
{
|
|
54
61
|
!!icon && (
|
|
55
62
|
<Icon
|
|
63
|
+
debugName={touchableProps?.debugName}
|
|
56
64
|
name={icon}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
selected && variantStyles['icon:selected'],
|
|
60
|
-
disabled && variantStyles['icon:disabled'],
|
|
61
|
-
]}
|
|
65
|
+
style={iconStyles}
|
|
66
|
+
{...iconProps}
|
|
62
67
|
/>
|
|
63
68
|
)
|
|
64
69
|
}
|
|
65
70
|
<Text
|
|
66
71
|
text={label}
|
|
72
|
+
debugName={touchableProps?.debugName}
|
|
67
73
|
css={[
|
|
68
74
|
variantStyles.text,
|
|
69
75
|
selected && variantStyles['text:selected'],
|
|
@@ -8,6 +8,7 @@ import { Touchable } from '../Touchable'
|
|
|
8
8
|
import { SegmentedControlComposition } from './styles'
|
|
9
9
|
import { motion, MotionProps, AnimationProps, ForwardRefComponent } from 'framer-motion'
|
|
10
10
|
import { useAnimatedVariantStyles } from '../../lib'
|
|
11
|
+
import { IconProps } from '../Icon'
|
|
11
12
|
|
|
12
13
|
export type SegmentedControlOptionProps<T = string> = {
|
|
13
14
|
label: string
|
|
@@ -46,6 +47,8 @@ export type SegmentedControlProps<T = string> = ComponentVariants<typeof Segment
|
|
|
46
47
|
animationProps?: AnimationProps
|
|
47
48
|
transitionDuration?: number
|
|
48
49
|
RenderAnimatedView?: ForwardRefComponent<HTMLDivElement, any>
|
|
50
|
+
textProps?: Omit<PropsOf<typeof Text>, 'key'>
|
|
51
|
+
iconProps?: Partial<IconProps>
|
|
49
52
|
}
|
|
50
53
|
|
|
51
54
|
const defaultProps: Partial<SegmentedControlProps> = {
|
|
@@ -75,6 +78,8 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
|
|
|
75
78
|
transitionDuration,
|
|
76
79
|
disabled,
|
|
77
80
|
RenderAnimatedView: Bubble,
|
|
81
|
+
textProps = {},
|
|
82
|
+
iconProps = {},
|
|
78
83
|
debugName,
|
|
79
84
|
...rest
|
|
80
85
|
} = allProps
|
|
@@ -92,7 +97,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
|
|
|
92
97
|
|
|
93
98
|
const maxDivWidthRef = useRef(null)
|
|
94
99
|
|
|
95
|
-
const
|
|
100
|
+
const largestWidth = React.useMemo(() => {
|
|
96
101
|
return {
|
|
97
102
|
width: maxDivWidthRef.current,
|
|
98
103
|
}
|
|
@@ -104,7 +109,7 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
|
|
|
104
109
|
updater: () => {
|
|
105
110
|
'worklet'
|
|
106
111
|
return {
|
|
107
|
-
translateX: currentOptionIdx *
|
|
112
|
+
translateX: currentOptionIdx * largestWidth.width,
|
|
108
113
|
transition: {
|
|
109
114
|
ease: 'easeInOut',
|
|
110
115
|
duration: transitionDuration,
|
|
@@ -112,13 +117,13 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
|
|
|
112
117
|
...animationProps,
|
|
113
118
|
} as AnimationProps
|
|
114
119
|
},
|
|
115
|
-
dependencies: [currentOptionIdx,
|
|
120
|
+
dependencies: [currentOptionIdx, largestWidth.width],
|
|
116
121
|
})
|
|
117
122
|
|
|
118
123
|
const selectedBubbleStyles = [
|
|
119
124
|
variantStyles.selectedBubble,
|
|
120
125
|
disabled && variantStyles['selectedBubble:disabled'],
|
|
121
|
-
|
|
126
|
+
largestWidth,
|
|
122
127
|
]
|
|
123
128
|
|
|
124
129
|
return (
|
|
@@ -146,8 +151,10 @@ export const SegmentedControl = (props: SegmentedControlProps) => {
|
|
|
146
151
|
icon={o.icon}
|
|
147
152
|
selected={value === o.value}
|
|
148
153
|
variantStyles={variantStyles}
|
|
149
|
-
style={
|
|
154
|
+
style={largestWidth}
|
|
150
155
|
disabled={disabled}
|
|
156
|
+
textProps={textProps}
|
|
157
|
+
iconProps={iconProps}
|
|
151
158
|
{...props?.touchableProps}
|
|
152
159
|
/>
|
|
153
160
|
))}
|