@ncds/ui-admin 1.8.4 → 1.8.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/dist/cjs/assets/scripts/featuredIcon.js +87 -0
- package/dist/cjs/assets/scripts/notification/FloatingNotification.js +178 -0
- package/dist/cjs/assets/scripts/notification/FullWidthNotification.js +133 -0
- package/dist/cjs/assets/scripts/notification/MessageNotification.js +159 -0
- package/dist/cjs/assets/scripts/notification/Notification.js +120 -0
- package/dist/cjs/assets/scripts/notification/const/classNames.js +50 -0
- package/dist/cjs/assets/scripts/notification/const/icons.js +31 -0
- package/dist/cjs/assets/scripts/notification/const/index.js +87 -0
- package/dist/cjs/assets/scripts/notification/const/sizes.js +46 -0
- package/dist/cjs/assets/scripts/notification/const/types.js +14 -0
- package/dist/cjs/assets/scripts/notification/index.js +116 -0
- package/dist/cjs/assets/scripts/notification/positionSync.js +180 -0
- package/dist/cjs/assets/scripts/notification/utils.js +122 -0
- package/dist/cjs/assets/scripts/shared/ButtonCloseX.js +45 -0
- package/dist/cjs/assets/scripts/utils/sanitize.js +39 -0
- package/dist/cjs/src/components/data-display/data-grid/DataGrid.js +5 -1
- package/dist/cjs/src/components/data-display/table/Table.js +118 -96
- package/dist/cjs/src/components/data-display/table/useTableScrollbars.js +187 -0
- package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
- package/dist/cjs/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
- package/dist/cjs/src/components/forms-and-input/select-box/SelectBox.js +67 -29
- package/dist/cjs/src/components/forms-and-input/slider/Slider.js +2 -3
- package/dist/cjs/src/components/overlays/dropdown/Dropdown.js +47 -19
- package/dist/cjs/src/components/overlays/notification/CalloutNotification.js +25 -0
- package/dist/cjs/src/components/overlays/notification/FloatingNotification.js +86 -13
- package/dist/cjs/src/components/overlays/notification/Notification.js +7 -0
- package/dist/cjs/src/components/overlays/notification/host.js +12 -0
- package/dist/cjs/src/components/overlays/tooltip/Tooltip.js +57 -44
- package/dist/cjs/src/components/select-dropdown/SelectDropdown.js +2 -1
- package/dist/cjs/src/contexts/FloatingContext.js +11 -0
- package/dist/cjs/src/contexts/index.js +16 -0
- package/dist/cjs/src/hooks/index.js +11 -0
- package/dist/cjs/src/hooks/useFloatingPosition.js +78 -0
- package/dist/cjs/src/hooks/usePortalState.js +17 -0
- package/dist/cjs/src/types/component-meta.js +8 -1
- package/dist/cjs/src/utils/dropdown/maxSelection.js +35 -0
- package/dist/cjs/src/utils/dropdown/multiSelect.js +72 -15
- package/dist/esm/assets/scripts/featuredIcon.js +80 -0
- package/dist/esm/assets/scripts/notification/FloatingNotification.js +171 -0
- package/dist/esm/assets/scripts/notification/FullWidthNotification.js +126 -0
- package/dist/esm/assets/scripts/notification/MessageNotification.js +152 -0
- package/dist/esm/assets/scripts/notification/Notification.js +113 -0
- package/dist/esm/assets/scripts/notification/const/classNames.js +44 -0
- package/dist/esm/assets/scripts/notification/const/icons.js +25 -0
- package/dist/esm/assets/scripts/notification/const/index.js +4 -0
- package/dist/esm/assets/scripts/notification/const/sizes.js +40 -0
- package/dist/esm/assets/scripts/notification/const/types.js +8 -0
- package/dist/esm/assets/scripts/notification/index.js +10 -0
- package/dist/esm/assets/scripts/notification/positionSync.js +171 -0
- package/dist/esm/assets/scripts/notification/utils.js +109 -0
- package/dist/esm/assets/scripts/shared/ButtonCloseX.js +37 -0
- package/dist/esm/assets/scripts/utils/sanitize.js +31 -0
- package/dist/esm/src/components/data-display/data-grid/DataGrid.js +5 -1
- package/dist/esm/src/components/data-display/table/Table.js +118 -96
- package/dist/esm/src/components/data-display/table/useTableScrollbars.js +179 -0
- package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +11 -10
- package/dist/esm/src/components/forms-and-input/image-file-input/ImageFileInput.js +5 -2
- package/dist/esm/src/components/forms-and-input/select-box/SelectBox.js +67 -29
- package/dist/esm/src/components/forms-and-input/slider/Slider.js +1 -2
- package/dist/esm/src/components/overlays/dropdown/Dropdown.js +47 -19
- package/dist/esm/src/components/overlays/notification/CalloutNotification.js +19 -0
- package/dist/esm/src/components/overlays/notification/FloatingNotification.js +86 -14
- package/dist/esm/src/components/overlays/notification/Notification.js +7 -0
- package/dist/esm/src/components/overlays/notification/host.js +9 -0
- package/dist/esm/src/components/overlays/tooltip/Tooltip.js +58 -45
- package/dist/esm/src/components/select-dropdown/SelectDropdown.js +2 -1
- package/dist/esm/src/contexts/FloatingContext.js +4 -0
- package/dist/esm/src/contexts/index.js +1 -0
- package/dist/esm/src/hooks/index.js +1 -0
- package/dist/esm/src/hooks/useFloatingPosition.js +71 -0
- package/dist/esm/src/hooks/usePortalState.js +10 -0
- package/dist/esm/src/types/component-meta.js +5 -1
- package/dist/esm/src/utils/dropdown/maxSelection.js +27 -0
- package/dist/esm/src/utils/dropdown/multiSelect.js +70 -14
- package/dist/temp/assets/scripts/featuredIcon.d.ts +22 -0
- package/dist/temp/assets/scripts/featuredIcon.js +79 -0
- package/dist/temp/assets/scripts/notification/FloatingNotification.d.ts +24 -0
- package/dist/temp/assets/scripts/notification/FloatingNotification.js +156 -0
- package/dist/temp/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
- package/dist/temp/assets/scripts/notification/FullWidthNotification.js +111 -0
- package/dist/temp/assets/scripts/notification/MessageNotification.d.ts +22 -0
- package/dist/temp/assets/scripts/notification/MessageNotification.js +140 -0
- package/dist/temp/assets/scripts/notification/Notification.d.ts +22 -0
- package/dist/temp/assets/scripts/notification/Notification.js +112 -0
- package/dist/temp/assets/scripts/notification/const/classNames.d.ts +43 -0
- package/dist/temp/assets/scripts/notification/const/classNames.js +44 -0
- package/dist/temp/assets/scripts/notification/const/icons.d.ts +25 -0
- package/dist/temp/assets/scripts/notification/const/icons.js +25 -0
- package/dist/temp/assets/scripts/notification/const/index.d.ts +5 -0
- package/dist/temp/assets/scripts/notification/const/index.js +4 -0
- package/dist/temp/assets/scripts/notification/const/sizes.d.ts +32 -0
- package/dist/temp/assets/scripts/notification/const/sizes.js +40 -0
- package/dist/temp/assets/scripts/notification/const/types.d.ts +19 -0
- package/dist/temp/assets/scripts/notification/const/types.js +8 -0
- package/dist/temp/assets/scripts/notification/index.d.ts +8 -0
- package/dist/temp/assets/scripts/notification/index.js +10 -0
- package/dist/temp/assets/scripts/notification/positionSync.d.ts +50 -0
- package/dist/temp/assets/scripts/notification/positionSync.js +170 -0
- package/dist/temp/assets/scripts/notification/utils.d.ts +8 -0
- package/dist/temp/assets/scripts/notification/utils.js +115 -0
- package/dist/temp/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
- package/dist/temp/assets/scripts/shared/ButtonCloseX.js +33 -0
- package/dist/temp/assets/scripts/utils/sanitize.d.ts +22 -0
- package/dist/temp/assets/scripts/utils/sanitize.js +31 -0
- package/dist/temp/src/components/data-display/data-grid/DataGrid.js +1 -1
- package/dist/temp/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
- package/dist/temp/src/components/data-display/table/Table.d.ts +4 -1
- package/dist/temp/src/components/data-display/table/Table.js +53 -68
- package/dist/temp/src/components/data-display/table/types.d.ts +18 -0
- package/dist/temp/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
- package/dist/temp/src/components/data-display/table/useTableScrollbars.js +136 -0
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +7 -11
- package/dist/temp/src/components/forms-and-input/image-file-input/ImageFileInput.js +1 -1
- package/dist/temp/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
- package/dist/temp/src/components/forms-and-input/select-box/SelectBox.js +30 -3
- package/dist/temp/src/components/forms-and-input/slider/Slider.d.ts +0 -1
- package/dist/temp/src/components/forms-and-input/slider/Slider.js +0 -1
- package/dist/temp/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
- package/dist/temp/src/components/overlays/dropdown/Dropdown.js +35 -11
- package/dist/temp/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
- package/dist/temp/src/components/overlays/notification/CalloutNotification.js +6 -0
- package/dist/temp/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
- package/dist/temp/src/components/overlays/notification/FloatingNotification.js +81 -13
- package/dist/temp/src/components/overlays/notification/Notification.d.ts +18 -3
- package/dist/temp/src/components/overlays/notification/Notification.js +4 -0
- package/dist/temp/src/components/overlays/notification/host.d.ts +9 -0
- package/dist/temp/src/components/overlays/notification/host.js +9 -0
- package/dist/temp/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
- package/dist/temp/src/components/overlays/tooltip/Tooltip.js +25 -22
- package/dist/temp/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
- package/dist/temp/src/components/select-dropdown/SelectDropdown.js +2 -2
- package/dist/temp/src/contexts/FloatingContext.d.ts +6 -0
- package/dist/temp/src/contexts/FloatingContext.js +4 -0
- package/dist/temp/src/contexts/index.d.ts +1 -0
- package/dist/temp/src/contexts/index.js +1 -0
- package/dist/temp/src/hooks/index.d.ts +1 -0
- package/dist/temp/src/hooks/index.js +1 -0
- package/dist/temp/src/hooks/useFloatingPosition.d.ts +19 -0
- package/dist/temp/src/hooks/useFloatingPosition.js +55 -0
- package/dist/temp/src/hooks/usePortalState.d.ts +6 -0
- package/dist/temp/src/hooks/usePortalState.js +7 -0
- package/dist/temp/src/types/component-meta.d.ts +6 -2
- package/dist/temp/src/types/component-meta.js +14 -1
- package/dist/temp/src/utils/dropdown/maxSelection.d.ts +24 -0
- package/dist/temp/src/utils/dropdown/maxSelection.js +28 -0
- package/dist/temp/src/utils/dropdown/multiSelect.d.ts +42 -2
- package/dist/temp/src/utils/dropdown/multiSelect.js +66 -13
- package/dist/types/assets/scripts/featuredIcon.d.ts +22 -0
- package/dist/types/assets/scripts/notification/FloatingNotification.d.ts +24 -0
- package/dist/types/assets/scripts/notification/FullWidthNotification.d.ts +21 -0
- package/dist/types/assets/scripts/notification/MessageNotification.d.ts +22 -0
- package/dist/types/assets/scripts/notification/Notification.d.ts +22 -0
- package/dist/types/assets/scripts/notification/const/classNames.d.ts +43 -0
- package/dist/types/assets/scripts/notification/const/icons.d.ts +25 -0
- package/dist/types/assets/scripts/notification/const/index.d.ts +5 -0
- package/dist/types/assets/scripts/notification/const/sizes.d.ts +32 -0
- package/dist/types/assets/scripts/notification/const/types.d.ts +19 -0
- package/dist/types/assets/scripts/notification/index.d.ts +8 -0
- package/dist/types/assets/scripts/notification/positionSync.d.ts +50 -0
- package/dist/types/assets/scripts/notification/utils.d.ts +8 -0
- package/dist/types/assets/scripts/shared/ButtonCloseX.d.ts +5 -0
- package/dist/types/assets/scripts/utils/sanitize.d.ts +22 -0
- package/dist/types/src/components/data-display/data-grid/DataGrid.types.d.ts +7 -0
- package/dist/types/src/components/data-display/table/Table.d.ts +4 -1
- package/dist/types/src/components/data-display/table/types.d.ts +18 -0
- package/dist/types/src/components/data-display/table/useTableScrollbars.d.ts +25 -0
- package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +8 -0
- package/dist/types/src/components/forms-and-input/select-box/SelectBox.d.ts +13 -0
- package/dist/types/src/components/forms-and-input/slider/Slider.d.ts +0 -1
- package/dist/types/src/components/overlays/dropdown/Dropdown.d.ts +5 -0
- package/dist/types/src/components/overlays/notification/CalloutNotification.d.ts +9 -0
- package/dist/types/src/components/overlays/notification/FloatingNotification.d.ts +15 -0
- package/dist/types/src/components/overlays/notification/Notification.d.ts +18 -3
- package/dist/types/src/components/overlays/notification/host.d.ts +9 -0
- package/dist/types/src/components/overlays/tooltip/Tooltip.d.ts +5 -1
- package/dist/types/src/components/select-dropdown/SelectDropdown.d.ts +6 -0
- package/dist/types/src/contexts/FloatingContext.d.ts +6 -0
- package/dist/types/src/contexts/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts +1 -0
- package/dist/types/src/hooks/useFloatingPosition.d.ts +19 -0
- package/dist/types/src/hooks/usePortalState.d.ts +6 -0
- package/dist/types/src/types/component-meta.d.ts +6 -2
- package/dist/types/src/utils/dropdown/maxSelection.d.ts +24 -0
- package/dist/types/src/utils/dropdown/multiSelect.d.ts +42 -2
- package/dist/ui-admin/assets/styles/style.css +312 -64
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { AlertCircle, AlertCircleFill, HelpCircle, HelpCircleFill } from '@ncds/ui-admin-icon';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
@@ -70,6 +70,42 @@ const computePanelCoords = (prefer, anchor, panel) => {
|
|
|
70
70
|
calculatedPosition
|
|
71
71
|
};
|
|
72
72
|
};
|
|
73
|
+
const buildPanelStyle = (disablePortal, coords, visible, zIndex) => {
|
|
74
|
+
const opacity = visible ? 1 : 0;
|
|
75
|
+
if (disablePortal) return {
|
|
76
|
+
opacity
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
top: `${coords.top}px`,
|
|
80
|
+
left: `${coords.left}px`,
|
|
81
|
+
opacity,
|
|
82
|
+
...(zIndex && {
|
|
83
|
+
zIndex
|
|
84
|
+
})
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const renderIcon = (iconStyle, iconType, iconSize, iconColor) => {
|
|
88
|
+
if (iconStyle === 'help-circle') {
|
|
89
|
+
return iconType === 'stroke' ? _jsx(HelpCircle, {
|
|
90
|
+
width: iconSize,
|
|
91
|
+
height: iconSize,
|
|
92
|
+
color: iconColor
|
|
93
|
+
}) : _jsx(HelpCircleFill, {
|
|
94
|
+
width: iconSize,
|
|
95
|
+
height: iconSize,
|
|
96
|
+
color: iconColor
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return iconType === 'stroke' ? _jsx(AlertCircle, {
|
|
100
|
+
width: iconSize,
|
|
101
|
+
height: iconSize,
|
|
102
|
+
color: iconColor
|
|
103
|
+
}) : _jsx(AlertCircleFill, {
|
|
104
|
+
width: iconSize,
|
|
105
|
+
height: iconSize,
|
|
106
|
+
color: iconColor
|
|
107
|
+
});
|
|
108
|
+
};
|
|
73
109
|
export const Tooltip = _ref => {
|
|
74
110
|
let {
|
|
75
111
|
tooltipType = 'white',
|
|
@@ -83,7 +119,9 @@ export const Tooltip = _ref => {
|
|
|
83
119
|
iconColor = 'var(--gray-300)',
|
|
84
120
|
iconStyle = 'help-circle',
|
|
85
121
|
className,
|
|
86
|
-
zIndex
|
|
122
|
+
zIndex,
|
|
123
|
+
forceVisible,
|
|
124
|
+
disablePortal = false
|
|
87
125
|
} = _ref;
|
|
88
126
|
const iconSize = size === 'sm' ? ICON_SIZE_SM : ICON_SIZE_DEFAULT;
|
|
89
127
|
const anchorRef = useRef(null);
|
|
@@ -97,6 +135,7 @@ export const Tooltip = _ref => {
|
|
|
97
135
|
const [calculatedPosition, setCalculatedPosition] = useState(position === 'auto' ? 'bottom' : position);
|
|
98
136
|
const [isVisible, setIsVisible] = useState(false);
|
|
99
137
|
const [isManuallyClose, setIsManuallyClose] = useState(false);
|
|
138
|
+
const effectiveVisible = forceVisible ?? isVisible;
|
|
100
139
|
useEffect(() => {
|
|
101
140
|
setMounted(true);
|
|
102
141
|
return () => {
|
|
@@ -107,7 +146,7 @@ export const Tooltip = _ref => {
|
|
|
107
146
|
};
|
|
108
147
|
}, []);
|
|
109
148
|
const updatePosition = useCallback(() => {
|
|
110
|
-
if (!anchorRef.current || !panelRef.current) return;
|
|
149
|
+
if (disablePortal || !anchorRef.current || !panelRef.current) return;
|
|
111
150
|
const anchor = anchorRef.current.getBoundingClientRect();
|
|
112
151
|
const panel = panelRef.current.getBoundingClientRect();
|
|
113
152
|
const next = computePanelCoords(position, anchor, panel);
|
|
@@ -116,9 +155,9 @@ export const Tooltip = _ref => {
|
|
|
116
155
|
left: next.left
|
|
117
156
|
});
|
|
118
157
|
setCalculatedPosition(next.calculatedPosition);
|
|
119
|
-
}, [position]);
|
|
158
|
+
}, [position, disablePortal]);
|
|
120
159
|
useEffect(() => {
|
|
121
|
-
if (!
|
|
160
|
+
if (!effectiveVisible || disablePortal) return;
|
|
122
161
|
updatePosition();
|
|
123
162
|
window.addEventListener('resize', updatePosition, {
|
|
124
163
|
passive: true
|
|
@@ -133,13 +172,10 @@ export const Tooltip = _ref => {
|
|
|
133
172
|
capture: true
|
|
134
173
|
});
|
|
135
174
|
};
|
|
136
|
-
}, [
|
|
175
|
+
}, [effectiveVisible, updatePosition, disablePortal]);
|
|
137
176
|
const handleMouseEnter = useCallback(() => {
|
|
138
177
|
if (isManuallyClose) return;
|
|
139
|
-
// opacity 전환 전에 좌표 확정 (ref 가드는 updatePosition 내부)
|
|
140
178
|
updatePosition();
|
|
141
|
-
// 웹폰트 로드·max-content 재계산 등 비동기 layout 안정화 후 한 번 더 보정
|
|
142
|
-
// 빠른 hover in/out 시 이전 프레임 요청은 취소해 중복/unmount 후 실행 방지
|
|
143
179
|
if (rafIdRef.current !== null) cancelAnimationFrame(rafIdRef.current);
|
|
144
180
|
rafIdRef.current = requestAnimationFrame(() => {
|
|
145
181
|
rafIdRef.current = null;
|
|
@@ -163,18 +199,13 @@ export const Tooltip = _ref => {
|
|
|
163
199
|
'ncua-tooltip--stroke': iconType === 'stroke',
|
|
164
200
|
'ncua-tooltip--auto': position === 'auto'
|
|
165
201
|
}, className), [size, type, hideArrow, iconType, position, className]);
|
|
166
|
-
const panelClassName = useMemo(() => classNames(
|
|
167
|
-
'ncua-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
opacity: isVisible ? 1 : 0,
|
|
174
|
-
...(zIndex && {
|
|
175
|
-
zIndex
|
|
176
|
-
})
|
|
177
|
-
};
|
|
202
|
+
const panelClassName = useMemo(() => classNames({
|
|
203
|
+
'ncua-tooltip-panel': !disablePortal
|
|
204
|
+
}, 'ncua-tooltip__bg', `ncua-tooltip__bg--${tooltipType}`, `ncua-tooltip__bg--${finalPosition}`, {
|
|
205
|
+
'ncua-tooltip__bg--visible': effectiveVisible,
|
|
206
|
+
'ncua-tooltip__bg--force-hidden': isManuallyClose && !forceVisible
|
|
207
|
+
}), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal]);
|
|
208
|
+
const panelStyle = buildPanelStyle(disablePortal, coords, effectiveVisible, zIndex);
|
|
178
209
|
const portalTarget = mounted ? resolvePortalTarget() : null;
|
|
179
210
|
const panel = _jsxs("span", {
|
|
180
211
|
ref: panelRef,
|
|
@@ -194,29 +225,11 @@ export const Tooltip = _ref => {
|
|
|
194
225
|
"aria-label": "\uD234\uD301 \uB2EB\uAE30"
|
|
195
226
|
})]
|
|
196
227
|
});
|
|
197
|
-
return _jsxs(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
children: [iconStyle === 'help-circle' && (iconType === 'stroke' ? _jsx(HelpCircle, {
|
|
204
|
-
width: iconSize,
|
|
205
|
-
height: iconSize,
|
|
206
|
-
color: iconColor
|
|
207
|
-
}) : _jsx(HelpCircleFill, {
|
|
208
|
-
width: iconSize,
|
|
209
|
-
height: iconSize,
|
|
210
|
-
color: iconColor
|
|
211
|
-
})), iconStyle === 'alert-circle' && (iconType === 'stroke' ? _jsx(AlertCircle, {
|
|
212
|
-
width: iconSize,
|
|
213
|
-
height: iconSize,
|
|
214
|
-
color: iconColor
|
|
215
|
-
}) : _jsx(AlertCircleFill, {
|
|
216
|
-
width: iconSize,
|
|
217
|
-
height: iconSize,
|
|
218
|
-
color: iconColor
|
|
219
|
-
}))]
|
|
220
|
-
}), portalTarget && /*#__PURE__*/createPortal(panel, portalTarget)]
|
|
228
|
+
return _jsxs("span", {
|
|
229
|
+
ref: anchorRef,
|
|
230
|
+
className: tooltipClassName,
|
|
231
|
+
onMouseEnter: handleMouseEnter,
|
|
232
|
+
onMouseLeave: handleMouseLeave,
|
|
233
|
+
children: [renderIcon(iconStyle, iconType, iconSize, iconColor), disablePortal ? panel : portalTarget && /*#__PURE__*/createPortal(panel, portalTarget)]
|
|
221
234
|
});
|
|
222
235
|
};
|
|
@@ -20,6 +20,7 @@ const SelectDropdown = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
20
20
|
multiple = false,
|
|
21
21
|
showFooterButtons = false,
|
|
22
22
|
selectAllButtonText = '전체 선택',
|
|
23
|
+
showSelectAllAction = true,
|
|
23
24
|
onSelectAll,
|
|
24
25
|
onEdit,
|
|
25
26
|
onComplete,
|
|
@@ -85,7 +86,7 @@ const SelectDropdown = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
85
86
|
className: "ncua-select-dropdown__footer-buttons",
|
|
86
87
|
children: [_jsx("div", {
|
|
87
88
|
className: "ncua-select-dropdown__footer-left",
|
|
88
|
-
children: multiple && _jsx(Button, {
|
|
89
|
+
children: multiple && showSelectAllAction && _jsx(Button, {
|
|
89
90
|
label: selectAllButtonText,
|
|
90
91
|
hierarchy: "text",
|
|
91
92
|
size: "xs",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './FloatingContext';
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { useLayoutEffect, useState } from 'react';
|
|
2
|
+
const FLOATING_Z_INDEX = 1500;
|
|
3
|
+
export const useFloatingPosition = _ref => {
|
|
4
|
+
let {
|
|
5
|
+
enabled,
|
|
6
|
+
isOpen,
|
|
7
|
+
triggerRef,
|
|
8
|
+
floatingRef,
|
|
9
|
+
direction,
|
|
10
|
+
offset = 4,
|
|
11
|
+
align = 'left',
|
|
12
|
+
matchTriggerWidth = false
|
|
13
|
+
} = _ref;
|
|
14
|
+
const [style, setStyle] = useState(null);
|
|
15
|
+
useLayoutEffect(() => {
|
|
16
|
+
if (!enabled || !isOpen) {
|
|
17
|
+
setStyle(null);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const trigger = triggerRef.current;
|
|
21
|
+
if (!trigger) return;
|
|
22
|
+
const calculatePosition = (trigger, floatingHeight, floatingWidth) => {
|
|
23
|
+
const top = direction === 'up' ? trigger.top - floatingHeight - offset : trigger.bottom + offset;
|
|
24
|
+
const left = align === 'right' ? trigger.right - floatingWidth : trigger.left;
|
|
25
|
+
return {
|
|
26
|
+
top,
|
|
27
|
+
left
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const update = () => {
|
|
31
|
+
const t = triggerRef.current;
|
|
32
|
+
const f = floatingRef.current;
|
|
33
|
+
if (!t) return;
|
|
34
|
+
const r = t.getBoundingClientRect();
|
|
35
|
+
const floatingHeight = f?.offsetHeight ?? 0;
|
|
36
|
+
const floatingWidth = matchTriggerWidth ? r.width : f?.offsetWidth ?? r.width;
|
|
37
|
+
const {
|
|
38
|
+
top,
|
|
39
|
+
left
|
|
40
|
+
} = calculatePosition(r, floatingHeight, floatingWidth);
|
|
41
|
+
// matchTriggerWidth=true: width는 max-content(콘텐츠 자연 너비), min-width만 trigger 너비로 보장.
|
|
42
|
+
// 옵션이 짧으면 trigger 너비, 길면 자연 확장 — 기존 absolute 모드와 동일한 UX.
|
|
43
|
+
setStyle({
|
|
44
|
+
position: 'fixed',
|
|
45
|
+
top,
|
|
46
|
+
left,
|
|
47
|
+
zIndex: FLOATING_Z_INDEX,
|
|
48
|
+
...(matchTriggerWidth ? {
|
|
49
|
+
minWidth: r.width
|
|
50
|
+
} : {})
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
update();
|
|
54
|
+
window.addEventListener('scroll', update, true);
|
|
55
|
+
window.addEventListener('resize', update);
|
|
56
|
+
const triggerObserver = new ResizeObserver(update);
|
|
57
|
+
triggerObserver.observe(trigger);
|
|
58
|
+
let floatingObserver;
|
|
59
|
+
if (floatingRef.current) {
|
|
60
|
+
floatingObserver = new ResizeObserver(update);
|
|
61
|
+
floatingObserver.observe(floatingRef.current);
|
|
62
|
+
}
|
|
63
|
+
return () => {
|
|
64
|
+
window.removeEventListener('scroll', update, true);
|
|
65
|
+
window.removeEventListener('resize', update);
|
|
66
|
+
triggerObserver.disconnect();
|
|
67
|
+
floatingObserver?.disconnect();
|
|
68
|
+
};
|
|
69
|
+
}, [enabled, isOpen, direction, offset, align, matchTriggerWidth, triggerRef, floatingRef]);
|
|
70
|
+
return style;
|
|
71
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useFloatingContext } from '../contexts/FloatingContext';
|
|
2
|
+
export const usePortalState = usePortalProp => {
|
|
3
|
+
const floatingContext = useFloatingContext();
|
|
4
|
+
const shouldPortal = usePortalProp ?? floatingContext?.preferPortal ?? false;
|
|
5
|
+
const portalContainer = floatingContext?.portalContainer ?? (typeof document !== 'undefined' ? document.body : null);
|
|
6
|
+
return {
|
|
7
|
+
shouldPortal,
|
|
8
|
+
portalContainer
|
|
9
|
+
};
|
|
10
|
+
};
|
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
/** AI 에이전트용 컴포넌트 메타데이터 타입 */
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* 컴포넌트 카테고리 — `src/components/` 하위 디렉토리명과 동일.
|
|
4
|
+
* 타입과 런타임 값을 한 곳에서 정의해 추가/변경 시 동기화 누락을 막는다.
|
|
5
|
+
*/
|
|
6
|
+
export const COMPONENT_CATEGORIES = ['action', 'data-display', 'feedback-and-status', 'forms-and-input', 'image-and-icons', 'layout', 'navigation', 'overlays'];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SelectBox / ComboBox 공통 — 최대 선택 개수 제한 활성 여부 판정.
|
|
3
|
+
*
|
|
4
|
+
* React 훅(`useMultiSelect`)과 vanilla 드롭다운(`DropdownModel`)가 동일 로직을 공유하므로
|
|
5
|
+
* 두 곳에 정의가 갈라지지 않도록 단일 source-of-truth로 둔다.
|
|
6
|
+
*
|
|
7
|
+
* **계약**
|
|
8
|
+
* - `0` 이상의 정수 → 활성 (단, `0`은 모든 새 선택을 차단하는 의미)
|
|
9
|
+
* - 음수 / 비정수 / `undefined` / `null` → 비활성 (제한 없는 것과 동일)
|
|
10
|
+
*
|
|
11
|
+
* `0`을 활성으로 보는 이유: 호출자가 `maxSelection={0}`을 명시적으로 전달했을 때
|
|
12
|
+
* "선택 자체를 막겠다"는 의도로 해석하기 위함. 음수는 일반적으로 "값 없음"의 sentinel이므로 비활성.
|
|
13
|
+
*/
|
|
14
|
+
export const isMaxSelectionLimitActive = maxSelection => typeof maxSelection === 'number' && Number.isInteger(maxSelection) && maxSelection >= 0;
|
|
15
|
+
/**
|
|
16
|
+
* 현재 선택 길이가 최대치에 도달했는지 단일 함수로 판정.
|
|
17
|
+
*
|
|
18
|
+
* 호출 측에서 `>=`을 직접 작성하지 않도록 의미를 노출하고, type predicate가 내부에서 적용되어
|
|
19
|
+
* `maxSelection`의 narrowing이 보장된다 (외부 predicate 호출로 인한 narrowing 누락 방지).
|
|
20
|
+
*
|
|
21
|
+
* - 제한 비활성(음수 / null / undefined / 비정수): 항상 `false`.
|
|
22
|
+
* - 활성: `currentLength >= maxSelection`.
|
|
23
|
+
*/
|
|
24
|
+
export const isMaxSelectionReached = (currentLength, maxSelection) => {
|
|
25
|
+
if (!isMaxSelectionLimitActive(maxSelection)) return false;
|
|
26
|
+
return currentLength >= maxSelection;
|
|
27
|
+
};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useCallback, useMemo } from 'react';
|
|
2
|
+
import { isMaxSelectionLimitActive, isMaxSelectionReached } from './maxSelection';
|
|
1
3
|
/**
|
|
2
4
|
* 전체 선택 상태를 확인하는 유틸 함수
|
|
3
5
|
*/
|
|
@@ -44,25 +46,79 @@ export const removeTagFromSelected = (selectedValues, tagIdToRemove) => {
|
|
|
44
46
|
return selectedValues.filter(id => id !== tagIdToRemove);
|
|
45
47
|
};
|
|
46
48
|
/**
|
|
47
|
-
*
|
|
49
|
+
* 최대 선택 개수 관련 응집을 별도 훅으로 분리.
|
|
50
|
+
*
|
|
51
|
+
* - `isMaxSelectionActive`: 제한 활성 여부.
|
|
52
|
+
* - `isMaxReached`: 현재 선택 길이가 최대치에 도달했는지.
|
|
53
|
+
* - `canAdd(currentLength)`: 새 항목 추가가 허용되는지 (도달 시 `false`).
|
|
54
|
+
*
|
|
55
|
+
* 도메인 규칙(`>=` 비교 등)을 외부로 노출하지 않고 헬퍼를 통해 의미만 반환한다.
|
|
56
|
+
* `useMultiSelect`가 내부에서 사용하지만, 단독으로도 필요한 호출자가 쓸 수 있게 export한다.
|
|
48
57
|
*/
|
|
49
|
-
export const
|
|
58
|
+
export const useMaxSelection = (selectedValues, maxSelection) => {
|
|
59
|
+
const isMaxSelectionActive = isMaxSelectionLimitActive(maxSelection);
|
|
60
|
+
const isMaxReached = isMaxSelectionReached(selectedValues.length, maxSelection);
|
|
61
|
+
const canAdd = useCallback(currentLength => !isMaxSelectionReached(currentLength, maxSelection), [maxSelection]);
|
|
62
|
+
return {
|
|
63
|
+
isMaxSelectionActive,
|
|
64
|
+
isMaxReached,
|
|
65
|
+
canAdd
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* 전체 선택 관련 로직을 한 번에 처리하는 커스텀 훅.
|
|
70
|
+
*
|
|
71
|
+
* 시그니처는 호출자가 maxSelection만 지정하더라도 텍스트 라벨을 건너뛰지 않도록 옵션 객체로 받는다.
|
|
72
|
+
*
|
|
73
|
+
* **반환의 `tryToggle`**: multiple 토글 결과를 직접 돌려준다.
|
|
74
|
+
* - 새 배열 반환: 정상 토글 (추가 또는 해제).
|
|
75
|
+
* - `null` 반환: 최대 개수 도달로 추가가 차단됨 (호출자는 변경 없이 종료해야 함).
|
|
76
|
+
*
|
|
77
|
+
* 호출자가 `>= maxSelection` 같은 도메인 규칙을 직접 검사하지 않도록 결과만 노출한다.
|
|
78
|
+
*/
|
|
79
|
+
export const useMultiSelect = function (selectedValues, options) {
|
|
80
|
+
let config = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
|
|
81
|
+
const {
|
|
82
|
+
selectText,
|
|
83
|
+
deselectText,
|
|
84
|
+
maxSelection
|
|
85
|
+
} = config;
|
|
50
86
|
const isAllSelected = isAllItemsSelected(selectedValues, options);
|
|
51
87
|
const buttonText = getSelectAllButtonText(selectedValues, options, selectText, deselectText);
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
88
|
+
const {
|
|
89
|
+
isMaxSelectionActive,
|
|
90
|
+
isMaxReached,
|
|
91
|
+
canAdd
|
|
92
|
+
} = useMaxSelection(selectedValues, maxSelection);
|
|
93
|
+
const toggleSelectAll = useCallback(() => handleSelectAllItems(selectedValues, options), [selectedValues, options]);
|
|
94
|
+
const getSelectedTagsData = useCallback(() => getSelectedTags(selectedValues, options), [selectedValues, options]);
|
|
95
|
+
const removeTag = useCallback(tagId => removeTagFromSelected(selectedValues, tagId), [selectedValues]);
|
|
96
|
+
/**
|
|
97
|
+
* multiple 선택 토글을 한 번에 처리한다.
|
|
98
|
+
* - 이미 포함된 항목: 해제 (배열에서 제거하여 반환).
|
|
99
|
+
* - 미포함 항목: 추가 시도. 최대치 도달이면 `null` 반환(차단).
|
|
100
|
+
*
|
|
101
|
+
* 호출 측은 `null` 여부만 확인하면 되며 maxSelection 도메인 규칙을 알 필요가 없다.
|
|
102
|
+
*/
|
|
103
|
+
const tryToggle = useCallback((optionId, currentArray) => {
|
|
104
|
+
const base = Array.isArray(currentArray) ? currentArray : [];
|
|
105
|
+
const idx = base.indexOf(optionId);
|
|
106
|
+
if (idx > -1) {
|
|
107
|
+
const next = [...base];
|
|
108
|
+
next.splice(idx, 1);
|
|
109
|
+
return next;
|
|
110
|
+
}
|
|
111
|
+
if (!canAdd(base.length)) return null;
|
|
112
|
+
return [...base, optionId];
|
|
113
|
+
}, [canAdd]);
|
|
114
|
+
return useMemo(() => ({
|
|
62
115
|
isAllSelected,
|
|
63
116
|
buttonText,
|
|
64
117
|
toggleSelectAll,
|
|
65
118
|
getSelectedTagsData,
|
|
66
|
-
removeTag
|
|
67
|
-
|
|
119
|
+
removeTag,
|
|
120
|
+
isMaxSelectionActive,
|
|
121
|
+
isMaxReached,
|
|
122
|
+
tryToggle
|
|
123
|
+
}), [isAllSelected, buttonText, toggleSelectAll, getSelectedTagsData, removeTag, isMaxSelectionActive, isMaxReached, tryToggle]);
|
|
68
124
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type FeaturedIconTheme = 'light-circle' | 'dark-circle' | 'outline-circle' | 'square-outline';
|
|
2
|
+
export type FeaturedIconColor = 'neutral' | 'error' | 'warning' | 'success';
|
|
3
|
+
export type FeaturedIconSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
4
|
+
export interface FeaturedIconOptions {
|
|
5
|
+
svgString: string;
|
|
6
|
+
theme?: FeaturedIconTheme;
|
|
7
|
+
color?: FeaturedIconColor;
|
|
8
|
+
size?: FeaturedIconSize;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class FeaturedIcon {
|
|
12
|
+
private element;
|
|
13
|
+
private options;
|
|
14
|
+
constructor(options: FeaturedIconOptions);
|
|
15
|
+
private createElement;
|
|
16
|
+
private addSizeToSvg;
|
|
17
|
+
getElement(): HTMLElement;
|
|
18
|
+
updateColor(color: FeaturedIconColor): void;
|
|
19
|
+
updateSize(size: FeaturedIconSize): void;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
static create(options: FeaturedIconOptions): FeaturedIcon;
|
|
22
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// React 컴포넌트와 동일하게 구성
|
|
2
|
+
const iconSizeMap = {
|
|
3
|
+
sm: 16,
|
|
4
|
+
md: 20,
|
|
5
|
+
lg: 24,
|
|
6
|
+
xl: 28,
|
|
7
|
+
};
|
|
8
|
+
export class FeaturedIcon {
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = {
|
|
11
|
+
theme: 'light-circle',
|
|
12
|
+
color: 'neutral',
|
|
13
|
+
size: 'sm',
|
|
14
|
+
className: '',
|
|
15
|
+
...options,
|
|
16
|
+
};
|
|
17
|
+
this.element = this.createElement();
|
|
18
|
+
}
|
|
19
|
+
// svgString ( ICON SVG ) 을 받아서 요소를 생성
|
|
20
|
+
createElement() {
|
|
21
|
+
const { theme, color, size, className, svgString } = this.options;
|
|
22
|
+
// React FeaturedIcon과 동일한 클래스 구조
|
|
23
|
+
const classes = [
|
|
24
|
+
'ncua-featured-icon',
|
|
25
|
+
`ncua-featured-icon--${theme}`,
|
|
26
|
+
`ncua-featured-icon--${color}`,
|
|
27
|
+
`ncua-featured-icon--${size}`,
|
|
28
|
+
];
|
|
29
|
+
if (className) {
|
|
30
|
+
classes.push(className);
|
|
31
|
+
}
|
|
32
|
+
const div = document.createElement('div');
|
|
33
|
+
div.className = classes.join(' ');
|
|
34
|
+
// outline-circle 테마일 때 추가 요소들 (React 컴포넌트와 동일)
|
|
35
|
+
if (theme === 'outline-circle') {
|
|
36
|
+
const innerOutline = document.createElement('div');
|
|
37
|
+
innerOutline.className = 'ncua-featured-icon__outline ncua-featured-icon__outline--inner';
|
|
38
|
+
div.appendChild(innerOutline);
|
|
39
|
+
const outerOutline = document.createElement('div');
|
|
40
|
+
outerOutline.className = 'ncua-featured-icon__outline ncua-featured-icon__outline--outer';
|
|
41
|
+
div.appendChild(outerOutline);
|
|
42
|
+
}
|
|
43
|
+
// SVG 아이콘 추가
|
|
44
|
+
const iconSize = iconSizeMap[size];
|
|
45
|
+
const svgWithSize = this.addSizeToSvg(svgString, iconSize);
|
|
46
|
+
div.innerHTML += svgWithSize;
|
|
47
|
+
return div;
|
|
48
|
+
}
|
|
49
|
+
addSizeToSvg(svgString, size) {
|
|
50
|
+
return svgString.replace(/<svg([^>]*)>/, `<svg$1 width="${size}" height="${size}">`);
|
|
51
|
+
}
|
|
52
|
+
// Public methods
|
|
53
|
+
getElement() {
|
|
54
|
+
return this.element;
|
|
55
|
+
}
|
|
56
|
+
updateColor(color) {
|
|
57
|
+
this.element.className = this.element.className.replace(/ncua-featured-icon--(neutral|error|warning|success)/, `ncua-featured-icon--${color}`);
|
|
58
|
+
this.options.color = color;
|
|
59
|
+
}
|
|
60
|
+
updateSize(size) {
|
|
61
|
+
const iconSize = iconSizeMap[size];
|
|
62
|
+
this.element.className = this.element.className.replace(/ncua-featured-icon--(sm|md|lg|xl)/, `ncua-featured-icon--${size}`);
|
|
63
|
+
const svgIcon = this.element.querySelector('svg');
|
|
64
|
+
if (svgIcon) {
|
|
65
|
+
svgIcon.setAttribute('width', String(iconSize));
|
|
66
|
+
svgIcon.setAttribute('height', String(iconSize));
|
|
67
|
+
}
|
|
68
|
+
this.options.size = size;
|
|
69
|
+
}
|
|
70
|
+
destroy() {
|
|
71
|
+
if (this.element.parentNode) {
|
|
72
|
+
this.element.parentNode.removeChild(this.element);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Static factory method
|
|
76
|
+
static create(options) {
|
|
77
|
+
return new FeaturedIcon(options);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { BaseNotificationOptions } from './const';
|
|
2
|
+
export interface FloatingNotificationOptions extends BaseNotificationOptions {
|
|
3
|
+
type?: 'floating';
|
|
4
|
+
}
|
|
5
|
+
export declare class FloatingNotification {
|
|
6
|
+
private element;
|
|
7
|
+
private options;
|
|
8
|
+
private autoCloseTimer?;
|
|
9
|
+
private featuredIcon?;
|
|
10
|
+
private mobileCleanup?;
|
|
11
|
+
constructor(options: FloatingNotificationOptions);
|
|
12
|
+
private createElement;
|
|
13
|
+
private buildTemplate;
|
|
14
|
+
private renderCloseButton;
|
|
15
|
+
private setupMobileListener;
|
|
16
|
+
private updateMobileStyles;
|
|
17
|
+
private bindEvents;
|
|
18
|
+
private setupAutoClose;
|
|
19
|
+
getElement(): HTMLElement;
|
|
20
|
+
appendTo(parent: HTMLElement): void;
|
|
21
|
+
remove(): void;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
static create(options: FloatingNotificationOptions): FloatingNotification;
|
|
24
|
+
}
|