@teamturing/react-kit 2.22.3 → 2.23.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.
@@ -4,9 +4,10 @@ import { FocusTrapHookSettings } from '../../hook/useFocusTrap';
4
4
  import { FocusZoneHookSettings } from '../../hook/useFocusZone';
5
5
  import { OverlayProps } from '../Overlay';
6
6
  type Props = {
7
- children: ReactNode | ((popperProps: HTMLAttributes<HTMLElement>, { isOpen, openOverlay }: {
7
+ children: ReactNode | ((popperProps: HTMLAttributes<HTMLElement>, { isOpen, openOverlay, closeOverlay }: {
8
8
  isOpen: boolean;
9
9
  openOverlay: () => void;
10
+ closeOverlay: () => void;
10
11
  }) => ReactNode);
11
12
  renderOverlay: (overlayProps: OverlayProps & {
12
13
  ref?: ForwardedRef<HTMLDivElement>;
@@ -16,12 +17,13 @@ type Props = {
16
17
  }, { elements }: {
17
18
  elements: UseFloatingReturn['elements'];
18
19
  }) => ReactNode;
20
+ triggeredBy?: 'click' | 'hover';
19
21
  placement?: Placement;
20
22
  focusZoneSettings?: Partial<FocusZoneHookSettings>;
21
23
  focusTrapSettings?: Partial<FocusTrapHookSettings>;
22
24
  onOpen?: () => void;
23
25
  onClose?: () => void;
24
26
  };
25
- declare const OverlayPopper: ({ children: propChildren, renderOverlay, placement, focusZoneSettings, focusTrapSettings, onOpen, onClose, }: Props) => import("react/jsx-runtime").JSX.Element;
27
+ declare const OverlayPopper: ({ children: propChildren, renderOverlay, triggeredBy, placement, focusZoneSettings, focusTrapSettings, onOpen, onClose, }: Props) => import("react/jsx-runtime").JSX.Element;
26
28
  export default OverlayPopper;
27
29
  export type { Props as OverlayPopperProps };
@@ -0,0 +1,10 @@
1
+ type Options<T extends (...args: any) => void> = {
2
+ func: T;
3
+ /**
4
+ * ms
5
+ */
6
+ delay?: number;
7
+ };
8
+ declare const useDelayedFunction: <T extends (...args: any) => void>({ func, delay }: Options<T>) => T;
9
+ export default useDelayedFunction;
10
+ export type { Options as UseDelayedFunctionOptions };
@@ -0,0 +1,10 @@
1
+ /// <reference types="react" />
2
+ type Options<T extends HTMLElement = HTMLDivElement> = {
3
+ targetRef?: React.RefObject<T>;
4
+ };
5
+ declare const useHover: <T extends HTMLElement = HTMLDivElement>({ targetRef }: Options<T>) => {
6
+ ref: import("react").RefObject<T>;
7
+ hovered: boolean;
8
+ };
9
+ export default useHover;
10
+ export type { Options as UseHoverOptions };
@@ -0,0 +1,12 @@
1
+ import { RefObject } from 'react';
2
+ type Options<T extends HTMLElement> = {
3
+ resetOnExit?: boolean;
4
+ targetRef?: RefObject<T>;
5
+ };
6
+ declare const useMousePosition: <T extends HTMLElement = any>({ targetRef, resetOnExit }: Options<T>) => {
7
+ x: number;
8
+ y: number;
9
+ ref: RefObject<T>;
10
+ };
11
+ export default useMousePosition;
12
+ export type { Options as UseMousePositionOptions };
package/dist/index.d.ts CHANGED
@@ -92,10 +92,13 @@ export { default as EnigmaUI } from './enigma/EnigmaUI';
92
92
  /**
93
93
  * hooks
94
94
  */
95
+ export { default as useDelayedFunction } from './hook/useDelayedFunction';
95
96
  export { default as useDevice } from './hook/useDevice';
96
97
  export { default as useFocusTrap } from './hook/useFocusTrap';
97
98
  export { default as useFocusZone } from './hook/useFocusZone';
99
+ export { default as useHover } from './hook/useHover';
98
100
  export { default as useMediaQuery } from './hook/useMediaQuery';
101
+ export { default as useMousePosition } from './hook/useMousePosition';
99
102
  export { default as useOutsideClick } from './hook/useOutsideClick';
100
103
  export { default as useProvidedOrCreatedRef } from './hook/useProvidedOrCreatedRef';
101
104
  export { default as useRelocation } from './hook/useRelocation';
package/dist/index.js CHANGED
@@ -20835,6 +20835,22 @@ function useFloating(options) {
20835
20835
  }), [data, update, refs, elements, floatingStyles]);
20836
20836
  }
20837
20837
 
20838
+ const useDelayedFunction = ({
20839
+ func,
20840
+ delay
20841
+ }) => {
20842
+ const timeout = React.useRef(-1);
20843
+ const delayedFunc = (...args) => {
20844
+ if (delay === 0 || delay === undefined) {
20845
+ func(...args);
20846
+ } else {
20847
+ timeout.current = window.setTimeout(func, delay);
20848
+ }
20849
+ };
20850
+ React.useEffect(() => () => window.clearTimeout(timeout.current), []);
20851
+ return delayedFunc;
20852
+ };
20853
+
20838
20854
  const useFocusZone = (settings = {}, dependencies = []) => {
20839
20855
  const containerRef = useProvidedOrCreatedRef(settings.containerRef);
20840
20856
  const useActiveDescendant = !!settings.activeDescendantFocus;
@@ -20883,6 +20899,7 @@ const useToggleState = ({
20883
20899
  const OverlayPopper = ({
20884
20900
  children: propChildren,
20885
20901
  renderOverlay,
20902
+ triggeredBy = 'click',
20886
20903
  placement = 'bottom-start',
20887
20904
  focusZoneSettings,
20888
20905
  focusTrapSettings,
@@ -20916,28 +20933,36 @@ const OverlayPopper = ({
20916
20933
  onClose?.();
20917
20934
  closeOverlay();
20918
20935
  };
20936
+ const delayedHandleOverlayClose = useDelayedFunction({
20937
+ func: handleOverlayClose,
20938
+ delay: 150
20939
+ });
20919
20940
  const handleDismiss = () => {
20920
20941
  handleOverlayClose();
20921
20942
  };
20922
20943
  const defaultPopperProps = {
20923
- onClick: handleOverlayToggle,
20924
- onKeyDown: e => {
20925
- if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
20926
- e.preventDefault();
20927
- handleOverlayOpen();
20928
- }
20929
- e.stopPropagation();
20930
- },
20931
20944
  tabIndex: 0,
20945
+ ...(triggeredBy === 'click' ? {
20946
+ onClick: handleOverlayToggle,
20947
+ onKeyDown: e => {
20948
+ if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
20949
+ e.preventDefault();
20950
+ handleOverlayOpen();
20951
+ }
20952
+ e.stopPropagation();
20953
+ }
20954
+ } : triggeredBy === 'hover' ? {
20955
+ onMouseEnter: handleOverlayOpen,
20956
+ onMouseLeave: delayedHandleOverlayClose
20957
+ } : {}),
20932
20958
  ...{
20933
20959
  ref: refs.setReference
20934
20960
  }
20935
20961
  };
20936
- const children = isFunction$1(propChildren) ? propChildren({
20937
- ...defaultPopperProps
20938
- }, {
20962
+ const children = isFunction$1(propChildren) ? propChildren(defaultPopperProps, {
20939
20963
  isOpen,
20940
- openOverlay: handleOverlayOpen
20964
+ openOverlay: handleOverlayOpen,
20965
+ closeOverlay: handleOverlayClose
20941
20966
  }) : React.Children.map(propChildren, child => /*#__PURE__*/React.cloneElement(child, {
20942
20967
  ...defaultPopperProps
20943
20968
  }));
@@ -26000,6 +26025,74 @@ const useDevice = () => {
26000
26025
  return deviceState || {};
26001
26026
  };
26002
26027
 
26028
+ const useHover = ({
26029
+ targetRef
26030
+ }) => {
26031
+ const [hovered, setHovered] = React.useState(false);
26032
+ const ref = useProvidedOrCreatedRef(targetRef);
26033
+ const onMouseEnter = React.useCallback(() => setHovered(true), []);
26034
+ const onMouseLeave = React.useCallback(() => setHovered(false), []);
26035
+ React.useEffect(() => {
26036
+ if (ref.current) {
26037
+ ref.current.addEventListener('mouseenter', onMouseEnter);
26038
+ ref.current.addEventListener('mouseleave', onMouseLeave);
26039
+ return () => {
26040
+ ref.current?.removeEventListener('mouseenter', onMouseEnter);
26041
+ ref.current?.removeEventListener('mouseleave', onMouseLeave);
26042
+ };
26043
+ }
26044
+ return undefined;
26045
+ }, []);
26046
+ return {
26047
+ ref,
26048
+ hovered
26049
+ };
26050
+ };
26051
+
26052
+ const useMousePosition = ({
26053
+ targetRef,
26054
+ resetOnExit = false
26055
+ }) => {
26056
+ const [position, setPosition] = React.useState({
26057
+ x: 0,
26058
+ y: 0
26059
+ });
26060
+ const ref = useProvidedOrCreatedRef(targetRef);
26061
+ const setMousePosition = event => {
26062
+ if (ref.current) {
26063
+ const rect = event.currentTarget.getBoundingClientRect();
26064
+ const x = Math.max(0, Math.round(event.pageX - rect.left - (window.pageXOffset || window.scrollX)));
26065
+ const y = Math.max(0, Math.round(event.pageY - rect.top - (window.pageYOffset || window.scrollY)));
26066
+ setPosition({
26067
+ x,
26068
+ y
26069
+ });
26070
+ } else {
26071
+ setPosition({
26072
+ x: event.clientX,
26073
+ y: event.clientY
26074
+ });
26075
+ }
26076
+ };
26077
+ const resetMousePosition = () => setPosition({
26078
+ x: 0,
26079
+ y: 0
26080
+ });
26081
+ React.useEffect(() => {
26082
+ const element = ref?.current ? ref.current : document;
26083
+ element.addEventListener('mousemove', setMousePosition);
26084
+ if (resetOnExit) element.addEventListener('mouseleave', resetMousePosition);
26085
+ return () => {
26086
+ element.removeEventListener('mousemove', setMousePosition);
26087
+ if (resetOnExit) element.removeEventListener('mouseleave', resetMousePosition);
26088
+ };
26089
+ }, [ref.current]);
26090
+ return {
26091
+ ref,
26092
+ ...position
26093
+ };
26094
+ };
26095
+
26003
26096
  /**
26004
26097
  * 특정 컴포넌트을 제외한 바깥쪽을 클릭했을 때를 핸들링하기 위한 훅입니다.
26005
26098
  */
@@ -26078,10 +26171,13 @@ exports.lineClamp = lineClamp;
26078
26171
  exports.sx = sx;
26079
26172
  exports.textDecoration = textDecoration;
26080
26173
  exports.theme = theme;
26174
+ exports.useDelayedFunction = useDelayedFunction;
26081
26175
  exports.useDevice = useDevice;
26082
26176
  exports.useFocusTrap = useFocusTrap;
26083
26177
  exports.useFocusZone = useFocusZone;
26178
+ exports.useHover = useHover;
26084
26179
  exports.useMediaQuery = useMediaQuery;
26180
+ exports.useMousePosition = useMousePosition;
26085
26181
  exports.useOutsideClick = useOutsideClick;
26086
26182
  exports.useProvidedOrCreatedRef = useProvidedOrCreatedRef;
26087
26183
  exports.useRelocation = useRelocation;
@@ -2,6 +2,7 @@ import { useFloating } from '../../node_modules/@floating-ui/react-dom/dist/floa
2
2
  import { isFunction } from '../../packages/utils/esm/isFunction.js';
3
3
  import { Children, cloneElement } from 'react';
4
4
  import { useTheme } from 'styled-components';
5
+ import useDelayedFunction from '../../hook/useDelayedFunction.js';
5
6
  import useFocusTrap from '../../hook/useFocusTrap.js';
6
7
  import useFocusZone from '../../hook/useFocusZone.js';
7
8
  import useToggleState from '../../hook/useToggleState.js';
@@ -12,6 +13,7 @@ import { offset, flip, shift } from '../../node_modules/@floating-ui/core/dist/f
12
13
  const OverlayPopper = ({
13
14
  children: propChildren,
14
15
  renderOverlay,
16
+ triggeredBy = 'click',
15
17
  placement = 'bottom-start',
16
18
  focusZoneSettings,
17
19
  focusTrapSettings,
@@ -45,28 +47,36 @@ const OverlayPopper = ({
45
47
  onClose?.();
46
48
  closeOverlay();
47
49
  };
50
+ const delayedHandleOverlayClose = useDelayedFunction({
51
+ func: handleOverlayClose,
52
+ delay: 150
53
+ });
48
54
  const handleDismiss = () => {
49
55
  handleOverlayClose();
50
56
  };
51
57
  const defaultPopperProps = {
52
- onClick: handleOverlayToggle,
53
- onKeyDown: e => {
54
- if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
55
- e.preventDefault();
56
- handleOverlayOpen();
57
- }
58
- e.stopPropagation();
59
- },
60
58
  tabIndex: 0,
59
+ ...(triggeredBy === 'click' ? {
60
+ onClick: handleOverlayToggle,
61
+ onKeyDown: e => {
62
+ if (['ArrowUp', 'ArrowDown'].includes(e.key)) {
63
+ e.preventDefault();
64
+ handleOverlayOpen();
65
+ }
66
+ e.stopPropagation();
67
+ }
68
+ } : triggeredBy === 'hover' ? {
69
+ onMouseEnter: handleOverlayOpen,
70
+ onMouseLeave: delayedHandleOverlayClose
71
+ } : {}),
61
72
  ...{
62
73
  ref: refs.setReference
63
74
  }
64
75
  };
65
- const children = isFunction(propChildren) ? propChildren({
66
- ...defaultPopperProps
67
- }, {
76
+ const children = isFunction(propChildren) ? propChildren(defaultPopperProps, {
68
77
  isOpen,
69
- openOverlay: handleOverlayOpen
78
+ openOverlay: handleOverlayOpen,
79
+ closeOverlay: handleOverlayClose
70
80
  }) : Children.map(propChildren, child => /*#__PURE__*/cloneElement(child, {
71
81
  ...defaultPopperProps
72
82
  }));
@@ -0,0 +1,19 @@
1
+ import { useRef, useEffect } from 'react';
2
+
3
+ const useDelayedFunction = ({
4
+ func,
5
+ delay
6
+ }) => {
7
+ const timeout = useRef(-1);
8
+ const delayedFunc = (...args) => {
9
+ if (delay === 0 || delay === undefined) {
10
+ func(...args);
11
+ } else {
12
+ timeout.current = window.setTimeout(func, delay);
13
+ }
14
+ };
15
+ useEffect(() => () => window.clearTimeout(timeout.current), []);
16
+ return delayedFunc;
17
+ };
18
+
19
+ export { useDelayedFunction as default };
@@ -0,0 +1,28 @@
1
+ import { useState, useCallback, useEffect } from 'react';
2
+ import useProvidedOrCreatedRef from './useProvidedOrCreatedRef.js';
3
+
4
+ const useHover = ({
5
+ targetRef
6
+ }) => {
7
+ const [hovered, setHovered] = useState(false);
8
+ const ref = useProvidedOrCreatedRef(targetRef);
9
+ const onMouseEnter = useCallback(() => setHovered(true), []);
10
+ const onMouseLeave = useCallback(() => setHovered(false), []);
11
+ useEffect(() => {
12
+ if (ref.current) {
13
+ ref.current.addEventListener('mouseenter', onMouseEnter);
14
+ ref.current.addEventListener('mouseleave', onMouseLeave);
15
+ return () => {
16
+ ref.current?.removeEventListener('mouseenter', onMouseEnter);
17
+ ref.current?.removeEventListener('mouseleave', onMouseLeave);
18
+ };
19
+ }
20
+ return undefined;
21
+ }, []);
22
+ return {
23
+ ref,
24
+ hovered
25
+ };
26
+ };
27
+
28
+ export { useHover as default };
@@ -0,0 +1,48 @@
1
+ import { useState, useEffect } from 'react';
2
+ import useProvidedOrCreatedRef from './useProvidedOrCreatedRef.js';
3
+
4
+ const useMousePosition = ({
5
+ targetRef,
6
+ resetOnExit = false
7
+ }) => {
8
+ const [position, setPosition] = useState({
9
+ x: 0,
10
+ y: 0
11
+ });
12
+ const ref = useProvidedOrCreatedRef(targetRef);
13
+ const setMousePosition = event => {
14
+ if (ref.current) {
15
+ const rect = event.currentTarget.getBoundingClientRect();
16
+ const x = Math.max(0, Math.round(event.pageX - rect.left - (window.pageXOffset || window.scrollX)));
17
+ const y = Math.max(0, Math.round(event.pageY - rect.top - (window.pageYOffset || window.scrollY)));
18
+ setPosition({
19
+ x,
20
+ y
21
+ });
22
+ } else {
23
+ setPosition({
24
+ x: event.clientX,
25
+ y: event.clientY
26
+ });
27
+ }
28
+ };
29
+ const resetMousePosition = () => setPosition({
30
+ x: 0,
31
+ y: 0
32
+ });
33
+ useEffect(() => {
34
+ const element = ref?.current ? ref.current : document;
35
+ element.addEventListener('mousemove', setMousePosition);
36
+ if (resetOnExit) element.addEventListener('mouseleave', resetMousePosition);
37
+ return () => {
38
+ element.removeEventListener('mousemove', setMousePosition);
39
+ if (resetOnExit) element.removeEventListener('mouseleave', resetMousePosition);
40
+ };
41
+ }, [ref.current]);
42
+ return {
43
+ ref,
44
+ ...position
45
+ };
46
+ };
47
+
48
+ export { useMousePosition as default };
package/esm/index.js CHANGED
@@ -41,10 +41,13 @@ export { default as Tooltip } from './core/Tooltip/index.js';
41
41
  export { default as View } from './core/View/index.js';
42
42
  export { default as UnstyledButton } from './core/_UnstyledButton.js';
43
43
  export { default as EnigmaUI } from './enigma/EnigmaUI/index.js';
44
+ export { default as useDelayedFunction } from './hook/useDelayedFunction.js';
44
45
  export { default as useDevice } from './hook/useDevice.js';
45
46
  export { default as useFocusTrap } from './hook/useFocusTrap.js';
46
47
  export { default as useFocusZone } from './hook/useFocusZone.js';
48
+ export { default as useHover } from './hook/useHover.js';
47
49
  export { default as useMediaQuery } from './hook/useMediaQuery.js';
50
+ export { default as useMousePosition } from './hook/useMousePosition.js';
48
51
  export { default as useOutsideClick } from './hook/useOutsideClick.js';
49
52
  export { default as useProvidedOrCreatedRef } from './hook/useProvidedOrCreatedRef.js';
50
53
  export { default as useRelocation } from './hook/useRelocation.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamturing/react-kit",
3
- "version": "2.22.3",
3
+ "version": "2.23.1",
4
4
  "description": "React components, hooks for create teamturing web application",
5
5
  "author": "Sungchang Park <psch300@gmail.com> (https://github.com/psch300)",
6
6
  "homepage": "https://github.com/weareteamturing/bombe#readme",
@@ -66,5 +66,5 @@
66
66
  "react-textarea-autosize": "^8.5.3",
67
67
  "styled-system": "^5.1.5"
68
68
  },
69
- "gitHead": "8954829533862df7f2899b1f9bc80d689b5afab8"
69
+ "gitHead": "5268a60ac707cb805ced97d347b7ab20de29b0f9"
70
70
  }