@contember/react-utils 1.2.0-rc.9 → 1.2.1
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/dist/development/hooks/useAutoHeightTextArea.js +41 -0
- package/dist/development/hooks/useAutoHeightTextArea.js.map +1 -0
- package/dist/development/hooks/useChildrenAsLabel.js +53 -0
- package/dist/development/hooks/useChildrenAsLabel.js.map +1 -0
- package/dist/development/hooks/useContainerWidth.js +1 -1
- package/dist/development/hooks/useContainerWidth.js.map +1 -1
- package/dist/development/hooks/useElementTopOffset.js +29 -0
- package/dist/development/hooks/useElementTopOffset.js.map +1 -0
- package/dist/development/hooks/useExpectSameValueReference.js +1 -1
- package/dist/development/hooks/useExpectSameValueReference.js.map +1 -1
- package/dist/development/hooks/useId.js +2 -2
- package/dist/development/hooks/useId.js.map +1 -1
- package/dist/development/hooks/useOnElementClickOutsideCallback.js +20 -0
- package/dist/development/hooks/useOnElementClickOutsideCallback.js.map +1 -0
- package/dist/development/hooks/useOnElementMouseEnterDelayedCallback.js +33 -0
- package/dist/development/hooks/useOnElementMouseEnterDelayedCallback.js.map +1 -0
- package/dist/development/hooks/useOnElementMutation.js +1 -1
- package/dist/development/hooks/useOnElementMutation.js.map +1 -1
- package/dist/development/hooks/useOnElementResize.js +28 -26
- package/dist/development/hooks/useOnElementResize.js.map +1 -1
- package/dist/development/hooks/useOnScrollWithin.js +33 -31
- package/dist/development/hooks/useOnScrollWithin.js.map +1 -1
- package/dist/development/index.js +13 -0
- package/dist/development/index.js.map +1 -1
- package/dist/development/theming/useClassNameFactory.js +4 -2
- package/dist/development/theming/useClassNameFactory.js.map +1 -1
- package/dist/development/theming/useThemedClassNameFactory.js +4 -2
- package/dist/development/theming/useThemedClassNameFactory.js.map +1 -1
- package/dist/production/hooks/useAutoHeightTextArea.js +41 -0
- package/dist/production/hooks/useAutoHeightTextArea.js.map +1 -0
- package/dist/production/hooks/useChildrenAsLabel.js +52 -0
- package/dist/production/hooks/useChildrenAsLabel.js.map +1 -0
- package/dist/production/hooks/useContainerWidth.js +1 -1
- package/dist/production/hooks/useContainerWidth.js.map +1 -1
- package/dist/production/hooks/useElementTopOffset.js +29 -0
- package/dist/production/hooks/useElementTopOffset.js.map +1 -0
- package/dist/production/hooks/useExpectSameValueReference.js +1 -1
- package/dist/production/hooks/useExpectSameValueReference.js.map +1 -1
- package/dist/production/hooks/useId.js +2 -2
- package/dist/production/hooks/useId.js.map +1 -1
- package/dist/production/hooks/useOnElementClickOutsideCallback.js +20 -0
- package/dist/production/hooks/useOnElementClickOutsideCallback.js.map +1 -0
- package/dist/production/hooks/useOnElementMouseEnterDelayedCallback.js +33 -0
- package/dist/production/hooks/useOnElementMouseEnterDelayedCallback.js.map +1 -0
- package/dist/production/hooks/useOnElementMutation.js +1 -1
- package/dist/production/hooks/useOnElementMutation.js.map +1 -1
- package/dist/production/hooks/useOnElementResize.js +28 -26
- package/dist/production/hooks/useOnElementResize.js.map +1 -1
- package/dist/production/hooks/useOnScrollWithin.js +33 -31
- package/dist/production/hooks/useOnScrollWithin.js.map +1 -1
- package/dist/production/index.js +13 -0
- package/dist/production/index.js.map +1 -1
- package/dist/production/theming/useClassNameFactory.js +4 -2
- package/dist/production/theming/useClassNameFactory.js.map +1 -1
- package/dist/production/theming/useThemedClassNameFactory.js +4 -2
- package/dist/production/theming/useThemedClassNameFactory.js.map +1 -1
- package/dist/types/hooks/index.d.ts +6 -0
- package/dist/types/hooks/index.d.ts.map +1 -1
- package/dist/types/hooks/useAutoHeightTextArea.d.ts +3 -0
- package/dist/types/hooks/useAutoHeightTextArea.d.ts.map +1 -0
- package/dist/types/hooks/useChildrenAsLabel.d.ts +18 -0
- package/dist/types/hooks/useChildrenAsLabel.d.ts.map +1 -0
- package/dist/types/hooks/useContainerWidth.d.ts +1 -1
- package/dist/types/hooks/useContainerWidth.d.ts.map +1 -1
- package/dist/types/hooks/useElementTopOffset.d.ts +3 -0
- package/dist/types/hooks/useElementTopOffset.d.ts.map +1 -0
- package/dist/types/hooks/useOnElementClickOutsideCallback.d.ts +3 -0
- package/dist/types/hooks/useOnElementClickOutsideCallback.d.ts.map +1 -0
- package/dist/types/hooks/useOnElementMouseEnterDelayedCallback.d.ts +14 -0
- package/dist/types/hooks/useOnElementMouseEnterDelayedCallback.d.ts.map +1 -0
- package/dist/types/hooks/useOnElementResize.d.ts.map +1 -1
- package/dist/types/hooks/useOnScrollWithin.d.ts.map +1 -1
- package/dist/types/theming/useClassNameFactory.d.ts.map +1 -1
- package/dist/types/theming/useThemedClassNameFactory.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -6
- package/src/hooks/index.ts +6 -0
- package/src/hooks/useAutoHeightTextArea.ts +51 -0
- package/src/hooks/useChildrenAsLabel.ts +72 -0
- package/src/hooks/useContainerWidth.ts +1 -1
- package/src/hooks/useElementTopOffset.ts +35 -0
- package/src/hooks/useExpectSameValueReference.ts +1 -1
- package/src/hooks/useId.ts +2 -2
- package/src/hooks/useOnElementClickOutsideCallback.ts +23 -0
- package/src/hooks/useOnElementMouseEnterDelayedCallback.ts +54 -0
- package/src/hooks/useOnElementMutation.ts +1 -1
- package/src/hooks/useOnElementResize.ts +34 -31
- package/src/hooks/useOnScrollWithin.ts +33 -32
- package/src/theming/useClassNameFactory.ts +4 -1
- package/src/theming/useThemedClassNameFactory.ts +4 -1
package/src/hooks/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export * from './unwrapRefValue'
|
|
|
2
2
|
export * from './useAbortController'
|
|
3
3
|
export * from './useAddClassNameDuringResize'
|
|
4
4
|
export * from './useArrayMapMemo'
|
|
5
|
+
export * from './useAutoHeightTextArea'
|
|
6
|
+
export * from './useChildrenAsLabel'
|
|
5
7
|
export * from './useComposeRef'
|
|
6
8
|
export * from './useConstantLengthInvariant'
|
|
7
9
|
export * from './useConstantValueInvariant'
|
|
@@ -10,14 +12,18 @@ export * from './useDebounce'
|
|
|
10
12
|
export * from './useDebounceCallback'
|
|
11
13
|
export * from './useDocumentTitle'
|
|
12
14
|
export * from './useElementSize'
|
|
15
|
+
export * from './useElementTopOffset'
|
|
13
16
|
export * from './useEventHandler'
|
|
14
17
|
export * from './useExpectSameValueReference'
|
|
15
18
|
export * from './useForceRender'
|
|
16
19
|
export * from './useId'
|
|
17
20
|
export * from './useIsMounted'
|
|
18
21
|
export * from './useObjectMemo'
|
|
22
|
+
export * from './useOnElementClickOutsideCallback'
|
|
23
|
+
export * from './useOnElementMouseEnterDelayedCallback'
|
|
19
24
|
export * from './useOnElementMutation'
|
|
20
25
|
export * from './useOnElementResize'
|
|
26
|
+
export * from './useOnScrollWithin'
|
|
21
27
|
export * from './useOnWindowResize'
|
|
22
28
|
export * from './usePreviousValue'
|
|
23
29
|
export * from './useScrollOffsets'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { px } from '@contember/utilities'
|
|
2
|
+
import { useCallback, useLayoutEffect } from 'react'
|
|
3
|
+
import { RefObjectOrElement, unwrapRefValue } from './unwrapRefValue'
|
|
4
|
+
import { useOnElementResize } from './useOnElementResize'
|
|
5
|
+
|
|
6
|
+
export const useAutoHeightTextArea = (
|
|
7
|
+
textAreaRef: RefObjectOrElement<HTMLTextAreaElement>,
|
|
8
|
+
value: string,
|
|
9
|
+
minRows: number,
|
|
10
|
+
maxRows: number,
|
|
11
|
+
) => {
|
|
12
|
+
const measure = useCallback((ref: HTMLTextAreaElement | null, minRows: number, maxRows: number, value: string) => {
|
|
13
|
+
if (ref) {
|
|
14
|
+
const rowsAttribute = ref.rows
|
|
15
|
+
|
|
16
|
+
ref.style.height = ''
|
|
17
|
+
ref.style.minHeight = ''
|
|
18
|
+
ref.style.maxHeight = ''
|
|
19
|
+
|
|
20
|
+
if (minRows !== maxRows) {
|
|
21
|
+
const height = ref.scrollHeight
|
|
22
|
+
let minHeight: number
|
|
23
|
+
let maxHeight: number
|
|
24
|
+
|
|
25
|
+
ref.rows = minRows
|
|
26
|
+
minHeight = ref.offsetHeight
|
|
27
|
+
|
|
28
|
+
if (maxRows === Infinity) {
|
|
29
|
+
maxHeight = Infinity
|
|
30
|
+
} else {
|
|
31
|
+
ref.rows = maxRows
|
|
32
|
+
maxHeight = ref.offsetHeight
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ref.style.height = px(height)
|
|
36
|
+
ref.style.minHeight = px(minHeight)
|
|
37
|
+
ref.style.maxHeight = px(maxHeight)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
ref.rows = rowsAttribute
|
|
41
|
+
}
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
useOnElementResize(textAreaRef, () => {
|
|
45
|
+
measure(unwrapRefValue(textAreaRef), minRows, maxRows, value)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
useLayoutEffect(() => {
|
|
49
|
+
measure(unwrapRefValue(textAreaRef), minRows, maxRows, value)
|
|
50
|
+
}, [maxRows, measure, minRows, textAreaRef, value])
|
|
51
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Children, ReactNode, useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
const MULTIPLE_SPACES_REG_EXP = / +/g
|
|
4
|
+
|
|
5
|
+
function normalize(text: string): string {
|
|
6
|
+
return text.replace(MULTIPLE_SPACES_REG_EXP, ' ').trim()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Walks through the children and returns a string representation of the text content.
|
|
11
|
+
*
|
|
12
|
+
* @param node - The children to walk through.
|
|
13
|
+
* @returns String representation of the text content or undefined if there is no text content.
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
export function getChildrenAsLabel(node: ReactNode): string | undefined {
|
|
17
|
+
if (typeof node === 'string' || typeof node === 'number') {
|
|
18
|
+
return String(node)
|
|
19
|
+
} else if (node === null || node === undefined || typeof node === 'boolean') {
|
|
20
|
+
return undefined
|
|
21
|
+
} else if (Array.isArray(node)) {
|
|
22
|
+
return normalize(node.map(getChildrenAsLabel).join(''))
|
|
23
|
+
} else {
|
|
24
|
+
let label: (string | number)[] = []
|
|
25
|
+
|
|
26
|
+
Children.map(node, child => {
|
|
27
|
+
if (child === null || child === undefined || typeof child === 'boolean') {
|
|
28
|
+
} else if (typeof child === 'string') {
|
|
29
|
+
label.push(child)
|
|
30
|
+
} else if (typeof child === 'number') {
|
|
31
|
+
label.push(child)
|
|
32
|
+
} else if (Array.isArray(child)) {
|
|
33
|
+
const childLabel = getChildrenAsLabel(child)
|
|
34
|
+
|
|
35
|
+
if (childLabel) {
|
|
36
|
+
label.push(childLabel)
|
|
37
|
+
}
|
|
38
|
+
} else {
|
|
39
|
+
// ReactElement | JSXElementConstructor | ReactPortal
|
|
40
|
+
if ('props' in child && 'children' in child.props) {
|
|
41
|
+
const childLabel = getChildrenAsLabel(child.props.children)
|
|
42
|
+
|
|
43
|
+
if (childLabel) {
|
|
44
|
+
label.push(childLabel)
|
|
45
|
+
}
|
|
46
|
+
} else if ('children' in child) {
|
|
47
|
+
// ReactPortal
|
|
48
|
+
const childLabel = getChildrenAsLabel(child.children)
|
|
49
|
+
|
|
50
|
+
if (childLabel) {
|
|
51
|
+
label.push(childLabel)
|
|
52
|
+
}
|
|
53
|
+
} else if (import.meta.env.DEV) {
|
|
54
|
+
console.warn('Unsupported child:', child)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
return label.length > 0 ? label.join('') : undefined
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Walks through the children and returns a string representation of the text content.
|
|
65
|
+
*
|
|
66
|
+
* @param node - The children to walk through.
|
|
67
|
+
* @returns String representation of the text content or undefined if there is no text content.
|
|
68
|
+
*
|
|
69
|
+
*/
|
|
70
|
+
export function useChildrenAsLabel(node: ReactNode): string | undefined {
|
|
71
|
+
return useMemo(() => getChildrenAsLabel(node), [node])
|
|
72
|
+
}
|
|
@@ -2,7 +2,7 @@ import { createContext, useContext } from 'react'
|
|
|
2
2
|
import { useWindowSize } from './useWindowSize'
|
|
3
3
|
|
|
4
4
|
export type ContainerWidthContextType = number
|
|
5
|
-
export const ContainerWidthContext = createContext<ContainerWidthContextType | null>(
|
|
5
|
+
export const ContainerWidthContext = createContext<ContainerWidthContextType | null | undefined>(undefined)
|
|
6
6
|
ContainerWidthContext.displayName = 'ContainerWidthContext'
|
|
7
7
|
|
|
8
8
|
export function useContainerWidth(): number {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { RefObject, useLayoutEffect, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export function useElementTopOffset(ref?: RefObject<HTMLElement>): number | undefined {
|
|
4
|
+
const [offsetTop, setOffsetTop] = useState<number | undefined>(undefined)
|
|
5
|
+
|
|
6
|
+
useLayoutEffect(() => {
|
|
7
|
+
if (!ref?.current) {
|
|
8
|
+
return
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const element = ref.current
|
|
12
|
+
|
|
13
|
+
function updateTopOffsetCallback() {
|
|
14
|
+
setOffsetTop(element.offsetTop)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function updateTopOffsetHandler() {
|
|
18
|
+
requestAnimationFrame(updateTopOffsetCallback)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
updateTopOffsetHandler()
|
|
22
|
+
|
|
23
|
+
const observer = new ResizeObserver(() => {
|
|
24
|
+
requestAnimationFrame(updateTopOffsetCallback)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
observer.observe(element)
|
|
28
|
+
|
|
29
|
+
return () => {
|
|
30
|
+
observer.unobserve(element)
|
|
31
|
+
}
|
|
32
|
+
}, [ref])
|
|
33
|
+
|
|
34
|
+
return offsetTop
|
|
35
|
+
}
|
package/src/hooks/useId.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useRef } from 'react'
|
|
1
|
+
import { useMemo, useRef } from 'react'
|
|
2
2
|
|
|
3
3
|
const count = { current: 360 }
|
|
4
4
|
|
|
@@ -17,5 +17,5 @@ function nextId() {
|
|
|
17
17
|
* @returns a unique id
|
|
18
18
|
*/
|
|
19
19
|
export function useId() {
|
|
20
|
-
return
|
|
20
|
+
return useMemo(() => nextId(), [])
|
|
21
21
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { RefObjectOrElement, unwrapRefValue } from './unwrapRefValue'
|
|
3
|
+
|
|
4
|
+
export function useOnElementClickOutsideCallback(
|
|
5
|
+
refOrElement: RefObjectOrElement<HTMLElement>,
|
|
6
|
+
callback: (event: MouseEvent) => void,
|
|
7
|
+
): void {
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const refValue = unwrapRefValue(refOrElement)
|
|
10
|
+
|
|
11
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
12
|
+
if (refValue && !refValue.contains(event.target as Node)) {
|
|
13
|
+
callback(event)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
document.addEventListener('mousedown', handleClickOutside)
|
|
18
|
+
|
|
19
|
+
return () => {
|
|
20
|
+
document.removeEventListener('mousedown', handleClickOutside)
|
|
21
|
+
}
|
|
22
|
+
}, [callback, refOrElement])
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
import { RefObjectOrElement, unwrapRefValue } from './unwrapRefValue'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Calls the callback when the mouse enters the element, but only after a timeout, e.g. for tooltips or dropdown menus.
|
|
6
|
+
*
|
|
7
|
+
* Calls the callback immediately if the mouse is pressed down with `event.type` of `mousedown`. This is useful for
|
|
8
|
+
* dropdown menus, where the user may click on the menu item before the callback is called resulting in the menu
|
|
9
|
+
* opening and closing immediately.
|
|
10
|
+
*
|
|
11
|
+
* @param refOrElement - The element or ref to the element to attach the event listener to.
|
|
12
|
+
* @param callback - The callback to call when the mouse enters the element.
|
|
13
|
+
* @param timeoutMs - The timeout in milliseconds to wait before calling the callback.
|
|
14
|
+
*/
|
|
15
|
+
export function useOnElementMouseEnterDelayedCallback(
|
|
16
|
+
refOrElement: RefObjectOrElement<HTMLElement>,
|
|
17
|
+
callback: (event: MouseEvent) => void,
|
|
18
|
+
timeoutMs: number = 300,
|
|
19
|
+
): void {
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const refValue = unwrapRefValue(refOrElement)
|
|
22
|
+
|
|
23
|
+
if (refValue) {
|
|
24
|
+
let timeoutID: ReturnType<typeof setTimeout> | undefined = undefined
|
|
25
|
+
|
|
26
|
+
function handleMouseEnter(event: MouseEvent) {
|
|
27
|
+
clearTimeout(timeoutID)
|
|
28
|
+
|
|
29
|
+
timeoutID = setTimeout(() => {
|
|
30
|
+
callback(event)
|
|
31
|
+
}, timeoutMs)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function handleMouseLeave() {
|
|
35
|
+
clearTimeout(timeoutID)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function handleMouseDown(event: MouseEvent) {
|
|
39
|
+
clearTimeout(timeoutID)
|
|
40
|
+
callback(event)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
refValue.addEventListener('mouseenter', handleMouseEnter)
|
|
44
|
+
refValue.addEventListener('mouseleave', handleMouseLeave)
|
|
45
|
+
refValue.addEventListener('mousedown', handleMouseDown)
|
|
46
|
+
|
|
47
|
+
return () => {
|
|
48
|
+
refValue.removeEventListener('mouseover', handleMouseEnter)
|
|
49
|
+
refValue.removeEventListener('mouseleave', handleMouseLeave)
|
|
50
|
+
refValue.removeEventListener('mousedown', handleMouseDown)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}, [callback, refOrElement, timeoutMs])
|
|
54
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import deepEqual from 'deep-equal'
|
|
1
|
+
import deepEqual from 'fast-deep-equal/es6/index.js'
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react'
|
|
3
3
|
import { useScopedConsoleRef } from '../debug-context'
|
|
4
4
|
import { RefObjectOrElement, unwrapRefValue } from './unwrapRefValue'
|
|
@@ -11,42 +11,45 @@ export function useOnElementResize(
|
|
|
11
11
|
const scopedConsoleRef = useScopedConsoleRef('useOnElementResize')
|
|
12
12
|
|
|
13
13
|
const { box = 'border-box' } = options
|
|
14
|
-
const element = unwrapRefValue(refOrElement)
|
|
15
14
|
const callbackRef = useRef(callback); callbackRef.current = callback
|
|
16
15
|
const lastTimeStamp = useRef<number>(0)
|
|
17
16
|
|
|
18
17
|
useLayoutEffect(() => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
18
|
+
const element = unwrapRefValue(refOrElement)
|
|
19
|
+
|
|
20
|
+
if (element) {
|
|
21
|
+
if (element instanceof HTMLElement) {
|
|
22
|
+
let timeoutID: number | undefined = undefined
|
|
23
|
+
|
|
24
|
+
function debouncedOnChange([entry]: ResizeObserverEntry[]) {
|
|
25
|
+
const timeStamp = Date.now()
|
|
26
|
+
const delta = timeStamp - lastTimeStamp.current
|
|
27
|
+
|
|
28
|
+
if (delta > timeout) {
|
|
29
|
+
scopedConsoleRef.current.warned('element.resize:immediate', null)
|
|
30
|
+
callbackRef.current(entry)
|
|
31
|
+
lastTimeStamp.current = timeStamp
|
|
32
|
+
} else {
|
|
33
|
+
clearTimeout(timeoutID)
|
|
34
|
+
timeoutID = setTimeout(() => {
|
|
35
|
+
scopedConsoleRef.current.warned('element.resize:debounced', null)
|
|
36
|
+
callbackRef.current(entry)
|
|
37
|
+
lastTimeStamp.current = timeStamp
|
|
38
|
+
}, timeout)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const resizeObserver = new ResizeObserver(debouncedOnChange)
|
|
43
|
+
|
|
44
|
+
resizeObserver.observe(element, { box })
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
clearTimeout(timeoutID)
|
|
48
|
+
resizeObserver.unobserve(element)
|
|
49
|
+
}
|
|
29
50
|
} else {
|
|
30
|
-
|
|
31
|
-
timeoutID = setTimeout(() => {
|
|
32
|
-
scopedConsoleRef.current.warned('element.resize:debounced', null)
|
|
33
|
-
callbackRef.current(entry)
|
|
34
|
-
lastTimeStamp.current = timeStamp
|
|
35
|
-
}, timeout)
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (element instanceof HTMLElement) {
|
|
40
|
-
const resizeObserver = new ResizeObserver(debouncedOnChange)
|
|
41
|
-
|
|
42
|
-
resizeObserver.observe(element, { box })
|
|
43
|
-
|
|
44
|
-
return () => {
|
|
45
|
-
clearTimeout(timeoutID)
|
|
46
|
-
resizeObserver.unobserve(element)
|
|
51
|
+
throw new Error('Exhaustive error: Expecting element to be instance of HTMLElement')
|
|
47
52
|
}
|
|
48
|
-
} else if (element) {
|
|
49
|
-
throw new Error('Exhaustive error: Expecting element to be instance of HTMLElement')
|
|
50
53
|
}
|
|
51
|
-
}, [box,
|
|
54
|
+
}, [box, refOrElement, scopedConsoleRef, timeout])
|
|
52
55
|
}
|
|
@@ -19,49 +19,50 @@ export function useOnScrollWithin(
|
|
|
19
19
|
const callbackRef = useRef(callback); callbackRef.current = callback
|
|
20
20
|
const lastTimeStamp = useRef<number>(0)
|
|
21
21
|
|
|
22
|
-
const element = unwrapRefValue(refOrElement)
|
|
23
|
-
|
|
24
22
|
useLayoutEffect(() => {
|
|
23
|
+
const element = unwrapRefValue(refOrElement)
|
|
24
|
+
|
|
25
25
|
let timeoutID: number | undefined = undefined
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
if (element) {
|
|
28
|
+
function debouncedHandler(event: Event) {
|
|
29
|
+
clearTimeout(timeoutID)
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
const delta = event.timeStamp - lastTimeStamp.current
|
|
32
|
+
scopedConsoleRef.current.logged('event:element.scroll:delta', delta)
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
callbackRef.current(event)
|
|
36
|
-
lastTimeStamp.current = event.timeStamp
|
|
37
|
-
} else {
|
|
38
|
-
timeoutID = setTimeout(() => {
|
|
39
|
-
scopedConsoleRef.current.warned('event:element.scroll:debounced', event)
|
|
40
|
-
lastTimeStamp.current = event.timeStamp
|
|
34
|
+
if (delta > interval) {
|
|
35
|
+
scopedConsoleRef.current.warned('event:element.scroll:immediately', event)
|
|
41
36
|
callbackRef.current(event)
|
|
42
|
-
|
|
37
|
+
lastTimeStamp.current = event.timeStamp
|
|
38
|
+
} else {
|
|
39
|
+
timeoutID = setTimeout(() => {
|
|
40
|
+
scopedConsoleRef.current.warned('event:element.scroll:debounced', event)
|
|
41
|
+
lastTimeStamp.current = event.timeStamp
|
|
42
|
+
callbackRef.current(event)
|
|
43
|
+
}, interval)
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
|
-
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
if (element instanceof HTMLElement) {
|
|
48
|
+
if (element instanceof HTMLBodyElement || element instanceof HTMLHtmlElement) {
|
|
49
|
+
window.addEventListener('scroll', debouncedHandler, { capture: true, passive: true })
|
|
50
|
+
window.addEventListener('resize', debouncedHandler)
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
return () => {
|
|
53
|
+
window.removeEventListener('scroll', debouncedHandler)
|
|
54
|
+
window.removeEventListener('resize', debouncedHandler)
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
element.addEventListener('scroll', debouncedHandler, { passive: true, capture: true })
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
return () => {
|
|
60
|
+
element.removeEventListener('scroll', debouncedHandler)
|
|
61
|
+
}
|
|
60
62
|
}
|
|
63
|
+
} else {
|
|
64
|
+
throw new Error('Exhaustive error: Expecting element to be instance of HTMLElement')
|
|
61
65
|
}
|
|
62
|
-
} else if (element) {
|
|
63
|
-
throw new Error('Exhaustive error: Expecting element to be instance of HTMLElement')
|
|
64
66
|
}
|
|
65
|
-
|
|
66
|
-
}, [element, interval, scopedConsoleRef])
|
|
67
|
+
}, [interval, refOrElement, scopedConsoleRef])
|
|
67
68
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NestedClassName, deduplicateClassName, flatClassNameList } from '@contember/utilities'
|
|
2
2
|
import { useContext } from 'react'
|
|
3
|
+
import { useReferentiallyStableCallback } from '../referentiallyStable'
|
|
3
4
|
import { GlobalClassNamePrefixContext } from './GlobalClassNamePrefixContext'
|
|
4
5
|
|
|
5
6
|
/**
|
|
@@ -28,7 +29,7 @@ export function useClassNameFactory(
|
|
|
28
29
|
componentClassName => `${classNamePrefix}${componentClassName}`,
|
|
29
30
|
)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
function componentClassNameFor(
|
|
32
33
|
suffix: string | null | undefined = null,
|
|
33
34
|
additionalClassName: NestedClassName = null,
|
|
34
35
|
): string {
|
|
@@ -44,4 +45,6 @@ export function useClassNameFactory(
|
|
|
44
45
|
flatClassNameList(additionalClassName),
|
|
45
46
|
)).join(' ')
|
|
46
47
|
}
|
|
48
|
+
|
|
49
|
+
return useReferentiallyStableCallback(componentClassNameFor)
|
|
47
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { NestedClassName, colorSchemeClassName, deduplicateClassName, filterThemedClassName, flatClassNameList } from '@contember/utilities'
|
|
2
2
|
import { useContext } from 'react'
|
|
3
|
+
import { useReferentiallyStableCallback } from '../referentiallyStable'
|
|
3
4
|
import { GlobalClassNamePrefixContext } from './GlobalClassNamePrefixContext'
|
|
4
5
|
import { useColorScheme } from './contexts'
|
|
5
6
|
|
|
@@ -17,7 +18,7 @@ export function useThemedClassNameFactory(
|
|
|
17
18
|
|
|
18
19
|
const colorScheme = colorSchemeClassName(useColorScheme())
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
function componentClassNameFor(
|
|
21
22
|
suffix: string | null | undefined = null,
|
|
22
23
|
additionalClassName: NestedClassName = null,
|
|
23
24
|
): string {
|
|
@@ -38,4 +39,6 @@ export function useThemedClassNameFactory(
|
|
|
38
39
|
),
|
|
39
40
|
)).join(' ')
|
|
40
41
|
}
|
|
42
|
+
|
|
43
|
+
return useReferentiallyStableCallback(componentClassNameFor)
|
|
41
44
|
}
|