@codeleap/web 3.23.2 → 3.23.4
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 -8
- package/src/components/Modal/index.tsx +1 -1
- package/src/components/SectionFilters/index.tsx +10 -3
- package/src/components/SectionFilters/types.ts +1 -0
- package/src/components/TextInput/index.tsx +1 -1
- package/src/components/Touchable/index.tsx +1 -1
- package/src/components/View/index.tsx +1 -1
- package/src/index.ts +1 -3
- package/src/lib/hooks/index.ts +16 -0
- package/src/lib/{useAnimatedStyle.ts → hooks/useAnimatedStyle.ts} +3 -4
- package/src/lib/hooks/useAnimatedVariantStyles.ts +29 -0
- package/src/lib/{useBreakpointMatch.ts → hooks/useBreakpointMatch.ts} +5 -5
- package/src/lib/{useClick.ts → hooks/useClick.ts} +3 -3
- package/src/lib/hooks/useClickOutside.ts +31 -0
- package/src/lib/{keyboard.ts → hooks/useKeydown.ts} +6 -5
- package/src/lib/{utils → hooks}/useMaxContentWidth.ts +1 -3
- package/src/lib/hooks/useMediaQuery.ts +41 -0
- package/src/lib/hooks/usePageExitBlocker.ts +44 -0
- package/src/lib/hooks/usePagination.ts +107 -0
- package/src/lib/{usePopState.ts → hooks/usePopState.ts} +0 -1
- package/src/lib/hooks/useScrollEffect.ts +19 -0
- package/src/lib/hooks/useStaticAnimationStyles.ts +14 -0
- package/src/lib/hooks/useWindowFocus.ts +33 -0
- package/src/lib/hooks/useWindowSize.ts +21 -0
- package/src/lib/index.ts +2 -10
- package/src/lib/tools/index.ts +4 -0
- package/src/lib/{localStorage.ts → tools/localStorage.ts} +4 -4
- package/src/lib/tools/mediaQuery.ts +25 -0
- package/src/lib/utils/index.ts +1 -4
- package/src/types/utility.ts +5 -2
- package/src/lib/Toast.ts +0 -23
- package/src/lib/hooks.ts +0 -428
- package/src/lib/utils/cookies.ts +0 -13
- /package/src/lib/{useSearchParams.ts → hooks/useSearchParams.ts} +0 -0
- /package/src/lib/{modal.ts → tools/modal.ts} +0 -0
- /package/src/lib/{navigation → tools/navigation}/index.ts +0 -0
- /package/src/lib/{navigation → tools/navigation}/types.ts +0 -0
- /package/src/lib/{test.ts → utils/test.ts} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codeleap/web",
|
|
3
|
-
"version": "3.23.
|
|
3
|
+
"version": "3.23.4",
|
|
4
4
|
"main": "src/index.ts",
|
|
5
5
|
"repository": {
|
|
6
6
|
"url": "https://github.com/codeleap-uk/internal-libs-monorepo.git",
|
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"@codeleap/config": "*",
|
|
13
13
|
"@emotion/react": "link:../../apps/web/node_modules/@emotion/react",
|
|
14
14
|
"@types/react-slick": "^0.23.10",
|
|
15
|
-
"@types/react-window": "1.8.5",
|
|
16
15
|
"react": "link:../../apps/web/node_modules/react"
|
|
17
16
|
},
|
|
18
17
|
"scripts": {
|
|
@@ -25,9 +24,7 @@
|
|
|
25
24
|
"@radix-ui/react-tooltip": "^1.0.6",
|
|
26
25
|
"@tiptap/react": "2.1.16",
|
|
27
26
|
"framer-motion": "^10.10.0",
|
|
28
|
-
"js-cookie": "^3.0.1",
|
|
29
27
|
"masonic": "^3.7.0",
|
|
30
|
-
"rc-slider": "^9.7.5",
|
|
31
28
|
"react-autosize-textarea": "^7.1.0",
|
|
32
29
|
"react-colorful": "^5.6.1",
|
|
33
30
|
"react-datepicker": "^4.20.0",
|
|
@@ -38,11 +35,7 @@
|
|
|
38
35
|
"react-number-format": "^5.2.1",
|
|
39
36
|
"react-select": "^5.7.3",
|
|
40
37
|
"react-slick": "^0.29.0",
|
|
41
|
-
"react-toastify": "^8.2.0",
|
|
42
|
-
"react-virtualized-auto-sizer": "^1.0.6",
|
|
43
|
-
"react-window": "1.8.5",
|
|
44
38
|
"slick-carousel": "^1.8.1",
|
|
45
|
-
"url-parse": "^1.5.10",
|
|
46
39
|
"uuid": "^8.3.2",
|
|
47
40
|
"zustand": "^4.4.1",
|
|
48
41
|
"@fastify/deepmerge": "1.3.0"
|
|
@@ -25,7 +25,7 @@ import { ActionIcon, ActionIconProps } from '../ActionIcon'
|
|
|
25
25
|
import { Scroll } from '../Scroll'
|
|
26
26
|
import { ComponentCommonProps } from '../../types'
|
|
27
27
|
import { Touchable, TouchableProps } from '../Touchable'
|
|
28
|
-
import { modalScrollLock, ModalStore } from '../../lib/modal'
|
|
28
|
+
import { modalScrollLock, ModalStore } from '../../lib/tools/modal'
|
|
29
29
|
|
|
30
30
|
export * from './styles'
|
|
31
31
|
|
|
@@ -22,6 +22,11 @@ const ItemOption = (props: OptionProps) => {
|
|
|
22
22
|
canSelectMultiple,
|
|
23
23
|
} = props
|
|
24
24
|
|
|
25
|
+
let buttonProps = {
|
|
26
|
+
...item?.itemProps,
|
|
27
|
+
...option?.itemProps,
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
const isItemSelected = useMemo(() => {
|
|
26
31
|
if (item?.options && selectedItems) {
|
|
27
32
|
if (canSelectMultiple) {
|
|
@@ -34,9 +39,11 @@ const ItemOption = (props: OptionProps) => {
|
|
|
34
39
|
}
|
|
35
40
|
}, [item?.options, option, selectedItems, item?.id, canSelectMultiple])
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if (isItemSelected) {
|
|
43
|
+
buttonProps = {
|
|
44
|
+
...buttonProps,
|
|
45
|
+
...item?.selectedItemProps,
|
|
46
|
+
}
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
return (
|
|
@@ -25,7 +25,7 @@ import { StylesOf, HTMLProps, ComponentWithDefaultProps } from '../../types/util
|
|
|
25
25
|
import { InputBase, InputBaseProps, selectInputBaseProps } from '../InputBase'
|
|
26
26
|
import { TextInputPresets } from './styles'
|
|
27
27
|
import { getMaskInputProps, TextInputMaskingProps } from './mask'
|
|
28
|
-
import { getTestId } from '../../lib/test'
|
|
28
|
+
import { getTestId } from '../../lib/utils/test'
|
|
29
29
|
|
|
30
30
|
export * from './styles'
|
|
31
31
|
export * from './mask'
|
|
@@ -5,7 +5,7 @@ import { View } from '../View'
|
|
|
5
5
|
import { TouchableComposition, TouchablePresets } from './styles'
|
|
6
6
|
import { CSSInterpolation } from '@emotion/css'
|
|
7
7
|
import { StylesOf, NativeHTMLElement } from '../../types'
|
|
8
|
-
import { getTestId } from '../../lib/test'
|
|
8
|
+
import { getTestId } from '../../lib/utils/test'
|
|
9
9
|
export * from './styles'
|
|
10
10
|
|
|
11
11
|
export type TouchableProps<T extends ElementType = 'button'> = ComponentPropsWithRef<T> & {
|
|
@@ -7,7 +7,7 @@ import { useMediaQuery } from '../../lib/hooks'
|
|
|
7
7
|
import { NativeHTMLElement } from '../../types'
|
|
8
8
|
import { motion } from 'framer-motion'
|
|
9
9
|
import { ViewProps } from './types'
|
|
10
|
-
import { getTestId } from '../../lib/test'
|
|
10
|
+
import { getTestId } from '../../lib/utils/test'
|
|
11
11
|
|
|
12
12
|
export * from './styles'
|
|
13
13
|
export * from './types'
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './useAnimatedStyle'
|
|
2
|
+
export * from './useAnimatedVariantStyles'
|
|
3
|
+
export * from './useBreakpointMatch'
|
|
4
|
+
export * from './useClick'
|
|
5
|
+
export * from './useClickOutside'
|
|
6
|
+
export * from './useMaxContentWidth'
|
|
7
|
+
export * from './useMediaQuery'
|
|
8
|
+
export * from './usePageExitBlocker'
|
|
9
|
+
export * from './usePagination'
|
|
10
|
+
export * from './usePopState'
|
|
11
|
+
export * from './useScrollEffect'
|
|
12
|
+
export * from './useSearchParams'
|
|
13
|
+
export * from './useStaticAnimationStyles'
|
|
14
|
+
export * from './useWindowFocus'
|
|
15
|
+
export * from './useWindowSize'
|
|
16
|
+
export * from './useKeydown'
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { onUpdate, useState } from '@codeleap/common'
|
|
1
|
+
import { useRef, useEffect, useState } from 'react'
|
|
3
2
|
import { AnimationProps } from 'framer-motion'
|
|
4
3
|
|
|
5
4
|
type UpdaterReturn = Omit<AnimationProps['animate'], 'transition'> & {
|
|
@@ -10,11 +9,11 @@ type UseAnimatedStyleReturn = Pick<AnimationProps, 'animate' | 'initial' | 'tran
|
|
|
10
9
|
|
|
11
10
|
export const useAnimatedStyle = (updater: () => UpdaterReturn, deps: Array<any>): UseAnimatedStyleReturn => {
|
|
12
11
|
const initialStyle = updater()
|
|
13
|
-
|
|
12
|
+
|
|
14
13
|
const [animatedStyle, setAnimatedStyle] = useState(initialStyle)
|
|
15
14
|
const transition = useRef(initialStyle.transition)
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
useEffect(() => {
|
|
18
17
|
const animatedStyleUpdated = updater()
|
|
19
18
|
|
|
20
19
|
setAnimatedStyle(animatedStyleUpdated)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AnimationProps } from 'framer-motion'
|
|
2
|
+
import { SelectProperties } from '../../types'
|
|
3
|
+
import { useStaticAnimationStyles } from './useStaticAnimationStyles'
|
|
4
|
+
import { useState, useEffect } from 'react'
|
|
5
|
+
|
|
6
|
+
type UseAnimatedVariantStylesConfig<T extends Record<string | number | symbol, any>, K extends keyof T> = {
|
|
7
|
+
variantStyles: T
|
|
8
|
+
animatedProperties: K[]
|
|
9
|
+
updater: (states: SelectProperties<T, K>) => AnimationProps
|
|
10
|
+
dependencies?: any[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useAnimatedVariantStyles<T extends Record<string | number | symbol, any>, K extends keyof T>(config: UseAnimatedVariantStylesConfig<T, K>) {
|
|
14
|
+
const { animatedProperties, updater, variantStyles, dependencies = [] } = config
|
|
15
|
+
|
|
16
|
+
const staticStyles = useStaticAnimationStyles(variantStyles, animatedProperties)
|
|
17
|
+
|
|
18
|
+
const initialState = updater(staticStyles)
|
|
19
|
+
|
|
20
|
+
const [animated, setAnimated] = useState(initialState)
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const nextState = updater(staticStyles)
|
|
24
|
+
|
|
25
|
+
setAnimated(nextState)
|
|
26
|
+
}, dependencies)
|
|
27
|
+
|
|
28
|
+
return animated
|
|
29
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
2
|
import { TypeGuards, useCodeleapContext } from '@codeleap/common'
|
|
3
|
-
import { useMediaQuery } from './
|
|
3
|
+
import { useMediaQuery } from './useMediaQuery'
|
|
4
4
|
|
|
5
5
|
export type BreakpointsMatch<T extends string = string> = Record<T, any>
|
|
6
6
|
|
|
@@ -9,7 +9,7 @@ export function useBreakpointMatch<T extends string = string>(values: Partial<Br
|
|
|
9
9
|
|
|
10
10
|
const themeBreakpoints: Record<string, number> = Theme?.breakpoints
|
|
11
11
|
|
|
12
|
-
const breakpoints: Record<string, number> =
|
|
12
|
+
const breakpoints: Record<string, number> = useMemo(() => {
|
|
13
13
|
const breaks = Object.entries(themeBreakpoints)
|
|
14
14
|
|
|
15
15
|
breaks?.sort((a, b) => a?.[1] - b?.[1])
|
|
@@ -19,7 +19,7 @@ export function useBreakpointMatch<T extends string = string>(values: Partial<Br
|
|
|
19
19
|
return sortBreakpoints
|
|
20
20
|
}, [])
|
|
21
21
|
|
|
22
|
-
const breakpointValues: Array<string> =
|
|
22
|
+
const breakpointValues: Array<string> = useMemo(() => {
|
|
23
23
|
const _breakpoints = Object.keys(breakpoints)
|
|
24
24
|
|
|
25
25
|
return _breakpoints.sort((a, b) => breakpoints?.[a] - breakpoints?.[b])
|
|
@@ -36,7 +36,7 @@ export function useBreakpointMatch<T extends string = string>(values: Partial<Br
|
|
|
36
36
|
|
|
37
37
|
const currentBreakpoint = Object.keys(breakpointMatches)?.find((key) => breakpointMatches?.[key])
|
|
38
38
|
|
|
39
|
-
const breakpoint =
|
|
39
|
+
const breakpoint = useMemo(() => {
|
|
40
40
|
const validBreakpointIndex = breakpointValues?.findIndex(_breakpoint => _breakpoint === currentBreakpoint)
|
|
41
41
|
|
|
42
42
|
const validBreakpoints = breakpointValues?.slice(validBreakpointIndex, 100)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TypeGuards } from '@codeleap/common'
|
|
2
|
-
import React, { useEffect } from 'react'
|
|
2
|
+
import React, { useEffect, useRef, useCallback } from 'react'
|
|
3
3
|
|
|
4
4
|
type HandlerClickOutside = (clickedOutside: boolean) => void
|
|
5
5
|
|
|
@@ -9,9 +9,9 @@ export function useClickOutsideElement(
|
|
|
9
9
|
handler: HandlerClickOutside,
|
|
10
10
|
elements: Array<React.MutableRefObject<any>> = null,
|
|
11
11
|
): UseClickOutsideElementReturn {
|
|
12
|
-
const elementRef =
|
|
12
|
+
const elementRef = useRef(null)
|
|
13
13
|
|
|
14
|
-
const handleClickOutside =
|
|
14
|
+
const handleClickOutside = useCallback((event: MouseEvent) => {
|
|
15
15
|
if (!elementRef.current) return
|
|
16
16
|
|
|
17
17
|
const clickedOutsideElement = !elementRef.current.contains(event?.target)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
const DEFAULT_EVENTS = ['mousedown', 'touchstart']
|
|
4
|
+
|
|
5
|
+
export function useClickOutside<T extends HTMLElement = any>(
|
|
6
|
+
handler: () => void,
|
|
7
|
+
events?: string[] | null,
|
|
8
|
+
nodes?: HTMLElement[],
|
|
9
|
+
) {
|
|
10
|
+
const ref = useRef<T>()
|
|
11
|
+
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
const listener = (event: any) => {
|
|
14
|
+
if (Array.isArray(nodes)) {
|
|
15
|
+
const shouldIgnore = event?.target?.hasAttribute('data-ignore-outside-clicks')
|
|
16
|
+
const shouldTrigger = nodes.every((node) => !!node && !node.contains(event.target))
|
|
17
|
+
shouldTrigger && !shouldIgnore && handler()
|
|
18
|
+
} else if (ref.current && !ref.current.contains(event.target)) {
|
|
19
|
+
handler()
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
(events || DEFAULT_EVENTS).forEach((fn) => document.addEventListener(fn, listener))
|
|
24
|
+
|
|
25
|
+
return () => {
|
|
26
|
+
(events || DEFAULT_EVENTS).forEach((fn) => document.removeEventListener(fn, listener))
|
|
27
|
+
}
|
|
28
|
+
}, [ref, handler, nodes])
|
|
29
|
+
|
|
30
|
+
return ref
|
|
31
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { TypeGuards } from '@codeleap/common'
|
|
2
|
+
import { useEffect } from 'react'
|
|
2
3
|
|
|
3
4
|
export const keydownDefaultKeyOptions = {
|
|
4
5
|
ArrowLeft: 'ArrowLeft',
|
|
@@ -11,11 +12,11 @@ export const keydownDefaultKeyOptions = {
|
|
|
11
12
|
code: 'Space',
|
|
12
13
|
},
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
export function useKeydown(
|
|
16
17
|
expectedKey: keyof typeof useKeydown.keys | { key: string; code: string },
|
|
17
|
-
handler: (event: KeyboardEvent) => void,
|
|
18
|
-
deps: Array<any> = [],
|
|
18
|
+
handler: (event: KeyboardEvent) => void,
|
|
19
|
+
deps: Array<any> = [],
|
|
19
20
|
options?: boolean | AddEventListenerOptions
|
|
20
21
|
) {
|
|
21
22
|
const eventKey = TypeGuards.isString(expectedKey) ? (useKeydown?.keys?.[expectedKey] ?? expectedKey) : expectedKey
|
|
@@ -28,7 +29,7 @@ export function useKeydown(
|
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
useEffect(() => {
|
|
32
33
|
document.addEventListener('keydown', handleKeyPress, options)
|
|
33
34
|
|
|
34
35
|
return () => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { useCodeleapContext } from '@codeleap/common'
|
|
2
|
-
import { useWindowSize } from '
|
|
2
|
+
import { useWindowSize } from './useWindowSize'
|
|
3
3
|
|
|
4
4
|
export const useMaxContentWidth = () => {
|
|
5
|
-
|
|
6
5
|
const { Theme } = useCodeleapContext()
|
|
7
6
|
const [width, height] = useWindowSize()
|
|
8
7
|
|
|
@@ -45,5 +44,4 @@ export const useMaxContentWidth = () => {
|
|
|
45
44
|
width: maxContentWidth,
|
|
46
45
|
padding,
|
|
47
46
|
}
|
|
48
|
-
|
|
49
47
|
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
+
import { attachMediaListener, isMediaQuery } from '../tools'
|
|
3
|
+
|
|
4
|
+
export interface UseMediaQueryOptions {
|
|
5
|
+
getInitialValueInEffect?: boolean
|
|
6
|
+
initialValue?: boolean
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useMediaQuery(
|
|
10
|
+
query: string,
|
|
11
|
+
queryOptions: UseMediaQueryOptions = {},
|
|
12
|
+
) {
|
|
13
|
+
const {
|
|
14
|
+
initialValue = false,
|
|
15
|
+
getInitialValueInEffect = true,
|
|
16
|
+
} = queryOptions
|
|
17
|
+
|
|
18
|
+
const _query = useMemo(() => {
|
|
19
|
+
return query.trim().replace('@media screen and ', '')
|
|
20
|
+
}, [query])
|
|
21
|
+
|
|
22
|
+
const [matches, setMatches] = useState(
|
|
23
|
+
getInitialValueInEffect ? initialValue : isMediaQuery(query, initialValue),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
const queryRef = useRef<MediaQueryList>()
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (query.trim() === '') return
|
|
30
|
+
|
|
31
|
+
if ('matchMedia' in window) {
|
|
32
|
+
queryRef.current = window.matchMedia(_query)
|
|
33
|
+
setMatches(queryRef.current.matches)
|
|
34
|
+
return attachMediaListener(queryRef.current, (event) => setMatches(event.matches))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return undefined
|
|
38
|
+
}, [_query])
|
|
39
|
+
|
|
40
|
+
return matches
|
|
41
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { globalHistory } from '@reach/router'
|
|
2
|
+
import { useEffect } from 'react'
|
|
3
|
+
|
|
4
|
+
export const usePageExitBlocker = (
|
|
5
|
+
handler: (willLeavePage: boolean) => void,
|
|
6
|
+
deps: Array<any> = [],
|
|
7
|
+
message = 'Are you sure you want to leave?',
|
|
8
|
+
) => {
|
|
9
|
+
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
10
|
+
if (!event) return null
|
|
11
|
+
|
|
12
|
+
event?.preventDefault()
|
|
13
|
+
event.returnValue = ''
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (!window) return null
|
|
19
|
+
|
|
20
|
+
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
21
|
+
|
|
22
|
+
return () => {
|
|
23
|
+
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
24
|
+
}
|
|
25
|
+
}, deps)
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
return globalHistory.listen((args) => {
|
|
29
|
+
if (!window) return null
|
|
30
|
+
|
|
31
|
+
const historyPathname = args?.location?.pathname
|
|
32
|
+
const windowPathname = window?.location?.pathname
|
|
33
|
+
|
|
34
|
+
const isPopAction = args?.action === 'POP'
|
|
35
|
+
const isLeaveAction = args?.action === 'PUSH' && !historyPathname?.includes(windowPathname)
|
|
36
|
+
|
|
37
|
+
if (isLeaveAction || isPopAction) {
|
|
38
|
+
const willLeavePage = window.confirm(message)
|
|
39
|
+
|
|
40
|
+
handler?.(willLeavePage)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
}, deps)
|
|
44
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { range, useMemo, useUncontrolled } from '@codeleap/common'
|
|
2
|
+
|
|
3
|
+
export interface PaginationParams {
|
|
4
|
+
/** Page selected on initial render, defaults to 1 */
|
|
5
|
+
initialPage?: number
|
|
6
|
+
|
|
7
|
+
/** Controlled active page number */
|
|
8
|
+
page?: number
|
|
9
|
+
|
|
10
|
+
/** Total amount of pages */
|
|
11
|
+
total: number
|
|
12
|
+
|
|
13
|
+
/** Siblings amount on left/right side of selected page, defaults to 1 */
|
|
14
|
+
siblings?: number
|
|
15
|
+
|
|
16
|
+
/** Amount of elements visible on left/right edges, defaults to 1 */
|
|
17
|
+
boundaries?: number
|
|
18
|
+
|
|
19
|
+
/** Callback fired after change of each page */
|
|
20
|
+
onChange?: (page: number) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function usePagination({
|
|
24
|
+
total,
|
|
25
|
+
siblings = 1,
|
|
26
|
+
boundaries = 1,
|
|
27
|
+
page,
|
|
28
|
+
initialPage = 1,
|
|
29
|
+
onChange,
|
|
30
|
+
}: PaginationParams) {
|
|
31
|
+
const [activePage, setActivePage] = useUncontrolled({
|
|
32
|
+
value: page,
|
|
33
|
+
onChange,
|
|
34
|
+
defaultValue: initialPage,
|
|
35
|
+
finalValue: initialPage,
|
|
36
|
+
rule: (_page) => typeof _page === 'number' && _page <= total,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const setPage = (pageNumber: number) => {
|
|
40
|
+
if (pageNumber <= 0) {
|
|
41
|
+
setActivePage(1)
|
|
42
|
+
} else if (pageNumber > total) {
|
|
43
|
+
setActivePage(total)
|
|
44
|
+
} else {
|
|
45
|
+
setActivePage(pageNumber)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const next = () => setPage(activePage + 1)
|
|
50
|
+
const previous = () => setPage(activePage - 1)
|
|
51
|
+
const first = () => setPage(1)
|
|
52
|
+
const last = () => setPage(total)
|
|
53
|
+
|
|
54
|
+
const DOTS = 'dots'
|
|
55
|
+
|
|
56
|
+
const paginationRange = useMemo((): (number | 'dots')[] => {
|
|
57
|
+
// Pages count is determined as siblings (left/right) + boundaries(left/right) + currentPage + 2*DOTS
|
|
58
|
+
const totalPageNumbers = siblings * 2 + 3 + boundaries * 2
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* If the number of pages is less than the page numbers we want to show in our
|
|
62
|
+
* paginationComponent, we return the range [1..total]
|
|
63
|
+
*/
|
|
64
|
+
if (totalPageNumbers >= total) {
|
|
65
|
+
return range(1, total)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const leftSiblingIndex = Math.max(activePage - siblings, boundaries)
|
|
69
|
+
const rightSiblingIndex = Math.min(activePage + siblings, total - boundaries)
|
|
70
|
+
|
|
71
|
+
/*
|
|
72
|
+
* We do not want to show dots if there is only one position left
|
|
73
|
+
* after/before the left/right page count as that would lead to a change if our Pagination
|
|
74
|
+
* component size which we do not want
|
|
75
|
+
*/
|
|
76
|
+
const shouldShowLeftDots = leftSiblingIndex > boundaries + 2
|
|
77
|
+
const shouldShowRightDots = rightSiblingIndex < total - (boundaries + 1)
|
|
78
|
+
|
|
79
|
+
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
80
|
+
const leftItemCount = siblings * 2 + boundaries + 2
|
|
81
|
+
return [...range(1, leftItemCount), DOTS, ...range(total - (boundaries - 1), total)]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
85
|
+
const rightItemCount = boundaries + 1 + 2 * siblings
|
|
86
|
+
return [...range(1, boundaries), DOTS, ...range(total - rightItemCount, total)]
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
...range(1, boundaries),
|
|
91
|
+
DOTS,
|
|
92
|
+
...range(leftSiblingIndex, rightSiblingIndex),
|
|
93
|
+
DOTS,
|
|
94
|
+
...range(total - boundaries + 1, total),
|
|
95
|
+
]
|
|
96
|
+
}, [total, siblings, activePage])
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
range: paginationRange,
|
|
100
|
+
active: activePage,
|
|
101
|
+
setPage,
|
|
102
|
+
next,
|
|
103
|
+
previous,
|
|
104
|
+
first,
|
|
105
|
+
last,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useScrollEffect(
|
|
4
|
+
effect: (passed: boolean, current: number) => any,
|
|
5
|
+
breakpoint: number,
|
|
6
|
+
extraDependencies = [],
|
|
7
|
+
) {
|
|
8
|
+
function handleScroll() {
|
|
9
|
+
const passed = window.scrollY > breakpoint
|
|
10
|
+
effect(passed, window.scrollY)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
document.addEventListener('scroll', handleScroll)
|
|
15
|
+
return () => {
|
|
16
|
+
document.removeEventListener('scroll', handleScroll)
|
|
17
|
+
}
|
|
18
|
+
}, [breakpoint, ...extraDependencies])
|
|
19
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useRef } from 'react'
|
|
2
|
+
import { SelectProperties } from '../../types'
|
|
3
|
+
|
|
4
|
+
export function useStaticAnimationStyles<T extends Record<string | number | symbol, any>, K extends keyof T>(obj: T, keys: K[]) {
|
|
5
|
+
const styles = useRef({})
|
|
6
|
+
|
|
7
|
+
if (Object.keys(styles.current).length === 0) {
|
|
8
|
+
const mappedStyles = keys.map((k) => [k, { ...obj[k] }])
|
|
9
|
+
|
|
10
|
+
styles.current = Object.fromEntries(mappedStyles)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return styles.current as SelectProperties<T, K>
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AnyFunction, TypeGuards } from '@codeleap/common'
|
|
2
|
+
import { useEffect, useState } from 'react'
|
|
3
|
+
|
|
4
|
+
type UseWindowFocusOptions = {
|
|
5
|
+
onFocus?: AnyFunction
|
|
6
|
+
onBlur?: AnyFunction
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const useWindowFocus = (options: UseWindowFocusOptions = {}, deps: Array<any> = []): boolean => {
|
|
10
|
+
const [focused, setFocused] = useState(true)
|
|
11
|
+
|
|
12
|
+
const onFocus = () => {
|
|
13
|
+
setFocused(true)
|
|
14
|
+
if (TypeGuards.isFunction(options?.onFocus)) options?.onFocus()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const onBlur = () => {
|
|
18
|
+
setFocused(false)
|
|
19
|
+
if (TypeGuards.isFunction(options?.onBlur)) options?.onBlur()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
window.addEventListener('focus', onFocus)
|
|
24
|
+
window.addEventListener('blur', onBlur)
|
|
25
|
+
|
|
26
|
+
return () => {
|
|
27
|
+
window.removeEventListener('focus', onFocus)
|
|
28
|
+
window.removeEventListener('blur', onBlur)
|
|
29
|
+
}
|
|
30
|
+
}, deps)
|
|
31
|
+
|
|
32
|
+
return focused
|
|
33
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useWindowSize() {
|
|
4
|
+
const [size, setSize] = useState([])
|
|
5
|
+
|
|
6
|
+
function handleResize() {
|
|
7
|
+
setSize([window.innerWidth, window.innerHeight])
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
handleResize()
|
|
12
|
+
|
|
13
|
+
window.addEventListener('resize', handleResize)
|
|
14
|
+
|
|
15
|
+
return () => {
|
|
16
|
+
window.removeEventListener('resize', handleResize)
|
|
17
|
+
}
|
|
18
|
+
}, [])
|
|
19
|
+
|
|
20
|
+
return size
|
|
21
|
+
}
|
package/src/lib/index.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
export * from './utils'
|
|
2
1
|
export * from './hooks'
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
5
|
-
export * from './useBreakpointMatch'
|
|
6
|
-
export * from './useClick'
|
|
2
|
+
export * from './tools'
|
|
3
|
+
export * from './utils'
|
|
7
4
|
export * from './ListMasonry'
|
|
8
|
-
export * from './localStorage'
|
|
9
|
-
export * from './useAnimatedStyle'
|
|
10
|
-
export * from './navigation'
|
|
11
|
-
export * from './modal'
|
|
12
|
-
export * from './keyboard'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { useEffect, useState } from 'react'
|
|
2
|
+
import { TypeGuards } from '@codeleap/common'
|
|
3
3
|
|
|
4
4
|
export type LocalStorageHandler<T> = (key: T, event: StorageEvent, value: any) => void
|
|
5
5
|
export type Key<T> = keyof T
|
|
@@ -138,7 +138,7 @@ export class LocalStorage<T extends Record<string, string>> {
|
|
|
138
138
|
return getItemValueOnMount ? (this.getItem(key, parseValueOnGet) ?? initialValue) : initialValue
|
|
139
139
|
})
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
useEffect(() => {
|
|
142
142
|
const handler = () => {
|
|
143
143
|
let _initialValue = initialValue
|
|
144
144
|
let storedValue = this.getItem(key, parseValueOnGet)
|
|
@@ -153,7 +153,7 @@ export class LocalStorage<T extends Record<string, string>> {
|
|
|
153
153
|
handler()
|
|
154
154
|
|
|
155
155
|
return disableListen ? null : this.listen(key, handler)
|
|
156
|
-
})
|
|
156
|
+
}, [])
|
|
157
157
|
|
|
158
158
|
const setValue = (to: S | ((prev:S) => S)) => {
|
|
159
159
|
return _setValue((prev) => {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
type MediaQueryCallback = (event: { matches: boolean; media: string }) => void
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Older versions of Safari (shipped withCatalina and before) do not support addEventListener on matchMedia
|
|
5
|
+
* https://stackoverflow.com/questions/56466261/matchmedia-addlistener-marked-as-deprecated-addeventlistener-equivalent
|
|
6
|
+
* */
|
|
7
|
+
export function attachMediaListener(query: MediaQueryList, callback: MediaQueryCallback) {
|
|
8
|
+
try {
|
|
9
|
+
query.addEventListener('change', callback)
|
|
10
|
+
return () => query.removeEventListener('change', callback)
|
|
11
|
+
} catch (e) {
|
|
12
|
+
query.addListener(callback)
|
|
13
|
+
return () => query.removeListener(callback)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function isMediaQuery(query: string, initialValue = false) {
|
|
18
|
+
const media = query.trim().replace('@media screen and ', '')
|
|
19
|
+
|
|
20
|
+
if (typeof window !== 'undefined' && 'matchMedia' in window) {
|
|
21
|
+
return window.matchMedia(media).matches
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return initialValue
|
|
25
|
+
}
|
package/src/lib/utils/index.ts
CHANGED
package/src/types/utility.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import { CSSInterpolation } from '@emotion/css'
|
|
3
|
-
import { PropsOf } from '@codeleap/common'
|
|
4
3
|
|
|
5
4
|
export type StylesOf<C extends string> = Partial<Record<C, CSSInterpolation>>
|
|
6
5
|
|
|
@@ -34,3 +33,7 @@ export type ComponentWithDefaultProps<P> = ((props: P) => JSX.Element) & { defau
|
|
|
34
33
|
export type ComponentCommonProps = {
|
|
35
34
|
debugName: string
|
|
36
35
|
}
|
|
36
|
+
|
|
37
|
+
export type SelectProperties<T extends Record<string | number | symbol, any>, K extends keyof T> = {
|
|
38
|
+
[P in K]: T[K]
|
|
39
|
+
}
|
package/src/lib/Toast.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { toast } from 'react-toastify'
|
|
2
|
-
|
|
3
|
-
type ToastArgs = {
|
|
4
|
-
title:string
|
|
5
|
-
} &Parameters<typeof toast.info>[1]
|
|
6
|
-
|
|
7
|
-
function info({ title, ...others }:ToastArgs) {
|
|
8
|
-
toast.info(title, others)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function success({ title, ...others }:ToastArgs) {
|
|
12
|
-
toast.success(title, others)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function error({ title, ...others }:ToastArgs) {
|
|
16
|
-
toast.error(title, others)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export default {
|
|
20
|
-
info,
|
|
21
|
-
success,
|
|
22
|
-
error,
|
|
23
|
-
}
|
package/src/lib/hooks.ts
DELETED
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
import { AnyFunction, onMount, onUpdate, range, TypeGuards, useUncontrolled } from '@codeleap/common'
|
|
2
|
-
import React, { useCallback, useMemo, useRef, useState } from 'react'
|
|
3
|
-
import { v4 } from 'uuid'
|
|
4
|
-
import { easeInOut, EasingFunction, AnimationProps, useAnimate, useAnimation, animate } from 'framer-motion'
|
|
5
|
-
import { globalHistory } from '@reach/router'
|
|
6
|
-
|
|
7
|
-
export function useWindowSize() {
|
|
8
|
-
const [size, setSize] = useState([])
|
|
9
|
-
|
|
10
|
-
onMount(() => {
|
|
11
|
-
setSize([window.innerWidth, window.innerHeight])
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
function handleResize() {
|
|
15
|
-
setSize([window.innerWidth, window.innerHeight])
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
onUpdate(() => {
|
|
19
|
-
window.addEventListener('resize', handleResize)
|
|
20
|
-
return () => {
|
|
21
|
-
window.removeEventListener('resize', handleResize)
|
|
22
|
-
}
|
|
23
|
-
}, [])
|
|
24
|
-
|
|
25
|
-
return size
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// type UseClickOutsideOpts = {
|
|
29
|
-
// customId?: string
|
|
30
|
-
// deps?: any[]
|
|
31
|
-
// }
|
|
32
|
-
// export function useClickOutside(
|
|
33
|
-
// callback: AnyFunction,
|
|
34
|
-
// deps?: UseClickOutsideOpts,
|
|
35
|
-
// ) {
|
|
36
|
-
// const id = useRef(deps?.customId || v4())
|
|
37
|
-
|
|
38
|
-
// const onClick = useCallback((e: Event) => {
|
|
39
|
-
// const element = document.getElementById(id.current)
|
|
40
|
-
// if (!element) return
|
|
41
|
-
// const isInside = element.contains(e.target as Node) || ((e.target as HTMLElement).id === id.current)
|
|
42
|
-
|
|
43
|
-
// // const iterNodes = (el:HTMLElement|Element) => {
|
|
44
|
-
// // if (isInside) return
|
|
45
|
-
// // for (let i = 0; i < el.children.length; i++) {
|
|
46
|
-
// // const node = el.children.item(i)
|
|
47
|
-
|
|
48
|
-
// // if (!node) return
|
|
49
|
-
// // const _isInside = node.contains(e.target as Node)
|
|
50
|
-
// // if (_isInside) {
|
|
51
|
-
// // isInside = _isInside
|
|
52
|
-
// // }
|
|
53
|
-
// // if (isInside) break
|
|
54
|
-
|
|
55
|
-
// // if (node.hasChildNodes()) {
|
|
56
|
-
// // iterNodes(node)
|
|
57
|
-
// // }
|
|
58
|
-
|
|
59
|
-
// // if (isInside) break
|
|
60
|
-
// // }
|
|
61
|
-
// // }
|
|
62
|
-
|
|
63
|
-
// // if (!isInside) {
|
|
64
|
-
// // iterNodes(element)
|
|
65
|
-
// // }
|
|
66
|
-
|
|
67
|
-
// if (!isInside) {
|
|
68
|
-
// callback(e)
|
|
69
|
-
// }
|
|
70
|
-
// }, deps?.deps || [])
|
|
71
|
-
// onUpdate(() => {
|
|
72
|
-
|
|
73
|
-
// document.addEventListener('click', onClick)
|
|
74
|
-
// return () => {
|
|
75
|
-
// document.removeEventListener('click', onClick)
|
|
76
|
-
// }
|
|
77
|
-
// }, [onClick])
|
|
78
|
-
|
|
79
|
-
// return id.current
|
|
80
|
-
// }
|
|
81
|
-
|
|
82
|
-
import { useEffect } from 'react'
|
|
83
|
-
|
|
84
|
-
const DEFAULT_EVENTS = ['mousedown', 'touchstart']
|
|
85
|
-
|
|
86
|
-
export function useClickOutside<T extends HTMLElement = any>(
|
|
87
|
-
handler: () => void,
|
|
88
|
-
events?: string[] | null,
|
|
89
|
-
nodes?: HTMLElement[],
|
|
90
|
-
) {
|
|
91
|
-
const ref = useRef<T>()
|
|
92
|
-
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
const listener = (event: any) => {
|
|
95
|
-
if (Array.isArray(nodes)) {
|
|
96
|
-
const shouldIgnore = event?.target?.hasAttribute('data-ignore-outside-clicks')
|
|
97
|
-
const shouldTrigger = nodes.every((node) => !!node && !node.contains(event.target))
|
|
98
|
-
shouldTrigger && !shouldIgnore && handler()
|
|
99
|
-
} else if (ref.current && !ref.current.contains(event.target)) {
|
|
100
|
-
handler()
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
(events || DEFAULT_EVENTS).forEach((fn) => document.addEventListener(fn, listener))
|
|
105
|
-
|
|
106
|
-
return () => {
|
|
107
|
-
(events || DEFAULT_EVENTS).forEach((fn) => document.removeEventListener(fn, listener))
|
|
108
|
-
}
|
|
109
|
-
}, [ref, handler, nodes])
|
|
110
|
-
|
|
111
|
-
return ref
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function useScrollEffect(
|
|
115
|
-
effect: (passed: boolean, current: number) => any,
|
|
116
|
-
breakpoint: number,
|
|
117
|
-
extraDependencies = [],
|
|
118
|
-
) {
|
|
119
|
-
function handleScroll() {
|
|
120
|
-
const passed = window.scrollY > breakpoint
|
|
121
|
-
effect(passed, window.scrollY)
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
onUpdate(() => {
|
|
125
|
-
document.addEventListener('scroll', handleScroll)
|
|
126
|
-
return () => {
|
|
127
|
-
document.removeEventListener('scroll', handleScroll)
|
|
128
|
-
}
|
|
129
|
-
}, [breakpoint, ...extraDependencies])
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export const DOTS = 'dots'
|
|
133
|
-
|
|
134
|
-
export interface PaginationParams {
|
|
135
|
-
/** Page selected on initial render, defaults to 1 */
|
|
136
|
-
initialPage?: number
|
|
137
|
-
|
|
138
|
-
/** Controlled active page number */
|
|
139
|
-
page?: number
|
|
140
|
-
|
|
141
|
-
/** Total amount of pages */
|
|
142
|
-
total: number
|
|
143
|
-
|
|
144
|
-
/** Siblings amount on left/right side of selected page, defaults to 1 */
|
|
145
|
-
siblings?: number
|
|
146
|
-
|
|
147
|
-
/** Amount of elements visible on left/right edges, defaults to 1 */
|
|
148
|
-
boundaries?: number
|
|
149
|
-
|
|
150
|
-
/** Callback fired after change of each page */
|
|
151
|
-
onChange?: (page: number) => void
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
export function usePagination({
|
|
155
|
-
total,
|
|
156
|
-
siblings = 1,
|
|
157
|
-
boundaries = 1,
|
|
158
|
-
page,
|
|
159
|
-
initialPage = 1,
|
|
160
|
-
onChange,
|
|
161
|
-
}: PaginationParams) {
|
|
162
|
-
const [activePage, setActivePage] = useUncontrolled({
|
|
163
|
-
value: page,
|
|
164
|
-
onChange,
|
|
165
|
-
defaultValue: initialPage,
|
|
166
|
-
finalValue: initialPage,
|
|
167
|
-
rule: (_page) => typeof _page === 'number' && _page <= total,
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
const setPage = (pageNumber: number) => {
|
|
171
|
-
if (pageNumber <= 0) {
|
|
172
|
-
setActivePage(1)
|
|
173
|
-
} else if (pageNumber > total) {
|
|
174
|
-
setActivePage(total)
|
|
175
|
-
} else {
|
|
176
|
-
setActivePage(pageNumber)
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const next = () => setPage(activePage + 1)
|
|
181
|
-
const previous = () => setPage(activePage - 1)
|
|
182
|
-
const first = () => setPage(1)
|
|
183
|
-
const last = () => setPage(total)
|
|
184
|
-
|
|
185
|
-
const paginationRange = useMemo((): (number | 'dots')[] => {
|
|
186
|
-
// Pages count is determined as siblings (left/right) + boundaries(left/right) + currentPage + 2*DOTS
|
|
187
|
-
const totalPageNumbers = siblings * 2 + 3 + boundaries * 2
|
|
188
|
-
|
|
189
|
-
/*
|
|
190
|
-
* If the number of pages is less than the page numbers we want to show in our
|
|
191
|
-
* paginationComponent, we return the range [1..total]
|
|
192
|
-
*/
|
|
193
|
-
if (totalPageNumbers >= total) {
|
|
194
|
-
return range(1, total)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
const leftSiblingIndex = Math.max(activePage - siblings, boundaries)
|
|
198
|
-
const rightSiblingIndex = Math.min(activePage + siblings, total - boundaries)
|
|
199
|
-
|
|
200
|
-
/*
|
|
201
|
-
* We do not want to show dots if there is only one position left
|
|
202
|
-
* after/before the left/right page count as that would lead to a change if our Pagination
|
|
203
|
-
* component size which we do not want
|
|
204
|
-
*/
|
|
205
|
-
const shouldShowLeftDots = leftSiblingIndex > boundaries + 2
|
|
206
|
-
const shouldShowRightDots = rightSiblingIndex < total - (boundaries + 1)
|
|
207
|
-
|
|
208
|
-
if (!shouldShowLeftDots && shouldShowRightDots) {
|
|
209
|
-
const leftItemCount = siblings * 2 + boundaries + 2
|
|
210
|
-
return [...range(1, leftItemCount), DOTS, ...range(total - (boundaries - 1), total)]
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (shouldShowLeftDots && !shouldShowRightDots) {
|
|
214
|
-
const rightItemCount = boundaries + 1 + 2 * siblings
|
|
215
|
-
return [...range(1, boundaries), DOTS, ...range(total - rightItemCount, total)]
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return [
|
|
219
|
-
...range(1, boundaries),
|
|
220
|
-
DOTS,
|
|
221
|
-
...range(leftSiblingIndex, rightSiblingIndex),
|
|
222
|
-
DOTS,
|
|
223
|
-
...range(total - boundaries + 1, total),
|
|
224
|
-
]
|
|
225
|
-
}, [total, siblings, activePage])
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
range: paginationRange,
|
|
229
|
-
active: activePage,
|
|
230
|
-
setPage,
|
|
231
|
-
next,
|
|
232
|
-
previous,
|
|
233
|
-
first,
|
|
234
|
-
last,
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
export interface UseMediaQueryOptions {
|
|
240
|
-
getInitialValueInEffect?: boolean
|
|
241
|
-
initialValue?: boolean
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
type MediaQueryCallback = (event: { matches: boolean; media: string }) => void;
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Older versions of Safari (shipped withCatalina and before) do not support addEventListener on matchMedia
|
|
248
|
-
* https://stackoverflow.com/questions/56466261/matchmedia-addlistener-marked-as-deprecated-addeventlistener-equivalent
|
|
249
|
-
* */
|
|
250
|
-
export function attachMediaListener(query: MediaQueryList, callback: MediaQueryCallback) {
|
|
251
|
-
try {
|
|
252
|
-
query.addEventListener('change', callback);
|
|
253
|
-
return () => query.removeEventListener('change', callback);
|
|
254
|
-
} catch (e) {
|
|
255
|
-
query.addListener(callback);
|
|
256
|
-
return () => query.removeListener(callback);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function getInitialValue(query: string, initialValue?: boolean) {
|
|
261
|
-
if (typeof initialValue === 'boolean') {
|
|
262
|
-
return initialValue;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (typeof window !== 'undefined' && 'matchMedia' in window) {
|
|
266
|
-
return window.matchMedia(query).matches;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export function isMediaQuery(query: string, initialValue = false) {
|
|
273
|
-
const media = query.trim().replace('@media screen and ', '')
|
|
274
|
-
|
|
275
|
-
if (typeof window !== 'undefined' && 'matchMedia' in window) {
|
|
276
|
-
return window.matchMedia(media).matches
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return initialValue
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export function useMediaQuery(
|
|
283
|
-
query: string,
|
|
284
|
-
queryOptions: UseMediaQueryOptions = {}
|
|
285
|
-
) {
|
|
286
|
-
const {
|
|
287
|
-
initialValue = false,
|
|
288
|
-
getInitialValueInEffect = true,
|
|
289
|
-
} = queryOptions
|
|
290
|
-
|
|
291
|
-
const _query = useMemo(() => {
|
|
292
|
-
return query.trim().replace('@media screen and ', '')
|
|
293
|
-
}, [query])
|
|
294
|
-
|
|
295
|
-
const [matches, setMatches] = useState(
|
|
296
|
-
getInitialValueInEffect ? initialValue : isMediaQuery(query, initialValue)
|
|
297
|
-
)
|
|
298
|
-
|
|
299
|
-
const queryRef = useRef<MediaQueryList>()
|
|
300
|
-
|
|
301
|
-
useEffect(() => {
|
|
302
|
-
if(query.trim() === '') return
|
|
303
|
-
|
|
304
|
-
if ('matchMedia' in window) {
|
|
305
|
-
queryRef.current = window.matchMedia(_query);
|
|
306
|
-
setMatches(queryRef.current.matches);
|
|
307
|
-
return attachMediaListener(queryRef.current, (event) => setMatches(event.matches));
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return undefined
|
|
311
|
-
}, [_query])
|
|
312
|
-
|
|
313
|
-
return matches
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
type SelectProperties<T extends Record<string|number|symbol, any>, K extends keyof T> = {
|
|
317
|
-
[P in K] : T[K]
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
export function useStaticAnimationStyles<T extends Record<string|number|symbol, any>, K extends keyof T >(obj: T, keys: K[]) {
|
|
321
|
-
const styles = useRef({})
|
|
322
|
-
|
|
323
|
-
if (Object.keys(styles.current).length === 0) {
|
|
324
|
-
const mappedStyles = keys.map((k) => [k, { ...obj[k] }])
|
|
325
|
-
|
|
326
|
-
styles.current = Object.fromEntries(mappedStyles)
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
return styles.current as SelectProperties<T, K>
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
type UseAnimatedVariantStylesConfig<T extends Record<string|number|symbol, any>, K extends keyof T > = {
|
|
333
|
-
variantStyles: T
|
|
334
|
-
animatedProperties: K[]
|
|
335
|
-
updater: (states: SelectProperties<T, K>) => AnimationProps
|
|
336
|
-
dependencies?: any[]
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
export function useAnimatedVariantStyles<T extends Record<string|number|symbol, any>, K extends keyof T >(config: UseAnimatedVariantStylesConfig<T, K>) {
|
|
340
|
-
const { animatedProperties, updater, variantStyles, dependencies = [] } = config
|
|
341
|
-
|
|
342
|
-
const staticStyles = useStaticAnimationStyles(variantStyles, animatedProperties)
|
|
343
|
-
|
|
344
|
-
const initialState = updater(staticStyles)
|
|
345
|
-
|
|
346
|
-
const [animated, setAnimated] = useState(initialState)
|
|
347
|
-
|
|
348
|
-
onUpdate(() => {
|
|
349
|
-
const nextState = updater(staticStyles)
|
|
350
|
-
|
|
351
|
-
setAnimated(nextState)
|
|
352
|
-
}, dependencies)
|
|
353
|
-
|
|
354
|
-
return animated
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
type UseWindowFocusOptions = {
|
|
358
|
-
onFocus?: AnyFunction
|
|
359
|
-
onBlur?: AnyFunction
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
export const useWindowFocus = (options: UseWindowFocusOptions = {}, deps: Array<any> = []): boolean => {
|
|
363
|
-
const [focused, setFocused] = useState(true)
|
|
364
|
-
|
|
365
|
-
const onFocus = () => {
|
|
366
|
-
setFocused(true)
|
|
367
|
-
if (TypeGuards.isFunction(options?.onFocus)) options?.onFocus()
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const onBlur = () => {
|
|
371
|
-
setFocused(false)
|
|
372
|
-
if (TypeGuards.isFunction(options?.onBlur)) options?.onBlur()
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
useEffect(() => {
|
|
376
|
-
window.addEventListener('focus', onFocus)
|
|
377
|
-
window.addEventListener('blur', onBlur)
|
|
378
|
-
|
|
379
|
-
return () => {
|
|
380
|
-
window.removeEventListener('focus', onFocus)
|
|
381
|
-
window.removeEventListener('blur', onBlur)
|
|
382
|
-
}
|
|
383
|
-
}, deps)
|
|
384
|
-
|
|
385
|
-
return focused
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
export const usePageExitBlocker = (
|
|
389
|
-
handler: (willLeavePage: boolean) => void,
|
|
390
|
-
deps: Array<any> = [],
|
|
391
|
-
message: string = 'Are you sure you want to leave?'
|
|
392
|
-
) => {
|
|
393
|
-
const handleBeforeUnload = (event: BeforeUnloadEvent) => {
|
|
394
|
-
if (!event) return null
|
|
395
|
-
|
|
396
|
-
event?.preventDefault()
|
|
397
|
-
event.returnValue = ''
|
|
398
|
-
return
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
React.useEffect(() => {
|
|
402
|
-
if (!window) return null
|
|
403
|
-
|
|
404
|
-
window.addEventListener('beforeunload', handleBeforeUnload)
|
|
405
|
-
|
|
406
|
-
return () => {
|
|
407
|
-
window.removeEventListener('beforeunload', handleBeforeUnload)
|
|
408
|
-
}
|
|
409
|
-
}, deps)
|
|
410
|
-
|
|
411
|
-
React.useEffect(() => {
|
|
412
|
-
return globalHistory.listen((args) => {
|
|
413
|
-
if (!window) return null
|
|
414
|
-
|
|
415
|
-
const historyPathname = args?.location?.pathname
|
|
416
|
-
const windowPathname = window?.location?.pathname
|
|
417
|
-
|
|
418
|
-
const isPopAction = args?.action === 'POP'
|
|
419
|
-
const isLeaveAction = args?.action === 'PUSH' && !historyPathname?.includes(windowPathname)
|
|
420
|
-
|
|
421
|
-
if (isLeaveAction || isPopAction) {
|
|
422
|
-
const willLeavePage = window.confirm(message)
|
|
423
|
-
|
|
424
|
-
handler?.(willLeavePage)
|
|
425
|
-
}
|
|
426
|
-
})
|
|
427
|
-
}, deps)
|
|
428
|
-
}
|
package/src/lib/utils/cookies.ts
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|