@codeleap/web 3.7.1 → 3.9.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 +1 -1
- package/src/components/Grid/index.tsx +1 -1
- package/src/components/List/index.tsx +8 -7
- package/src/components/Select/index.tsx +5 -1
- package/src/components/Select/types.ts +2 -0
- package/src/components/Tooltip/index.tsx +14 -1
- package/src/lib/index.ts +1 -0
- package/src/lib/useBreakpointMatch.ts +38 -11
- package/src/lib/useClick.ts +44 -0
package/package.json
CHANGED
|
@@ -14,6 +14,8 @@ export * from './useInfiniteScroll'
|
|
|
14
14
|
export * from './types'
|
|
15
15
|
export * from './ListLayout'
|
|
16
16
|
|
|
17
|
+
export type ListComponentType = <T extends any[] = any[]>(props: ListProps<T>) => React.ReactElement
|
|
18
|
+
|
|
17
19
|
const RenderSeparator = (props: { separatorStyles: ViewProps<'div'>['css'] }) => {
|
|
18
20
|
return (
|
|
19
21
|
<View css={[props?.separatorStyles]}></View>
|
|
@@ -29,14 +31,14 @@ const defaultProps: Partial<ListProps> = {
|
|
|
29
31
|
ListSeparatorComponent: RenderSeparator,
|
|
30
32
|
refreshDebounce: 3000,
|
|
31
33
|
refreshSize: 40,
|
|
32
|
-
refreshThreshold:
|
|
34
|
+
refreshThreshold: 0.5,
|
|
33
35
|
refreshPosition: 2,
|
|
34
36
|
refresh: true,
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
const
|
|
38
|
-
const allProps = {
|
|
39
|
-
...defaultProps,
|
|
39
|
+
export const List: ListComponentType = React.forwardRef<'div', ListProps>((flatListProps, ref) => {
|
|
40
|
+
const allProps = { // @ts-ignore
|
|
41
|
+
...List.defaultProps,
|
|
40
42
|
...flatListProps,
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -114,6 +116,5 @@ const ListCP = React.forwardRef<'div', ListProps>((flatListProps, ref) => {
|
|
|
114
116
|
)
|
|
115
117
|
})
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
export const List = ListCP as unknown as ListComponentType
|
|
119
|
+
// @ts-ignore
|
|
120
|
+
List.defaultProps = defaultProps
|
|
@@ -219,13 +219,17 @@ export const Select = forwardRef<HTMLInputElement, SelectProps>(
|
|
|
219
219
|
filterItems = null,
|
|
220
220
|
itemProps = {},
|
|
221
221
|
loadingIndicatorSize,
|
|
222
|
+
selectedOption: _selectedOption,
|
|
223
|
+
setSelectedOption: _setSelectedOption,
|
|
222
224
|
...otherProps
|
|
223
225
|
} = selectProps
|
|
224
226
|
|
|
225
227
|
const innerInputRef = useRef<any>(null)
|
|
226
228
|
const innerWrapperRef = useRef(null)
|
|
227
229
|
|
|
228
|
-
const
|
|
230
|
+
const hasSelectedOptionState = !TypeGuards.isNil(_selectedOption) && TypeGuards.isFunction(_setSelectedOption)
|
|
231
|
+
|
|
232
|
+
const [selectedOption, setSelectedOption] = hasSelectedOptionState ? [_selectedOption, _setSelectedOption] : useState(value)
|
|
229
233
|
|
|
230
234
|
const [_isFocused, setIsFocused] = useState(false)
|
|
231
235
|
|
|
@@ -93,6 +93,8 @@ export type SelectProps<T = any, Multi extends boolean = false> = React.PropsWit
|
|
|
93
93
|
itemProps?: ButtonProps
|
|
94
94
|
loadingIndicatorSize?: number
|
|
95
95
|
limit?: number
|
|
96
|
+
selectedOption?: ReactSelectProps<T>['value']
|
|
97
|
+
setSelectedOption?: ReactSelectProps<T>['onValueChange']
|
|
96
98
|
} & Omit<
|
|
97
99
|
ReactSelectProps<T, Multi>,
|
|
98
100
|
'isSearchable' | 'isClearable' | 'isDisabled' | 'loadingMessage' | 'filterOption' |
|
|
@@ -18,6 +18,7 @@ import { AnyFunction, ComponentVariants, StylesOf, TypeGuards, useDefaultCompone
|
|
|
18
18
|
import { TooltipComposition, TooltipPresets } from './styles'
|
|
19
19
|
import { ComponentCommonProps, ComponentWithDefaultProps } from '../../types/utility'
|
|
20
20
|
import { View, ViewProps } from '../View'
|
|
21
|
+
import { useClickOutsideElement } from '../../lib'
|
|
21
22
|
|
|
22
23
|
type TooltipComponentProps = {
|
|
23
24
|
contentProps?: TooltipContentProps
|
|
@@ -39,6 +40,7 @@ export type TooltipProps = PrimitiveTooltipProps & TooltipComponentProps & {
|
|
|
39
40
|
openOnHover?: boolean
|
|
40
41
|
disabled?: boolean
|
|
41
42
|
delayDuration?: number
|
|
43
|
+
closeOnClickOutside?: boolean
|
|
42
44
|
onOpen?: AnyFunction
|
|
43
45
|
onClose?: AnyFunction
|
|
44
46
|
onValueChange?: (value: boolean) => void
|
|
@@ -52,6 +54,7 @@ const defaultProps: Partial<TooltipProps> = {
|
|
|
52
54
|
openOnHover: true,
|
|
53
55
|
disabled: false,
|
|
54
56
|
delayDuration: 0,
|
|
57
|
+
closeOnClickOutside: false,
|
|
55
58
|
side: 'bottom',
|
|
56
59
|
triggerWrapper: View,
|
|
57
60
|
}
|
|
@@ -87,6 +90,7 @@ export const Tooltip: ComponentWithDefaultProps<TooltipProps> = (props: TooltipP
|
|
|
87
90
|
variants = [],
|
|
88
91
|
responsiveVariants = {},
|
|
89
92
|
styles = {},
|
|
93
|
+
closeOnClickOutside,
|
|
90
94
|
...rest
|
|
91
95
|
} = allProps
|
|
92
96
|
|
|
@@ -142,6 +146,14 @@ export const Tooltip: ComponentWithDefaultProps<TooltipProps> = (props: TooltipP
|
|
|
142
146
|
if (TypeGuards.isFunction(onPress)) onPress?.(value)
|
|
143
147
|
}
|
|
144
148
|
|
|
149
|
+
const triggerRef = React.useRef(null)
|
|
150
|
+
|
|
151
|
+
const contentRef = useClickOutsideElement((isOutside) => {
|
|
152
|
+
if (isOutside && closeOnClickOutside && !openOnHover && visible) {
|
|
153
|
+
handleToggle(false)
|
|
154
|
+
}
|
|
155
|
+
}, [triggerRef])
|
|
156
|
+
|
|
145
157
|
return (
|
|
146
158
|
<TooltipContainer {...providerProps}>
|
|
147
159
|
<TooltipWrapper
|
|
@@ -155,6 +167,7 @@ export const Tooltip: ComponentWithDefaultProps<TooltipProps> = (props: TooltipP
|
|
|
155
167
|
onMouseEnter={() => _onHover('enter')}
|
|
156
168
|
onMouseLeave={() => _onHover('leave')}
|
|
157
169
|
asChild
|
|
170
|
+
ref={triggerRef}
|
|
158
171
|
{...triggerProps}
|
|
159
172
|
>
|
|
160
173
|
<TriggerWrapper {...allProps as any} {...triggerWrapperProps}>
|
|
@@ -162,7 +175,7 @@ export const Tooltip: ComponentWithDefaultProps<TooltipProps> = (props: TooltipP
|
|
|
162
175
|
</TriggerWrapper>
|
|
163
176
|
</TooltipTrigger>
|
|
164
177
|
<TooltipPortal {...portalProps}>
|
|
165
|
-
<TooltipContent css={[tooltipDirectionStyle, variantsStyles.wrapper]} sideOffset={2} side={side} {...contentProps}>
|
|
178
|
+
<TooltipContent ref={contentRef} css={[tooltipDirectionStyle, variantsStyles.wrapper]} sideOffset={2} side={side} {...contentProps}>
|
|
166
179
|
{
|
|
167
180
|
TypeGuards.isFunction(Content)
|
|
168
181
|
? <Content
|
package/src/lib/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TypeGuards, useCodeleapContext } from '@codeleap/common'
|
|
2
3
|
import { useMediaQuery } from './hooks'
|
|
3
4
|
|
|
4
|
-
export type BreakpointsMatch = Record<
|
|
5
|
-
defaultMedia: string
|
|
6
|
-
}
|
|
5
|
+
export type BreakpointsMatch<T extends string = string> = Record<T, any>
|
|
7
6
|
|
|
8
|
-
export
|
|
7
|
+
export function useBreakpointMatch<T extends string = string>(values: BreakpointsMatch<T>) {
|
|
9
8
|
const { Theme } = useCodeleapContext()
|
|
10
9
|
|
|
11
|
-
const
|
|
12
|
-
|
|
10
|
+
const themeBreakpoints: Record<string, number> = Theme?.breakpoints
|
|
11
|
+
|
|
12
|
+
const breakpoints: Record<string, number> = React.useMemo(() => {
|
|
13
|
+
const breaks = Object.entries(themeBreakpoints)
|
|
13
14
|
|
|
14
15
|
breaks?.sort((a, b) => a?.[1] - b?.[1])
|
|
15
16
|
|
|
@@ -18,16 +19,42 @@ export const useBreakpointMatch = (values: BreakpointsMatch) => {
|
|
|
18
19
|
return sortBreakpoints
|
|
19
20
|
}, [])
|
|
20
21
|
|
|
22
|
+
const breakpointValues: Array<string> = React.useMemo(() => {
|
|
23
|
+
const _breakpoints = Object.keys(breakpoints)
|
|
24
|
+
|
|
25
|
+
return _breakpoints.sort((a, b) => breakpoints?.[a] - breakpoints?.[b])
|
|
26
|
+
}, [])
|
|
27
|
+
|
|
21
28
|
const breakpointMatches = {}
|
|
22
29
|
|
|
23
30
|
for (const breakpoint in breakpoints) {
|
|
24
|
-
const matchesDown = useMediaQuery(Theme.media.down(breakpoint))
|
|
25
|
-
const matchesUp = useMediaQuery(Theme.media.up(breakpoint))
|
|
31
|
+
const matchesDown = useMediaQuery(Theme.media.down(breakpoint), { getInitialValueInEffect: false })
|
|
32
|
+
const matchesUp = useMediaQuery(Theme.media.up(breakpoint), { getInitialValueInEffect: false })
|
|
26
33
|
|
|
27
34
|
breakpointMatches[breakpoint] = !matchesUp && matchesDown
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
const
|
|
37
|
+
const currentBreakpoint = Object.keys(breakpointMatches)?.find((key) => breakpointMatches?.[key])
|
|
38
|
+
|
|
39
|
+
const breakpoint = React.useMemo(() => {
|
|
40
|
+
const validBreakpointIndex = breakpointValues?.findIndex(_breakpoint => _breakpoint === currentBreakpoint)
|
|
41
|
+
|
|
42
|
+
const validBreakpoints = breakpointValues?.slice(validBreakpointIndex, 100)
|
|
43
|
+
|
|
44
|
+
let validBreakpoint = null
|
|
45
|
+
|
|
46
|
+
validBreakpoint = validBreakpoints?.find((currentValue) => {
|
|
47
|
+
if (Object?.keys(values)?.includes(currentValue)) {
|
|
48
|
+
return currentValue
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if (TypeGuards.isNil(validBreakpoint)) {
|
|
53
|
+
validBreakpoint = breakpointValues?.reverse()?.find(_breakpoint => !!values?.[_breakpoint])
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return validBreakpoint
|
|
57
|
+
}, [currentBreakpoint])
|
|
31
58
|
|
|
32
|
-
return values[breakpoint]
|
|
59
|
+
return values?.[breakpoint]
|
|
33
60
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { TypeGuards } from '@codeleap/common'
|
|
2
|
+
import React, { useEffect } from 'react'
|
|
3
|
+
|
|
4
|
+
type HandlerClickOutside = (clickedOutside: boolean) => void
|
|
5
|
+
|
|
6
|
+
type UseClickOutsideElementReturn = React.MutableRefObject<any>
|
|
7
|
+
|
|
8
|
+
export function useClickOutsideElement(
|
|
9
|
+
handler: HandlerClickOutside,
|
|
10
|
+
elements: Array<React.MutableRefObject<any>> = null,
|
|
11
|
+
): UseClickOutsideElementReturn {
|
|
12
|
+
const elementRef = React.useRef(null)
|
|
13
|
+
|
|
14
|
+
const handleClickOutside = React.useCallback((event: MouseEvent) => {
|
|
15
|
+
if (!elementRef.current) return
|
|
16
|
+
|
|
17
|
+
const clickedOutsideElement = !elementRef.current.contains(event?.target)
|
|
18
|
+
const clickedOutsideElements = [true]
|
|
19
|
+
|
|
20
|
+
if (TypeGuards?.isArray(elements)) {
|
|
21
|
+
elements?.forEach(element => {
|
|
22
|
+
if (element.current) {
|
|
23
|
+
clickedOutsideElements.push(!element.current.contains(event?.target))
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (clickedOutsideElement && clickedOutsideElements.every((clickedOutside) => !!clickedOutside)) {
|
|
29
|
+
handler(true)
|
|
30
|
+
} else {
|
|
31
|
+
handler(false)
|
|
32
|
+
}
|
|
33
|
+
}, [handler])
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
document.addEventListener('click', handleClickOutside)
|
|
37
|
+
|
|
38
|
+
return () => {
|
|
39
|
+
document.removeEventListener('click', handleClickOutside)
|
|
40
|
+
}
|
|
41
|
+
}, [handleClickOutside])
|
|
42
|
+
|
|
43
|
+
return elementRef
|
|
44
|
+
}
|