jy-headless 0.2.36 → 0.3.6
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/Input/NumberInput.d.ts +3 -0
- package/Input/NumberInput.type.d.ts +31 -0
- package/Input/TextInput.d.ts +3 -0
- package/Input/TextInput.type.d.ts +95 -0
- package/Popover/Popover.d.ts +2 -0
- package/Popover/Popover.js +75 -0
- package/Popover/Popover.type.d.ts +9 -0
- package/Popover/index.d.ts +2 -0
- package/Select/Select.d.ts +51 -0
- package/Select/Select.type.d.ts +52 -0
- package/Tooltip/Tooltip.d.ts +10 -0
- package/Tooltip/Tooltip.js +36 -0
- package/Tooltip/Tooltip.type.d.ts +20 -0
- package/Tooltip/index.d.ts +2 -0
- package/cjs/Input/NumberInput.d.ts +3 -0
- package/cjs/Input/NumberInput.type.d.ts +31 -0
- package/cjs/Input/TextInput.d.ts +3 -0
- package/cjs/Input/TextInput.type.d.ts +95 -0
- package/cjs/Popover/Popover.d.ts +2 -0
- package/cjs/Popover/Popover.js +77 -0
- package/cjs/Popover/Popover.type.d.ts +9 -0
- package/cjs/Popover/index.d.ts +2 -0
- package/cjs/Select/Select.d.ts +51 -0
- package/cjs/Select/Select.type.d.ts +52 -0
- package/cjs/Tooltip/Tooltip.d.ts +10 -0
- package/cjs/Tooltip/Tooltip.js +38 -0
- package/cjs/Tooltip/Tooltip.type.d.ts +20 -0
- package/cjs/Tooltip/index.d.ts +2 -0
- package/cjs/hooks/index.d.ts +3 -3
- package/cjs/hooks/useDebounce.d.ts +1 -0
- package/cjs/hooks/useDebounce.js +24 -0
- package/cjs/hooks/usePortal.d.ts +23 -0
- package/cjs/hooks/usePortal.js +80 -0
- package/cjs/hooks/useThrottle.d.ts +1 -0
- package/cjs/hooks/useThrottle.js +34 -0
- package/cjs/index.d.ts +3 -6
- package/cjs/index.js +8 -23
- package/hooks/index.d.ts +3 -3
- package/hooks/useDebounce.d.ts +1 -0
- package/hooks/useDebounce.js +22 -0
- package/hooks/usePortal.d.ts +23 -0
- package/hooks/usePortal.js +78 -0
- package/hooks/useThrottle.d.ts +1 -0
- package/hooks/useThrottle.js +32 -0
- package/index.d.ts +3 -6
- package/index.js +5 -13
- package/package.json +23 -63
- package/version.txt +1 -1
- package/buttons/Button/Button.d.ts +0 -3
- package/buttons/Button/Button.js +0 -9
- package/buttons/index.d.ts +0 -1
- package/cjs/buttons/Button/Button.d.ts +0 -3
- package/cjs/buttons/Button/Button.js +0 -11
- package/cjs/buttons/index.d.ts +0 -1
- package/cjs/hooks/useDebouncing.d.ts +0 -2
- package/cjs/hooks/useDebouncing.js +0 -16
- package/cjs/hooks/useDropdown.d.ts +0 -10
- package/cjs/hooks/useDropdown.js +0 -32
- package/cjs/hooks/useThrottling.d.ts +0 -2
- package/cjs/hooks/useThrottling.js +0 -16
- package/cjs/inputs/ImageInput/ImageInput.d.ts +0 -7
- package/cjs/inputs/ImageInput/ImageInput.js +0 -76
- package/cjs/inputs/Input/Input.d.ts +0 -3
- package/cjs/inputs/Input/Input.js +0 -17
- package/cjs/inputs/checkboxList/CheckboxList.d.ts +0 -5
- package/cjs/inputs/checkboxList/CheckboxList.js +0 -32
- package/cjs/inputs/checkboxList/index.d.ts +0 -5
- package/cjs/inputs/checkboxList/index.js +0 -32
- package/cjs/inputs/index.d.ts +0 -3
- package/cjs/selectors/Dropdown/Dropdown.d.ts +0 -8
- package/cjs/selectors/Dropdown/Dropdown.js +0 -55
- package/cjs/selectors/index.d.ts +0 -1
- package/cjs/tooltip/Tooltip/Tooltip.d.ts +0 -5
- package/cjs/tooltip/Tooltip/Tooltip.js +0 -66
- package/cjs/tooltip/index.d.ts +0 -1
- package/cjs/types/buttons/index.d.ts +0 -7
- package/cjs/types/common/index.d.ts +0 -11
- package/cjs/types/index.d.ts +0 -4
- package/cjs/types/inputs/index.d.ts +0 -36
- package/cjs/types/selectors/index.d.ts +0 -16
- package/cjs/types/tooltip/index.d.ts +0 -14
- package/cjs/types/tooltip/index.js +0 -7
- package/cjs/utils/ArrayUtils.d.ts +0 -2
- package/cjs/utils/MessageUtils.d.ts +0 -1
- package/cjs/utils/index.d.ts +0 -1
- package/hooks/useDebouncing.d.ts +0 -2
- package/hooks/useDebouncing.js +0 -14
- package/hooks/useDropdown.d.ts +0 -10
- package/hooks/useDropdown.js +0 -26
- package/hooks/useThrottling.d.ts +0 -2
- package/hooks/useThrottling.js +0 -14
- package/inputs/ImageInput/ImageInput.d.ts +0 -7
- package/inputs/ImageInput/ImageInput.js +0 -71
- package/inputs/Input/Input.d.ts +0 -3
- package/inputs/Input/Input.js +0 -15
- package/inputs/checkboxList/CheckboxList.d.ts +0 -5
- package/inputs/checkboxList/CheckboxList.js +0 -30
- package/inputs/checkboxList/index.d.ts +0 -5
- package/inputs/checkboxList/index.js +0 -30
- package/inputs/index.d.ts +0 -3
- package/selectors/Dropdown/Dropdown.d.ts +0 -8
- package/selectors/Dropdown/Dropdown.js +0 -53
- package/selectors/index.d.ts +0 -1
- package/tooltip/Tooltip/Tooltip.d.ts +0 -5
- package/tooltip/Tooltip/Tooltip.js +0 -64
- package/tooltip/index.d.ts +0 -1
- package/types/buttons/index.d.ts +0 -7
- package/types/common/index.d.ts +0 -11
- package/types/index.d.ts +0 -4
- package/types/inputs/index.d.ts +0 -36
- package/types/selectors/index.d.ts +0 -16
- package/types/tooltip/index.d.ts +0 -14
- package/types/tooltip/index.js +0 -5
- package/utils/ArrayUtils.d.ts +0 -2
- package/utils/MessageUtils.d.ts +0 -1
- package/utils/index.d.ts +0 -1
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Dispatch, SetStateAction } from 'react';
|
|
2
|
+
import { SelectOptionProps, SelectOptionsProps, SelectProps, SelectTriggerProps } from './Select.type';
|
|
3
|
+
/**
|
|
4
|
+
* Select 내부 상태 공유용 Context
|
|
5
|
+
*/
|
|
6
|
+
type SelectContextValue = {
|
|
7
|
+
/** 드롭다운 열림 여부 */
|
|
8
|
+
open: boolean;
|
|
9
|
+
/** 드롭다운 열기/닫기 */
|
|
10
|
+
setOpen: (v: boolean) => void;
|
|
11
|
+
/** 현재 선택된 값들 */
|
|
12
|
+
value: string[];
|
|
13
|
+
/** 값 토글 */
|
|
14
|
+
toggleValue: (v: string) => void;
|
|
15
|
+
/** 다중 선택 여부 */
|
|
16
|
+
multiple: boolean;
|
|
17
|
+
/** Trigger DOM ref */
|
|
18
|
+
triggerRef: React.RefObject<HTMLDivElement | null>;
|
|
19
|
+
/** Option DOM refs 목록 */
|
|
20
|
+
optionRefs: React.MutableRefObject<HTMLDivElement[]>;
|
|
21
|
+
/** 현재 포커스된 옵션 index */
|
|
22
|
+
focusedIndex: number | null;
|
|
23
|
+
/** 포커스 index 설정 */
|
|
24
|
+
setFocusedIndex: Dispatch<SetStateAction<number>>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Select Context 접근 훅
|
|
28
|
+
*
|
|
29
|
+
* @throws Select 외부에서 사용할 경우 에러
|
|
30
|
+
*/
|
|
31
|
+
export declare const useSelectContext: () => SelectContextValue;
|
|
32
|
+
/**
|
|
33
|
+
* Compound Select 컴포넌트
|
|
34
|
+
*
|
|
35
|
+
* 사용 예시:
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* <Select value={value} onChange={setValue}>
|
|
39
|
+
* <Select.Trigger>열기</Select.Trigger>
|
|
40
|
+
* <Select.Options>
|
|
41
|
+
* <Select.Option value="a">A</Select.Option>
|
|
42
|
+
* <Select.Option value="b">B</Select.Option>
|
|
43
|
+
* </Select.Options>
|
|
44
|
+
* </Select>
|
|
45
|
+
*/
|
|
46
|
+
declare const Select: (({ value, onChange, multiple, children }: SelectProps) => import("react/jsx-runtime").JSX.Element) & {
|
|
47
|
+
Trigger: (props: SelectTriggerProps) => import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
Options: ({ children, ...props }: SelectOptionsProps) => import("react").ReactPortal | null;
|
|
49
|
+
Option: ({ value, disabled, children, ...props }: SelectOptionProps) => import("react/jsx-runtime").JSX.Element;
|
|
50
|
+
};
|
|
51
|
+
export default Select;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { HTMLProps, ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Select 루트 컴포넌트 props
|
|
4
|
+
*/
|
|
5
|
+
export interface SelectProps {
|
|
6
|
+
/**
|
|
7
|
+
* 선택된 값 목록
|
|
8
|
+
*
|
|
9
|
+
* - single 모드에서도 배열로 관리
|
|
10
|
+
*/
|
|
11
|
+
value: string[];
|
|
12
|
+
/**
|
|
13
|
+
* 값 변경 시 호출
|
|
14
|
+
*/
|
|
15
|
+
onChange: (v: string[]) => void;
|
|
16
|
+
/**
|
|
17
|
+
* 다중 선택 여부
|
|
18
|
+
* @default false
|
|
19
|
+
*/
|
|
20
|
+
multiple?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Select.Trigger / Select.Options 등을 포함하는 children
|
|
23
|
+
*/
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Options 컨테이너 props
|
|
28
|
+
*/
|
|
29
|
+
export interface SelectOptionsProps extends HTMLProps<HTMLDivElement> {
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 개별 Option props
|
|
33
|
+
*/
|
|
34
|
+
export interface SelectOptionProps extends HTMLProps<HTMLDivElement> {
|
|
35
|
+
/**
|
|
36
|
+
* 옵션의 실제 값
|
|
37
|
+
*/
|
|
38
|
+
value: string;
|
|
39
|
+
/**
|
|
40
|
+
* 비활성화 여부
|
|
41
|
+
*/
|
|
42
|
+
disabled?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* 표시될 내용
|
|
45
|
+
*/
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Trigger 버튼 props
|
|
50
|
+
*/
|
|
51
|
+
export interface SelectTriggerProps extends HTMLProps<HTMLDivElement> {
|
|
52
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { TooltipProps } from './Tooltip.type';
|
|
2
|
+
/**
|
|
3
|
+
* 범용 Tooltip 컴포넌트
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* <Tooltip popover="안녕하세요" direction="top">
|
|
7
|
+
* <button>Hover me</button>
|
|
8
|
+
* </Tooltip>
|
|
9
|
+
*/
|
|
10
|
+
export declare const Tooltip: ({ direction, popover, children, key, gap, autoFlip }: TooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var usePortal = require('../hooks/usePortal.js');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 범용 Tooltip 컴포넌트
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* <Tooltip popover="안녕하세요" direction="top">
|
|
12
|
+
* <button>Hover me</button>
|
|
13
|
+
* </Tooltip>
|
|
14
|
+
*/
|
|
15
|
+
const Tooltip = ({ direction = 'top', popover, children, key, gap = 0, autoFlip = true }) => {
|
|
16
|
+
const [visible, setVisible] = react.useState(false);
|
|
17
|
+
const targetRef = react.useRef(null);
|
|
18
|
+
const popoverRef = react.useRef(null);
|
|
19
|
+
const { portal, rootDom } = usePortal({
|
|
20
|
+
content: popover,
|
|
21
|
+
key,
|
|
22
|
+
visible,
|
|
23
|
+
targetRef,
|
|
24
|
+
popoverRef,
|
|
25
|
+
direction,
|
|
26
|
+
gap,
|
|
27
|
+
autoFlip,
|
|
28
|
+
});
|
|
29
|
+
const handleMouseEnter = () => {
|
|
30
|
+
setVisible(true);
|
|
31
|
+
};
|
|
32
|
+
const handleMouseLeave = () => {
|
|
33
|
+
setVisible(false);
|
|
34
|
+
};
|
|
35
|
+
return (jsxRuntime.jsxs("span", { onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ref: targetRef, children: [children, rootDom && visible && portal] }));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
exports.Tooltip = Tooltip;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Direction } from '../hooks';
|
|
3
|
+
export interface TooltipProps {
|
|
4
|
+
/** 툴팁을 띄울 대상 요소 */
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
/** 툴팁에 표시될 내용 */
|
|
7
|
+
popover: ReactNode;
|
|
8
|
+
/** 툴팁이 나타날 방향 */
|
|
9
|
+
direction: Direction;
|
|
10
|
+
/** 특정 DOM 타겟 ID (선택) */
|
|
11
|
+
targetId?: string;
|
|
12
|
+
/** 포털로 렌더링할 DOM 노드 */
|
|
13
|
+
domNode?: Element;
|
|
14
|
+
/** React key 값 */
|
|
15
|
+
key?: string;
|
|
16
|
+
/** 타겟과 툴팁 사이 간격(px) */
|
|
17
|
+
gap?: number;
|
|
18
|
+
/** 화면 밖으로 나가면 자동 반전 여부 */
|
|
19
|
+
autoFlip?: boolean;
|
|
20
|
+
}
|
package/cjs/hooks/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
1
|
+
export * from './usePortal';
|
|
2
|
+
export * from './useDebounce';
|
|
3
|
+
export * from './useThrottle';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useDebounce: (callback: (...args: any[]) => void, delay: number) => (...args: any[]) => void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
const useDebounce = (callback, delay) => {
|
|
6
|
+
const timeoutRef = react.useRef(0);
|
|
7
|
+
react.useEffect(() => {
|
|
8
|
+
return () => {
|
|
9
|
+
if (timeoutRef.current) {
|
|
10
|
+
clearTimeout(timeoutRef.current);
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}, []);
|
|
14
|
+
return react.useCallback((...args) => {
|
|
15
|
+
if (timeoutRef.current) {
|
|
16
|
+
clearTimeout(timeoutRef.current);
|
|
17
|
+
}
|
|
18
|
+
timeoutRef.current = setTimeout(() => {
|
|
19
|
+
callback(...args);
|
|
20
|
+
}, delay);
|
|
21
|
+
}, [callback, delay]);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
exports.useDebounce = useDebounce;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Key, ReactNode, RefObject } from 'react';
|
|
2
|
+
type Position = {
|
|
3
|
+
top: number;
|
|
4
|
+
left: number;
|
|
5
|
+
};
|
|
6
|
+
export type Direction = 'top-left' | 'top-center' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-center' | 'bottom-right';
|
|
7
|
+
type UsePortalProps = {
|
|
8
|
+
content: ReactNode;
|
|
9
|
+
key?: Key | null;
|
|
10
|
+
visible?: boolean;
|
|
11
|
+
targetRef?: RefObject<HTMLElement | null>;
|
|
12
|
+
popoverRef?: RefObject<HTMLElement | null>;
|
|
13
|
+
direction?: Direction;
|
|
14
|
+
gap?: number;
|
|
15
|
+
position?: Position;
|
|
16
|
+
autoFlip?: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare const usePortal: ({ content, key, visible, targetRef, popoverRef, direction, gap, position: customPosition, autoFlip, }: UsePortalProps) => {
|
|
19
|
+
portal: import("react").ReactPortal;
|
|
20
|
+
rootDom: HTMLElement;
|
|
21
|
+
finalDirection: Direction;
|
|
22
|
+
};
|
|
23
|
+
export default usePortal;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var reactDom = require('react-dom');
|
|
6
|
+
|
|
7
|
+
const usePortal = ({ content, key, visible = true, targetRef, popoverRef, direction = 'top', gap = 0, position: customPosition, autoFlip = true, }) => {
|
|
8
|
+
const rootDom = react.useMemo(() => document.body, []);
|
|
9
|
+
const [position, setPosition] = react.useState(customPosition || { top: 0, left: 0 });
|
|
10
|
+
const [finalDirection, setFinalDirection] = react.useState(direction);
|
|
11
|
+
const getOppositeDirection = (dir) => {
|
|
12
|
+
if (dir.startsWith('top'))
|
|
13
|
+
return dir.replace('top', 'bottom');
|
|
14
|
+
if (dir.startsWith('bottom'))
|
|
15
|
+
return dir.replace('bottom', 'top');
|
|
16
|
+
if (dir === 'left')
|
|
17
|
+
return 'right';
|
|
18
|
+
if (dir === 'right')
|
|
19
|
+
return 'left';
|
|
20
|
+
return dir;
|
|
21
|
+
};
|
|
22
|
+
const getPopoverPosition = (targetRect, popoverRect, dir) => {
|
|
23
|
+
let top = (dir.startsWith('top')
|
|
24
|
+
? targetRect.top - popoverRect.height
|
|
25
|
+
: dir.startsWith('bottom')
|
|
26
|
+
? targetRect.top + targetRect.height
|
|
27
|
+
: targetRect.top + targetRect.height / 2 - popoverRect.height / 2) - gap;
|
|
28
|
+
let left = dir.endsWith('left')
|
|
29
|
+
? targetRect.left - popoverRect.width - gap
|
|
30
|
+
: (dir.endsWith('right')
|
|
31
|
+
? targetRect.left + targetRect.width + gap
|
|
32
|
+
: targetRect.left + targetRect.width / 2 - popoverRect.width / 2);
|
|
33
|
+
return { top, left };
|
|
34
|
+
};
|
|
35
|
+
const isOutOfViewport = (pos, popoverRect) => {
|
|
36
|
+
const viewportWidth = window.innerWidth;
|
|
37
|
+
const viewportHeight = window.innerHeight;
|
|
38
|
+
return (pos.top < 0 || // 상단 밖
|
|
39
|
+
pos.left < 0 || // 좌측 밖
|
|
40
|
+
pos.top + popoverRect.height > viewportHeight + window.scrollY || // 하단 밖
|
|
41
|
+
pos.left + popoverRect.width > viewportWidth + window.scrollX // 우측 밖
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
react.useLayoutEffect(() => {
|
|
45
|
+
if (visible && targetRef?.current && popoverRef?.current) {
|
|
46
|
+
const targetRect = targetRef.current.getBoundingClientRect();
|
|
47
|
+
const popoverRect = popoverRef.current.getBoundingClientRect();
|
|
48
|
+
// 원래 방향으로 위치 계산
|
|
49
|
+
let calculatedPosition = getPopoverPosition(targetRect, popoverRect, direction);
|
|
50
|
+
let currentDirection = direction;
|
|
51
|
+
// autoFlip이 활성화되어 있고 화면 밖으로 나가면 반대 방향 시도
|
|
52
|
+
if (autoFlip && isOutOfViewport(calculatedPosition, popoverRect)) {
|
|
53
|
+
const oppositeDir = getOppositeDirection(direction);
|
|
54
|
+
const oppositePosition = getPopoverPosition(targetRect, popoverRect, oppositeDir);
|
|
55
|
+
// 반대 방향이 더 나으면 반대 방향 사용
|
|
56
|
+
if (!isOutOfViewport(oppositePosition, popoverRect)) {
|
|
57
|
+
calculatedPosition = oppositePosition;
|
|
58
|
+
currentDirection = oppositeDir;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
setFinalDirection(currentDirection);
|
|
62
|
+
setPosition({
|
|
63
|
+
top: calculatedPosition.top + window.scrollY,
|
|
64
|
+
left: calculatedPosition.left + window.scrollX,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if (customPosition) {
|
|
68
|
+
setPosition(customPosition);
|
|
69
|
+
}
|
|
70
|
+
}, [visible, direction, gap, customPosition, autoFlip]);
|
|
71
|
+
const wrappedContent = targetRef ? (jsxRuntime.jsx("span", { ref: popoverRef, style: {
|
|
72
|
+
position: 'absolute',
|
|
73
|
+
top: `${position.top}px`,
|
|
74
|
+
left: `${position.left}px`,
|
|
75
|
+
}, children: content })) : content;
|
|
76
|
+
const portal = reactDom.createPortal(wrappedContent, rootDom, key);
|
|
77
|
+
return { portal, rootDom, finalDirection };
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
module.exports = usePortal;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useThrottle: (callback: (...args: any[]) => void, delay: number) => (...args: any[]) => void;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
|
|
5
|
+
const useThrottle = (callback, delay) => {
|
|
6
|
+
const lastRun = react.useRef(Date.now());
|
|
7
|
+
const timeoutRef = react.useRef(0);
|
|
8
|
+
react.useEffect(() => {
|
|
9
|
+
return () => {
|
|
10
|
+
if (timeoutRef.current) {
|
|
11
|
+
clearTimeout(timeoutRef.current);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}, []);
|
|
15
|
+
return react.useCallback((...args) => {
|
|
16
|
+
const now = Date.now();
|
|
17
|
+
const timeSinceLastRun = now - lastRun.current;
|
|
18
|
+
if (timeSinceLastRun >= delay) {
|
|
19
|
+
callback(...args);
|
|
20
|
+
lastRun.current = now;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
if (timeoutRef.current) {
|
|
24
|
+
clearTimeout(timeoutRef.current);
|
|
25
|
+
}
|
|
26
|
+
timeoutRef.current = setTimeout(() => {
|
|
27
|
+
callback(...args);
|
|
28
|
+
lastRun.current = Date.now();
|
|
29
|
+
}, delay - timeSinceLastRun);
|
|
30
|
+
}
|
|
31
|
+
}, [callback, delay]);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
exports.useThrottle = useThrottle;
|
package/cjs/index.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export * from './buttons';
|
|
2
|
-
export * from './inputs';
|
|
3
|
-
export * from './selectors';
|
|
4
|
-
export * from './tooltip';
|
|
5
|
-
export * from './types';
|
|
6
|
-
export * from './utils';
|
|
7
1
|
export * from './hooks';
|
|
2
|
+
export * from './Tooltip';
|
|
3
|
+
export * from './Input/TextInput';
|
|
4
|
+
export * from './Input/NumberInput';
|
package/cjs/index.js
CHANGED
|
@@ -1,29 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
var Input = require('./inputs/Input/Input.js');
|
|
5
|
-
var ImageInput = require('./inputs/ImageInput/ImageInput.js');
|
|
6
|
-
var CheckboxList = require('./inputs/checkboxList/CheckboxList.js');
|
|
7
|
-
var Dropdown = require('./selectors/Dropdown/Dropdown.js');
|
|
8
|
-
var Tooltip = require('./tooltip/Tooltip/Tooltip.js');
|
|
3
|
+
require('react/jsx-runtime');
|
|
9
4
|
require('react');
|
|
10
|
-
|
|
5
|
+
require('react-dom');
|
|
6
|
+
var useDebounce = require('./hooks/useDebounce.js');
|
|
7
|
+
var useThrottle = require('./hooks/useThrottle.js');
|
|
8
|
+
var Tooltip = require('./Tooltip/Tooltip.js');
|
|
11
9
|
|
|
12
|
-
function _interopNamespaceDefaultOnly (e) { return Object.freeze({ __proto__: null, default: e }); }
|
|
13
10
|
|
|
14
|
-
var Button__namespace = /*#__PURE__*/_interopNamespaceDefaultOnly(Button);
|
|
15
|
-
var Input__namespace = /*#__PURE__*/_interopNamespaceDefaultOnly(Input);
|
|
16
|
-
var CheckboxList__namespace = /*#__PURE__*/_interopNamespaceDefaultOnly(CheckboxList);
|
|
17
|
-
var Dropdown__namespace = /*#__PURE__*/_interopNamespaceDefaultOnly(Dropdown);
|
|
18
|
-
var Tooltip__namespace = /*#__PURE__*/_interopNamespaceDefaultOnly(Tooltip);
|
|
19
11
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
exports.
|
|
23
|
-
exports.Input = Input__namespace;
|
|
24
|
-
exports.ImageInput = ImageInput;
|
|
25
|
-
exports.CheckboxList = CheckboxList__namespace;
|
|
26
|
-
exports.Dropdown = Dropdown__namespace;
|
|
27
|
-
exports.Tooltip = Tooltip__namespace;
|
|
28
|
-
exports.DropdownContext = useDropdown.DropdownContext;
|
|
29
|
-
exports.useDropdownContext = useDropdown.useDropdownContext;
|
|
12
|
+
exports.useDebounce = useDebounce.useDebounce;
|
|
13
|
+
exports.useThrottle = useThrottle.useThrottle;
|
|
14
|
+
exports.Tooltip = Tooltip.Tooltip;
|
package/hooks/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from './
|
|
2
|
-
export * from './
|
|
3
|
-
export * from './
|
|
1
|
+
export * from './usePortal';
|
|
2
|
+
export * from './useDebounce';
|
|
3
|
+
export * from './useThrottle';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useDebounce: (callback: (...args: any[]) => void, delay: number) => (...args: any[]) => void;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useRef, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
const useDebounce = (callback, delay) => {
|
|
4
|
+
const timeoutRef = useRef(0);
|
|
5
|
+
useEffect(() => {
|
|
6
|
+
return () => {
|
|
7
|
+
if (timeoutRef.current) {
|
|
8
|
+
clearTimeout(timeoutRef.current);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
}, []);
|
|
12
|
+
return useCallback((...args) => {
|
|
13
|
+
if (timeoutRef.current) {
|
|
14
|
+
clearTimeout(timeoutRef.current);
|
|
15
|
+
}
|
|
16
|
+
timeoutRef.current = setTimeout(() => {
|
|
17
|
+
callback(...args);
|
|
18
|
+
}, delay);
|
|
19
|
+
}, [callback, delay]);
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { useDebounce };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Key, ReactNode, RefObject } from 'react';
|
|
2
|
+
type Position = {
|
|
3
|
+
top: number;
|
|
4
|
+
left: number;
|
|
5
|
+
};
|
|
6
|
+
export type Direction = 'top-left' | 'top-center' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-center' | 'bottom-right';
|
|
7
|
+
type UsePortalProps = {
|
|
8
|
+
content: ReactNode;
|
|
9
|
+
key?: Key | null;
|
|
10
|
+
visible?: boolean;
|
|
11
|
+
targetRef?: RefObject<HTMLElement | null>;
|
|
12
|
+
popoverRef?: RefObject<HTMLElement | null>;
|
|
13
|
+
direction?: Direction;
|
|
14
|
+
gap?: number;
|
|
15
|
+
position?: Position;
|
|
16
|
+
autoFlip?: boolean;
|
|
17
|
+
};
|
|
18
|
+
declare const usePortal: ({ content, key, visible, targetRef, popoverRef, direction, gap, position: customPosition, autoFlip, }: UsePortalProps) => {
|
|
19
|
+
portal: import("react").ReactPortal;
|
|
20
|
+
rootDom: HTMLElement;
|
|
21
|
+
finalDirection: Direction;
|
|
22
|
+
};
|
|
23
|
+
export default usePortal;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useMemo, useState, useLayoutEffect } from 'react';
|
|
3
|
+
import { createPortal } from 'react-dom';
|
|
4
|
+
|
|
5
|
+
const usePortal = ({ content, key, visible = true, targetRef, popoverRef, direction = 'top', gap = 0, position: customPosition, autoFlip = true, }) => {
|
|
6
|
+
const rootDom = useMemo(() => document.body, []);
|
|
7
|
+
const [position, setPosition] = useState(customPosition || { top: 0, left: 0 });
|
|
8
|
+
const [finalDirection, setFinalDirection] = useState(direction);
|
|
9
|
+
const getOppositeDirection = (dir) => {
|
|
10
|
+
if (dir.startsWith('top'))
|
|
11
|
+
return dir.replace('top', 'bottom');
|
|
12
|
+
if (dir.startsWith('bottom'))
|
|
13
|
+
return dir.replace('bottom', 'top');
|
|
14
|
+
if (dir === 'left')
|
|
15
|
+
return 'right';
|
|
16
|
+
if (dir === 'right')
|
|
17
|
+
return 'left';
|
|
18
|
+
return dir;
|
|
19
|
+
};
|
|
20
|
+
const getPopoverPosition = (targetRect, popoverRect, dir) => {
|
|
21
|
+
let top = (dir.startsWith('top')
|
|
22
|
+
? targetRect.top - popoverRect.height
|
|
23
|
+
: dir.startsWith('bottom')
|
|
24
|
+
? targetRect.top + targetRect.height
|
|
25
|
+
: targetRect.top + targetRect.height / 2 - popoverRect.height / 2) - gap;
|
|
26
|
+
let left = dir.endsWith('left')
|
|
27
|
+
? targetRect.left - popoverRect.width - gap
|
|
28
|
+
: (dir.endsWith('right')
|
|
29
|
+
? targetRect.left + targetRect.width + gap
|
|
30
|
+
: targetRect.left + targetRect.width / 2 - popoverRect.width / 2);
|
|
31
|
+
return { top, left };
|
|
32
|
+
};
|
|
33
|
+
const isOutOfViewport = (pos, popoverRect) => {
|
|
34
|
+
const viewportWidth = window.innerWidth;
|
|
35
|
+
const viewportHeight = window.innerHeight;
|
|
36
|
+
return (pos.top < 0 || // 상단 밖
|
|
37
|
+
pos.left < 0 || // 좌측 밖
|
|
38
|
+
pos.top + popoverRect.height > viewportHeight + window.scrollY || // 하단 밖
|
|
39
|
+
pos.left + popoverRect.width > viewportWidth + window.scrollX // 우측 밖
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
useLayoutEffect(() => {
|
|
43
|
+
if (visible && targetRef?.current && popoverRef?.current) {
|
|
44
|
+
const targetRect = targetRef.current.getBoundingClientRect();
|
|
45
|
+
const popoverRect = popoverRef.current.getBoundingClientRect();
|
|
46
|
+
// 원래 방향으로 위치 계산
|
|
47
|
+
let calculatedPosition = getPopoverPosition(targetRect, popoverRect, direction);
|
|
48
|
+
let currentDirection = direction;
|
|
49
|
+
// autoFlip이 활성화되어 있고 화면 밖으로 나가면 반대 방향 시도
|
|
50
|
+
if (autoFlip && isOutOfViewport(calculatedPosition, popoverRect)) {
|
|
51
|
+
const oppositeDir = getOppositeDirection(direction);
|
|
52
|
+
const oppositePosition = getPopoverPosition(targetRect, popoverRect, oppositeDir);
|
|
53
|
+
// 반대 방향이 더 나으면 반대 방향 사용
|
|
54
|
+
if (!isOutOfViewport(oppositePosition, popoverRect)) {
|
|
55
|
+
calculatedPosition = oppositePosition;
|
|
56
|
+
currentDirection = oppositeDir;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
setFinalDirection(currentDirection);
|
|
60
|
+
setPosition({
|
|
61
|
+
top: calculatedPosition.top + window.scrollY,
|
|
62
|
+
left: calculatedPosition.left + window.scrollX,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else if (customPosition) {
|
|
66
|
+
setPosition(customPosition);
|
|
67
|
+
}
|
|
68
|
+
}, [visible, direction, gap, customPosition, autoFlip]);
|
|
69
|
+
const wrappedContent = targetRef ? (jsx("span", { ref: popoverRef, style: {
|
|
70
|
+
position: 'absolute',
|
|
71
|
+
top: `${position.top}px`,
|
|
72
|
+
left: `${position.left}px`,
|
|
73
|
+
}, children: content })) : content;
|
|
74
|
+
const portal = createPortal(wrappedContent, rootDom, key);
|
|
75
|
+
return { portal, rootDom, finalDirection };
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export { usePortal as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useThrottle: (callback: (...args: any[]) => void, delay: number) => (...args: any[]) => void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { useRef, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
const useThrottle = (callback, delay) => {
|
|
4
|
+
const lastRun = useRef(Date.now());
|
|
5
|
+
const timeoutRef = useRef(0);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
return () => {
|
|
8
|
+
if (timeoutRef.current) {
|
|
9
|
+
clearTimeout(timeoutRef.current);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
}, []);
|
|
13
|
+
return useCallback((...args) => {
|
|
14
|
+
const now = Date.now();
|
|
15
|
+
const timeSinceLastRun = now - lastRun.current;
|
|
16
|
+
if (timeSinceLastRun >= delay) {
|
|
17
|
+
callback(...args);
|
|
18
|
+
lastRun.current = now;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
if (timeoutRef.current) {
|
|
22
|
+
clearTimeout(timeoutRef.current);
|
|
23
|
+
}
|
|
24
|
+
timeoutRef.current = setTimeout(() => {
|
|
25
|
+
callback(...args);
|
|
26
|
+
lastRun.current = Date.now();
|
|
27
|
+
}, delay - timeSinceLastRun);
|
|
28
|
+
}
|
|
29
|
+
}, [callback, delay]);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { useThrottle };
|
package/index.d.ts
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
export * from './buttons';
|
|
2
|
-
export * from './inputs';
|
|
3
|
-
export * from './selectors';
|
|
4
|
-
export * from './tooltip';
|
|
5
|
-
export * from './types';
|
|
6
|
-
export * from './utils';
|
|
7
1
|
export * from './hooks';
|
|
2
|
+
export * from './Tooltip';
|
|
3
|
+
export * from './Input/TextInput';
|
|
4
|
+
export * from './Input/NumberInput';
|
package/index.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import
|
|
2
|
-
export { Button };
|
|
3
|
-
import * as Input from './inputs/Input/Input.js';
|
|
4
|
-
export { Input };
|
|
5
|
-
import * as ImageInput from './inputs/ImageInput/ImageInput.js';
|
|
6
|
-
export { ImageInput };
|
|
7
|
-
import * as CheckboxList from './inputs/checkboxList/CheckboxList.js';
|
|
8
|
-
export { CheckboxList };
|
|
9
|
-
import * as Dropdown from './selectors/Dropdown/Dropdown.js';
|
|
10
|
-
export { Dropdown };
|
|
11
|
-
import * as Tooltip from './tooltip/Tooltip/Tooltip.js';
|
|
12
|
-
export { Tooltip };
|
|
1
|
+
import 'react/jsx-runtime';
|
|
13
2
|
import 'react';
|
|
14
|
-
|
|
3
|
+
import 'react-dom';
|
|
4
|
+
export { useDebounce } from './hooks/useDebounce.js';
|
|
5
|
+
export { useThrottle } from './hooks/useThrottle.js';
|
|
6
|
+
export { Tooltip } from './Tooltip/Tooltip.js';
|