@codeleap/web 3.3.2 → 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.
Files changed (59) hide show
  1. package/package.json +3 -1
  2. package/src/components/ActionIcon/index.tsx +49 -30
  3. package/src/components/ActionIcon/styles.ts +8 -5
  4. package/src/components/ActivityIndicator/index.tsx +6 -5
  5. package/src/components/Badge/index.tsx +135 -0
  6. package/src/components/Badge/styles.ts +15 -0
  7. package/src/components/Button/index.tsx +79 -69
  8. package/src/components/Button/styles.ts +10 -14
  9. package/src/components/Checkbox/index.tsx +9 -3
  10. package/src/components/Collapse/index.tsx +6 -3
  11. package/src/components/Drawer/index.tsx +72 -44
  12. package/src/components/Drawer/styles.ts +11 -3
  13. package/src/components/EmptyPlaceholder/index.tsx +135 -0
  14. package/src/components/EmptyPlaceholder/styles.ts +16 -0
  15. package/src/components/Grid/index.tsx +169 -0
  16. package/src/components/Grid/styles.ts +10 -0
  17. package/src/components/Grid/types.ts +24 -0
  18. package/src/components/Icon/index.tsx +7 -4
  19. package/src/components/InputBase/styles.ts +8 -56
  20. package/src/components/InputBase/utils.ts +63 -0
  21. package/src/components/List/ListLayout.tsx +98 -0
  22. package/src/components/List/PaginationIndicator.tsx +102 -0
  23. package/src/components/List/index.tsx +104 -91
  24. package/src/components/List/styles.ts +17 -5
  25. package/src/components/List/types.ts +51 -0
  26. package/src/components/List/useInfiniteScroll.ts +113 -0
  27. package/src/components/LoadingOverlay/index.tsx +5 -3
  28. package/src/components/Modal/index.tsx +30 -29
  29. package/src/components/NumberIncrement/index.tsx +3 -2
  30. package/src/components/Overlay/index.tsx +12 -2
  31. package/src/components/Pager/index.tsx +5 -1
  32. package/src/components/RadioInput/index.tsx +4 -3
  33. package/src/components/SearchInput/index.tsx +93 -0
  34. package/src/components/SegmentedControl/SegmentedControlOption.tsx +15 -9
  35. package/src/components/SegmentedControl/index.tsx +12 -5
  36. package/src/components/Select/index.tsx +18 -14
  37. package/src/components/Select/styles.ts +4 -3
  38. package/src/components/Select/types.ts +4 -3
  39. package/src/components/Slider/index.tsx +3 -2
  40. package/src/components/Switch/index.tsx +4 -2
  41. package/src/components/Text/index.tsx +98 -25
  42. package/src/components/Text/styles.ts +4 -4
  43. package/src/components/TextInput/index.tsx +6 -15
  44. package/src/components/Tooltip/index.tsx +163 -131
  45. package/src/components/Tooltip/styles.ts +13 -9
  46. package/src/components/Touchable/index.tsx +88 -26
  47. package/src/components/Touchable/styles.ts +4 -6
  48. package/src/components/View/index.tsx +1 -1
  49. package/src/components/components.ts +5 -1
  50. package/src/components/defaultStyles.ts +10 -3
  51. package/src/index.ts +2 -0
  52. package/src/lib/hooks.ts +27 -13
  53. package/src/lib/index.ts +4 -1
  54. package/src/lib/useBreakpointMatch.ts +33 -0
  55. package/src/lib/usePopState.ts +30 -0
  56. package/src/lib/useSearchParams.ts +54 -0
  57. package/src/lib/utils/stopPropagation.ts +3 -3
  58. package/src/types/index.ts +1 -1
  59. 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
- scrollable?: boolean
56
- } & ComponentVariants<typeof ModalPresets>
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
- scrollable: false,
149
+ withScrollContainer: false,
150
+ scrollLocked: true,
143
151
  }
144
152
 
145
- export const ModalContent: React.FC<ModalProps & { id: string }> = (
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
- scrollable,
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
- onUpdate(() => {
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 = scrollable ? Scroll : View
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
- <ModalArea
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
- <View css={variantStyles.innerWrapper}>
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
- </View>
292
- </ModalArea>
291
+ </ModalArea>
292
+ </View>
293
293
  )
294
294
  }
295
295
 
296
- export const Modal: React.FC<ModalProps> = (props) => {
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 { visible, responsiveVariants, variants, styles, ...props } =
22
- overlayProps
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
- variants?: ComponentVariants<typeof RadioInputPresets>['variants']
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
- variants,
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
- css={[
58
- variantStyles.icon,
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 biggerWidth = React.useMemo(() => {
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 * biggerWidth.width,
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, biggerWidth.width],
120
+ dependencies: [currentOptionIdx, largestWidth.width],
116
121
  })
117
122
 
118
123
  const selectedBubbleStyles = [
119
124
  variantStyles.selectedBubble,
120
125
  disabled && variantStyles['selectedBubble:disabled'],
121
- biggerWidth,
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={biggerWidth}
154
+ style={largestWidth}
150
155
  disabled={disabled}
156
+ textProps={textProps}
157
+ iconProps={iconProps}
151
158
  {...props?.touchableProps}
152
159
  />
153
160
  ))}