@ncds/ui-admin 1.8.7 → 1.8.9
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/src/components/data-display/table/Table.js +2 -0
- package/dist/cjs/src/components/data-display/table/useTableScrollbars.js +6 -2
- package/dist/cjs/src/components/feedback-and-status/badge/Badge.js +19 -0
- package/dist/cjs/src/components/forms-and-input/combo-box/ComboBox.js +3 -1
- package/dist/cjs/src/components/forms-and-input/image-file-input/ImageFileInput.js +30 -25
- package/dist/cjs/src/components/forms-and-input/select-box/SelectBox.js +3 -1
- package/dist/cjs/src/components/overlays/tooltip/Tooltip.js +5 -4
- package/dist/cjs/src/components/select-dropdown/SelectDropdown.js +1 -1
- package/dist/esm/src/components/data-display/table/Table.js +2 -0
- package/dist/esm/src/components/data-display/table/useTableScrollbars.js +6 -2
- package/dist/esm/src/components/feedback-and-status/badge/Badge.js +19 -0
- package/dist/esm/src/components/forms-and-input/combo-box/ComboBox.js +3 -1
- package/dist/esm/src/components/forms-and-input/image-file-input/ImageFileInput.js +30 -25
- package/dist/esm/src/components/forms-and-input/select-box/SelectBox.js +3 -1
- package/dist/esm/src/components/overlays/tooltip/Tooltip.js +5 -4
- package/dist/esm/src/components/select-dropdown/SelectDropdown.js +1 -1
- package/dist/temp/src/components/data-display/table/Table.js +2 -0
- package/dist/temp/src/components/data-display/table/useTableScrollbars.d.ts +3 -1
- package/dist/temp/src/components/data-display/table/useTableScrollbars.js +7 -3
- package/dist/temp/src/components/feedback-and-status/badge/Badge.d.ts +3 -2
- package/dist/temp/src/components/feedback-and-status/badge/Badge.js +10 -0
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.d.ts +4 -0
- package/dist/temp/src/components/forms-and-input/combo-box/ComboBox.js +3 -2
- package/dist/temp/src/components/forms-and-input/image-file-input/ImageFileInput.js +17 -14
- package/dist/temp/src/components/forms-and-input/select-box/SelectBox.d.ts +4 -0
- package/dist/temp/src/components/forms-and-input/select-box/SelectBox.js +3 -2
- package/dist/temp/src/components/overlays/tooltip/Tooltip.d.ts +2 -1
- package/dist/temp/src/components/overlays/tooltip/Tooltip.js +4 -4
- package/dist/temp/src/components/select-dropdown/SelectDropdown.d.ts +1 -1
- package/dist/temp/src/components/select-dropdown/SelectDropdown.js +1 -1
- package/dist/types/src/components/data-display/table/useTableScrollbars.d.ts +3 -1
- package/dist/types/src/components/feedback-and-status/badge/Badge.d.ts +3 -2
- package/dist/types/src/components/forms-and-input/combo-box/ComboBox.d.ts +4 -0
- package/dist/types/src/components/forms-and-input/select-box/SelectBox.d.ts +4 -0
- package/dist/types/src/components/overlays/tooltip/Tooltip.d.ts +2 -1
- package/dist/types/src/components/select-dropdown/SelectDropdown.d.ts +1 -1
- package/dist/ui-admin/assets/styles/style.css +71 -4
- package/package.json +2 -2
|
@@ -281,8 +281,10 @@ const TableComponent = /*#__PURE__*/(0, _react.forwardRef)((_ref0, ref) => {
|
|
|
281
281
|
handleThumbMouseDown
|
|
282
282
|
} = (0, _useTableScrollbars.useTableVerticalScrollbar)({
|
|
283
283
|
enabled: fixedScrollEnabled,
|
|
284
|
+
layoutKey: horizontalScroll,
|
|
284
285
|
scrollContainerRef,
|
|
285
286
|
scrollAreaRef,
|
|
287
|
+
scrollbarRef,
|
|
286
288
|
thumbRef
|
|
287
289
|
});
|
|
288
290
|
const {
|
|
@@ -72,10 +72,13 @@ const startDrag = (e, options) => {
|
|
|
72
72
|
const useTableVerticalScrollbar = _ref => {
|
|
73
73
|
let {
|
|
74
74
|
enabled,
|
|
75
|
+
layoutKey,
|
|
75
76
|
scrollContainerRef,
|
|
76
77
|
scrollAreaRef,
|
|
78
|
+
scrollbarRef,
|
|
77
79
|
thumbRef
|
|
78
80
|
} = _ref;
|
|
81
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: layoutKey는 직접 참조하지 않지만 horizontalScroll 토글로 스크롤바 DOM이 리마운트될 때 effect를 재바인딩하기 위한 트리거로 의도적으로 포함한다.
|
|
79
82
|
(0, _react.useEffect)(() => {
|
|
80
83
|
if (!enabled) return;
|
|
81
84
|
const scrollEl = scrollContainerRef.current;
|
|
@@ -91,7 +94,7 @@ const useTableVerticalScrollbar = _ref => {
|
|
|
91
94
|
thumbEl.style.height = '0';
|
|
92
95
|
return;
|
|
93
96
|
}
|
|
94
|
-
const trackHeight = (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
|
|
97
|
+
const trackHeight = scrollbarRef.current?.clientHeight || (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
|
|
95
98
|
const thumbHeight = Math.max(SCROLLBAR_THUMB_MIN_HEIGHT, clientHeight / scrollHeight * trackHeight);
|
|
96
99
|
const thumbTop = scrollTop / (scrollHeight - clientHeight) * (trackHeight - thumbHeight);
|
|
97
100
|
thumbEl.style.height = `${thumbHeight}px`;
|
|
@@ -103,12 +106,13 @@ const useTableVerticalScrollbar = _ref => {
|
|
|
103
106
|
const observer = new ResizeObserver(update);
|
|
104
107
|
observer.observe(scrollEl);
|
|
105
108
|
if (scrollAreaRef.current) observer.observe(scrollAreaRef.current);
|
|
109
|
+
if (scrollbarRef.current) observer.observe(scrollbarRef.current);
|
|
106
110
|
update();
|
|
107
111
|
return () => {
|
|
108
112
|
scrollEl.removeEventListener('scroll', update);
|
|
109
113
|
observer.disconnect();
|
|
110
114
|
};
|
|
111
|
-
}, [enabled, scrollContainerRef, scrollAreaRef, thumbRef]);
|
|
115
|
+
}, [enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef]);
|
|
112
116
|
const handleThumbMouseDown = e => {
|
|
113
117
|
const scrollEl = scrollContainerRef.current;
|
|
114
118
|
const thumbEl = thumbRef.current;
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.Badge = void 0;
|
|
7
7
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
var _uiAdminIcon = require("@ncds/ui-admin-icon");
|
|
8
9
|
var _classnames = _interopRequireDefault(require("classnames"));
|
|
9
10
|
var _utils = require("./utils");
|
|
10
11
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -12,6 +13,11 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
12
13
|
* 뱃지 컴포넌트의 아이콘은 디자인 시스템에서 12px 고정으로 정의되어 있습니다.
|
|
13
14
|
*/
|
|
14
15
|
const BADGE_ICON_SIZE = 12;
|
|
16
|
+
/** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
|
|
17
|
+
const NEW_BADGE_ICON_SIZE = {
|
|
18
|
+
sm: 12,
|
|
19
|
+
md: 16
|
|
20
|
+
};
|
|
15
21
|
const Badge = _ref => {
|
|
16
22
|
let {
|
|
17
23
|
label,
|
|
@@ -22,6 +28,19 @@ const Badge = _ref => {
|
|
|
22
28
|
trailingIcon,
|
|
23
29
|
size = 'xs'
|
|
24
30
|
} = _ref;
|
|
31
|
+
// new-badge: 신규 콘텐츠 'N' 마크 전용 타입. label·color·leadingIcon·trailingIcon은 무시되고
|
|
32
|
+
// 색은 --pink-600 으로 고정된다. (디자이너 명세: Icon/FeaturedIcon/Badge로 대체 불가한 전용 표시)
|
|
33
|
+
if (type === 'new-badge') {
|
|
34
|
+
const newBadgeSize = size === 'md' ? 'md' : 'sm';
|
|
35
|
+
const iconSize = NEW_BADGE_ICON_SIZE[newBadgeSize];
|
|
36
|
+
return (0, _jsxRuntime.jsx)("span", {
|
|
37
|
+
className: (0, _classnames.default)('ncua-badge', 'ncua-badge--new-badge', `ncua-badge--new-badge-${newBadgeSize}`, className),
|
|
38
|
+
children: (0, _jsxRuntime.jsx)(_uiAdminIcon.New, {
|
|
39
|
+
width: iconSize,
|
|
40
|
+
height: iconSize
|
|
41
|
+
})
|
|
42
|
+
});
|
|
43
|
+
}
|
|
25
44
|
return (0, _jsxRuntime.jsxs)("span", {
|
|
26
45
|
className: (0, _classnames.default)('ncua-badge', `ncua-badge--${type}`, `ncua-badge--${color}`, `ncua-badge--${size}`, className),
|
|
27
46
|
children: [leadingIcon && (0, _utils.sideSlotRender)({
|
|
@@ -50,6 +50,7 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref, r
|
|
|
50
50
|
showFooterButtons = false,
|
|
51
51
|
maxSelection,
|
|
52
52
|
onEdit,
|
|
53
|
+
onComplete,
|
|
53
54
|
...props
|
|
54
55
|
} = _ref;
|
|
55
56
|
const internalRef = (0, _react.useRef)(null);
|
|
@@ -183,12 +184,13 @@ const ComboBox = exports.ComboBox = /*#__PURE__*/(0, _react.forwardRef)((_ref, r
|
|
|
183
184
|
const handleEdit = () => {
|
|
184
185
|
onEdit?.();
|
|
185
186
|
};
|
|
186
|
-
const handleComplete =
|
|
187
|
+
const handleComplete = completedValue => {
|
|
187
188
|
if (!multiple) return;
|
|
188
189
|
const tags = getSelectedTagsData();
|
|
189
190
|
setSelectedTags(tags);
|
|
190
191
|
setInputValue('');
|
|
191
192
|
closeDropdown();
|
|
193
|
+
onComplete?.(completedValue);
|
|
192
194
|
};
|
|
193
195
|
const handleRemoveTag = tagId => {
|
|
194
196
|
if (!onChange) return;
|
|
@@ -14,6 +14,20 @@ var _shared = require("../../shared");
|
|
|
14
14
|
var _FileInput = require("../file-input/FileInput");
|
|
15
15
|
var _ImagePreview = require("./components/ImagePreview");
|
|
16
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
17
|
+
const toInvalidFile = (file, errorType) => ({
|
|
18
|
+
name: file.name,
|
|
19
|
+
size: file.size,
|
|
20
|
+
type: file.type,
|
|
21
|
+
lastModified: file.lastModified,
|
|
22
|
+
webkitRelativePath: file.webkitRelativePath,
|
|
23
|
+
arrayBuffer: () => file.arrayBuffer(),
|
|
24
|
+
stream: () => file.stream(),
|
|
25
|
+
text: () => file.text(),
|
|
26
|
+
slice: function () {
|
|
27
|
+
return file.slice(...arguments);
|
|
28
|
+
},
|
|
29
|
+
errorType
|
|
30
|
+
});
|
|
17
31
|
const ImageFileInput = exports.ImageFileInput = /*#__PURE__*/(0, _react.forwardRef)((_ref, ref) => {
|
|
18
32
|
let {
|
|
19
33
|
size = 'sm',
|
|
@@ -81,25 +95,16 @@ const ImageFileInput = exports.ImageFileInput = /*#__PURE__*/(0, _react.forwardR
|
|
|
81
95
|
const invalidFiles = [];
|
|
82
96
|
for (const file of fileList) {
|
|
83
97
|
if (files.some(f => f.name === file.name && f.size === file.size)) {
|
|
84
|
-
invalidFiles.push(
|
|
85
|
-
...file,
|
|
86
|
-
errorType: _FileInput.FileInputErrorType.ALREADY_UPLOADED
|
|
87
|
-
});
|
|
98
|
+
invalidFiles.push(toInvalidFile(file, _FileInput.FileInputErrorType.ALREADY_UPLOADED));
|
|
88
99
|
continue;
|
|
89
100
|
}
|
|
90
101
|
if (!!maxFileSize && file.size > maxFileSize) {
|
|
91
|
-
invalidFiles.push(
|
|
92
|
-
...file,
|
|
93
|
-
errorType: _FileInput.FileInputErrorType.EXCEED_MAX_FILE_SIZE
|
|
94
|
-
});
|
|
102
|
+
invalidFiles.push(toInvalidFile(file, _FileInput.FileInputErrorType.EXCEED_MAX_FILE_SIZE));
|
|
95
103
|
continue;
|
|
96
104
|
}
|
|
97
105
|
// Skip max count check if maxFileCount is 1 (allow replacement)
|
|
98
106
|
if (!!maxFileCount && maxFileCount !== 1 && files.length + validFiles.length >= maxFileCount) {
|
|
99
|
-
invalidFiles.push(
|
|
100
|
-
...file,
|
|
101
|
-
errorType: _FileInput.FileInputErrorType.EXCEED_MAX_FILE_COUNT
|
|
102
|
-
});
|
|
107
|
+
invalidFiles.push(toInvalidFile(file, _FileInput.FileInputErrorType.EXCEED_MAX_FILE_COUNT));
|
|
103
108
|
continue;
|
|
104
109
|
}
|
|
105
110
|
validFiles.push(file);
|
|
@@ -144,7 +149,7 @@ const ImageFileInput = exports.ImageFileInput = /*#__PURE__*/(0, _react.forwardR
|
|
|
144
149
|
position: "bottom",
|
|
145
150
|
tooltipType: "black",
|
|
146
151
|
forceVisible: isButtonHovered && !disabled,
|
|
147
|
-
|
|
152
|
+
panelClassName: (0, _classnames.default)('ncua-image-file-input__slot-tooltip', `ncua-image-file-input__slot-tooltip--${size}`)
|
|
148
153
|
})]
|
|
149
154
|
})]
|
|
150
155
|
});
|
|
@@ -163,19 +168,19 @@ const ImageFileInput = exports.ImageFileInput = /*#__PURE__*/(0, _react.forwardR
|
|
|
163
168
|
className: (0, _classnames.default)('ncua-image-file-input', `ncua-image-file-input--${size}`, {
|
|
164
169
|
destructive: destructive
|
|
165
170
|
}),
|
|
166
|
-
children: [renderImagePreview(files),
|
|
171
|
+
children: [renderImagePreview(files), (0, _jsxRuntime.jsx)("input", {
|
|
172
|
+
hidden: true,
|
|
173
|
+
ref: fileInputRef,
|
|
174
|
+
type: "file",
|
|
175
|
+
accept: accept,
|
|
176
|
+
multiple: multiple,
|
|
177
|
+
onChange: handleFileChange,
|
|
178
|
+
tabIndex: -1,
|
|
179
|
+
"aria-hidden": "true",
|
|
180
|
+
...props
|
|
181
|
+
}), showFileInput && (0, _jsxRuntime.jsxs)("div", {
|
|
167
182
|
className: (0, _classnames.default)('ncua-file-input', `ncua-file-input--${size}`),
|
|
168
|
-
children: [(0, _jsxRuntime.
|
|
169
|
-
hidden: true,
|
|
170
|
-
ref: fileInputRef,
|
|
171
|
-
type: "file",
|
|
172
|
-
accept: accept,
|
|
173
|
-
multiple: multiple,
|
|
174
|
-
onChange: handleFileChange,
|
|
175
|
-
tabIndex: -1,
|
|
176
|
-
"aria-hidden": "true",
|
|
177
|
-
...props
|
|
178
|
-
}), (0, _jsxRuntime.jsxs)("div", {
|
|
183
|
+
children: [(0, _jsxRuntime.jsxs)("div", {
|
|
179
184
|
className: "ncua-file-input__input-container",
|
|
180
185
|
children: [(0, _jsxRuntime.jsxs)("div", {
|
|
181
186
|
className: "ncua-file-input__label",
|
|
@@ -89,6 +89,7 @@ const SelectBox = exports.SelectBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2
|
|
|
89
89
|
register,
|
|
90
90
|
onChange,
|
|
91
91
|
onEdit,
|
|
92
|
+
onComplete,
|
|
92
93
|
...props
|
|
93
94
|
} = _ref2;
|
|
94
95
|
const internalRef = (0, _react.useRef)(null);
|
|
@@ -173,10 +174,11 @@ const SelectBox = exports.SelectBox = /*#__PURE__*/(0, _react.forwardRef)((_ref2
|
|
|
173
174
|
const handleEdit = () => {
|
|
174
175
|
onEdit?.();
|
|
175
176
|
};
|
|
176
|
-
const handleComplete =
|
|
177
|
+
const handleComplete = completedValue => {
|
|
177
178
|
if (multiple) {
|
|
178
179
|
const tags = getSelectedTagsData();
|
|
179
180
|
setSelectedTags(tags);
|
|
181
|
+
onComplete?.(completedValue);
|
|
180
182
|
}
|
|
181
183
|
closeDropdown();
|
|
182
184
|
};
|
|
@@ -128,7 +128,8 @@ const Tooltip = _ref => {
|
|
|
128
128
|
className,
|
|
129
129
|
zIndex,
|
|
130
130
|
forceVisible,
|
|
131
|
-
disablePortal = false
|
|
131
|
+
disablePortal = false,
|
|
132
|
+
panelClassName
|
|
132
133
|
} = _ref;
|
|
133
134
|
const iconSize = size === 'sm' ? ICON_SIZE_SM : ICON_SIZE_DEFAULT;
|
|
134
135
|
const anchorRef = (0, _react.useRef)(null);
|
|
@@ -206,17 +207,17 @@ const Tooltip = _ref => {
|
|
|
206
207
|
'ncua-tooltip--stroke': iconType === 'stroke',
|
|
207
208
|
'ncua-tooltip--auto': position === 'auto'
|
|
208
209
|
}, className), [size, type, hideArrow, iconType, position, className]);
|
|
209
|
-
const
|
|
210
|
+
const panelClasses = (0, _react.useMemo)(() => (0, _classnames.default)({
|
|
210
211
|
'ncua-tooltip-panel': !disablePortal
|
|
211
212
|
}, 'ncua-tooltip__bg', `ncua-tooltip__bg--${tooltipType}`, `ncua-tooltip__bg--${finalPosition}`, {
|
|
212
213
|
'ncua-tooltip__bg--visible': effectiveVisible,
|
|
213
214
|
'ncua-tooltip__bg--force-hidden': isManuallyClose && !forceVisible
|
|
214
|
-
}), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal]);
|
|
215
|
+
}, panelClassName), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal, panelClassName]);
|
|
215
216
|
const panelStyle = buildPanelStyle(disablePortal, coords, effectiveVisible, zIndex);
|
|
216
217
|
const portalTarget = mounted ? resolvePortalTarget() : null;
|
|
217
218
|
const panel = (0, _jsxRuntime.jsxs)("span", {
|
|
218
219
|
ref: panelRef,
|
|
219
|
-
className:
|
|
220
|
+
className: panelClasses,
|
|
220
221
|
style: panelStyle,
|
|
221
222
|
children: [title && (0, _jsxRuntime.jsx)("span", {
|
|
222
223
|
className: "ncua-tooltip__title",
|
|
@@ -111,7 +111,7 @@ const SelectDropdown = exports.SelectDropdown = /*#__PURE__*/(0, _react.forwardR
|
|
|
111
111
|
label: "\uC120\uD0DD \uC644\uB8CC",
|
|
112
112
|
hierarchy: "secondary",
|
|
113
113
|
size: "xs",
|
|
114
|
-
onClick: onComplete
|
|
114
|
+
onClick: () => onComplete?.(value ?? [])
|
|
115
115
|
})]
|
|
116
116
|
})]
|
|
117
117
|
})
|
|
@@ -274,8 +274,10 @@ const TableComponent = /*#__PURE__*/forwardRef((_ref0, ref) => {
|
|
|
274
274
|
handleThumbMouseDown
|
|
275
275
|
} = useTableVerticalScrollbar({
|
|
276
276
|
enabled: fixedScrollEnabled,
|
|
277
|
+
layoutKey: horizontalScroll,
|
|
277
278
|
scrollContainerRef,
|
|
278
279
|
scrollAreaRef,
|
|
280
|
+
scrollbarRef,
|
|
279
281
|
thumbRef
|
|
280
282
|
});
|
|
281
283
|
const {
|
|
@@ -66,10 +66,13 @@ const startDrag = (e, options) => {
|
|
|
66
66
|
export const useTableVerticalScrollbar = _ref => {
|
|
67
67
|
let {
|
|
68
68
|
enabled,
|
|
69
|
+
layoutKey,
|
|
69
70
|
scrollContainerRef,
|
|
70
71
|
scrollAreaRef,
|
|
72
|
+
scrollbarRef,
|
|
71
73
|
thumbRef
|
|
72
74
|
} = _ref;
|
|
75
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: layoutKey는 직접 참조하지 않지만 horizontalScroll 토글로 스크롤바 DOM이 리마운트될 때 effect를 재바인딩하기 위한 트리거로 의도적으로 포함한다.
|
|
73
76
|
useEffect(() => {
|
|
74
77
|
if (!enabled) return;
|
|
75
78
|
const scrollEl = scrollContainerRef.current;
|
|
@@ -85,7 +88,7 @@ export const useTableVerticalScrollbar = _ref => {
|
|
|
85
88
|
thumbEl.style.height = '0';
|
|
86
89
|
return;
|
|
87
90
|
}
|
|
88
|
-
const trackHeight = (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
|
|
91
|
+
const trackHeight = scrollbarRef.current?.clientHeight || (scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
|
|
89
92
|
const thumbHeight = Math.max(SCROLLBAR_THUMB_MIN_HEIGHT, clientHeight / scrollHeight * trackHeight);
|
|
90
93
|
const thumbTop = scrollTop / (scrollHeight - clientHeight) * (trackHeight - thumbHeight);
|
|
91
94
|
thumbEl.style.height = `${thumbHeight}px`;
|
|
@@ -97,12 +100,13 @@ export const useTableVerticalScrollbar = _ref => {
|
|
|
97
100
|
const observer = new ResizeObserver(update);
|
|
98
101
|
observer.observe(scrollEl);
|
|
99
102
|
if (scrollAreaRef.current) observer.observe(scrollAreaRef.current);
|
|
103
|
+
if (scrollbarRef.current) observer.observe(scrollbarRef.current);
|
|
100
104
|
update();
|
|
101
105
|
return () => {
|
|
102
106
|
scrollEl.removeEventListener('scroll', update);
|
|
103
107
|
observer.disconnect();
|
|
104
108
|
};
|
|
105
|
-
}, [enabled, scrollContainerRef, scrollAreaRef, thumbRef]);
|
|
109
|
+
}, [enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef]);
|
|
106
110
|
const handleThumbMouseDown = e => {
|
|
107
111
|
const scrollEl = scrollContainerRef.current;
|
|
108
112
|
const thumbEl = thumbRef.current;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { New } from '@ncds/ui-admin-icon';
|
|
2
3
|
import classNames from 'classnames';
|
|
3
4
|
import { sideSlotRender } from './utils';
|
|
4
5
|
/**
|
|
5
6
|
* 뱃지 컴포넌트의 아이콘은 디자인 시스템에서 12px 고정으로 정의되어 있습니다.
|
|
6
7
|
*/
|
|
7
8
|
const BADGE_ICON_SIZE = 12;
|
|
9
|
+
/** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
|
|
10
|
+
const NEW_BADGE_ICON_SIZE = {
|
|
11
|
+
sm: 12,
|
|
12
|
+
md: 16
|
|
13
|
+
};
|
|
8
14
|
const Badge = _ref => {
|
|
9
15
|
let {
|
|
10
16
|
label,
|
|
@@ -15,6 +21,19 @@ const Badge = _ref => {
|
|
|
15
21
|
trailingIcon,
|
|
16
22
|
size = 'xs'
|
|
17
23
|
} = _ref;
|
|
24
|
+
// new-badge: 신규 콘텐츠 'N' 마크 전용 타입. label·color·leadingIcon·trailingIcon은 무시되고
|
|
25
|
+
// 색은 --pink-600 으로 고정된다. (디자이너 명세: Icon/FeaturedIcon/Badge로 대체 불가한 전용 표시)
|
|
26
|
+
if (type === 'new-badge') {
|
|
27
|
+
const newBadgeSize = size === 'md' ? 'md' : 'sm';
|
|
28
|
+
const iconSize = NEW_BADGE_ICON_SIZE[newBadgeSize];
|
|
29
|
+
return _jsx("span", {
|
|
30
|
+
className: classNames('ncua-badge', 'ncua-badge--new-badge', `ncua-badge--new-badge-${newBadgeSize}`, className),
|
|
31
|
+
children: _jsx(New, {
|
|
32
|
+
width: iconSize,
|
|
33
|
+
height: iconSize
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
}
|
|
18
37
|
return _jsxs("span", {
|
|
19
38
|
className: classNames('ncua-badge', `ncua-badge--${type}`, `ncua-badge--${color}`, `ncua-badge--${size}`, className),
|
|
20
39
|
children: [leadingIcon && sideSlotRender({
|
|
@@ -43,6 +43,7 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
43
43
|
showFooterButtons = false,
|
|
44
44
|
maxSelection,
|
|
45
45
|
onEdit,
|
|
46
|
+
onComplete,
|
|
46
47
|
...props
|
|
47
48
|
} = _ref;
|
|
48
49
|
const internalRef = useRef(null);
|
|
@@ -176,12 +177,13 @@ const ComboBox = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
176
177
|
const handleEdit = () => {
|
|
177
178
|
onEdit?.();
|
|
178
179
|
};
|
|
179
|
-
const handleComplete =
|
|
180
|
+
const handleComplete = completedValue => {
|
|
180
181
|
if (!multiple) return;
|
|
181
182
|
const tags = getSelectedTagsData();
|
|
182
183
|
setSelectedTags(tags);
|
|
183
184
|
setInputValue('');
|
|
184
185
|
closeDropdown();
|
|
186
|
+
onComplete?.(completedValue);
|
|
185
187
|
};
|
|
186
188
|
const handleRemoveTag = tagId => {
|
|
187
189
|
if (!onChange) return;
|
|
@@ -7,6 +7,20 @@ import { Tooltip } from '../../overlays/tooltip';
|
|
|
7
7
|
import { HintText, Label } from '../../shared';
|
|
8
8
|
import { FileInputErrorType as ImageFileInputErrorType } from '../file-input/FileInput';
|
|
9
9
|
import { ImagePreview } from './components/ImagePreview';
|
|
10
|
+
const toInvalidFile = (file, errorType) => ({
|
|
11
|
+
name: file.name,
|
|
12
|
+
size: file.size,
|
|
13
|
+
type: file.type,
|
|
14
|
+
lastModified: file.lastModified,
|
|
15
|
+
webkitRelativePath: file.webkitRelativePath,
|
|
16
|
+
arrayBuffer: () => file.arrayBuffer(),
|
|
17
|
+
stream: () => file.stream(),
|
|
18
|
+
text: () => file.text(),
|
|
19
|
+
slice: function () {
|
|
20
|
+
return file.slice(...arguments);
|
|
21
|
+
},
|
|
22
|
+
errorType
|
|
23
|
+
});
|
|
10
24
|
export const ImageFileInput = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
11
25
|
let {
|
|
12
26
|
size = 'sm',
|
|
@@ -74,25 +88,16 @@ export const ImageFileInput = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
74
88
|
const invalidFiles = [];
|
|
75
89
|
for (const file of fileList) {
|
|
76
90
|
if (files.some(f => f.name === file.name && f.size === file.size)) {
|
|
77
|
-
invalidFiles.push(
|
|
78
|
-
...file,
|
|
79
|
-
errorType: ImageFileInputErrorType.ALREADY_UPLOADED
|
|
80
|
-
});
|
|
91
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.ALREADY_UPLOADED));
|
|
81
92
|
continue;
|
|
82
93
|
}
|
|
83
94
|
if (!!maxFileSize && file.size > maxFileSize) {
|
|
84
|
-
invalidFiles.push(
|
|
85
|
-
...file,
|
|
86
|
-
errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_SIZE
|
|
87
|
-
});
|
|
95
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.EXCEED_MAX_FILE_SIZE));
|
|
88
96
|
continue;
|
|
89
97
|
}
|
|
90
98
|
// Skip max count check if maxFileCount is 1 (allow replacement)
|
|
91
99
|
if (!!maxFileCount && maxFileCount !== 1 && files.length + validFiles.length >= maxFileCount) {
|
|
92
|
-
invalidFiles.push(
|
|
93
|
-
...file,
|
|
94
|
-
errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_COUNT
|
|
95
|
-
});
|
|
100
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.EXCEED_MAX_FILE_COUNT));
|
|
96
101
|
continue;
|
|
97
102
|
}
|
|
98
103
|
validFiles.push(file);
|
|
@@ -137,7 +142,7 @@ export const ImageFileInput = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
137
142
|
position: "bottom",
|
|
138
143
|
tooltipType: "black",
|
|
139
144
|
forceVisible: isButtonHovered && !disabled,
|
|
140
|
-
|
|
145
|
+
panelClassName: classNames('ncua-image-file-input__slot-tooltip', `ncua-image-file-input__slot-tooltip--${size}`)
|
|
141
146
|
})]
|
|
142
147
|
})]
|
|
143
148
|
});
|
|
@@ -156,19 +161,19 @@ export const ImageFileInput = /*#__PURE__*/forwardRef((_ref, ref) => {
|
|
|
156
161
|
className: classNames('ncua-image-file-input', `ncua-image-file-input--${size}`, {
|
|
157
162
|
destructive: destructive
|
|
158
163
|
}),
|
|
159
|
-
children: [renderImagePreview(files),
|
|
164
|
+
children: [renderImagePreview(files), _jsx("input", {
|
|
165
|
+
hidden: true,
|
|
166
|
+
ref: fileInputRef,
|
|
167
|
+
type: "file",
|
|
168
|
+
accept: accept,
|
|
169
|
+
multiple: multiple,
|
|
170
|
+
onChange: handleFileChange,
|
|
171
|
+
tabIndex: -1,
|
|
172
|
+
"aria-hidden": "true",
|
|
173
|
+
...props
|
|
174
|
+
}), showFileInput && _jsxs("div", {
|
|
160
175
|
className: classNames('ncua-file-input', `ncua-file-input--${size}`),
|
|
161
|
-
children: [
|
|
162
|
-
hidden: true,
|
|
163
|
-
ref: fileInputRef,
|
|
164
|
-
type: "file",
|
|
165
|
-
accept: accept,
|
|
166
|
-
multiple: multiple,
|
|
167
|
-
onChange: handleFileChange,
|
|
168
|
-
tabIndex: -1,
|
|
169
|
-
"aria-hidden": "true",
|
|
170
|
-
...props
|
|
171
|
-
}), _jsxs("div", {
|
|
176
|
+
children: [_jsxs("div", {
|
|
172
177
|
className: "ncua-file-input__input-container",
|
|
173
178
|
children: [_jsxs("div", {
|
|
174
179
|
className: "ncua-file-input__label",
|
|
@@ -82,6 +82,7 @@ const SelectBox = /*#__PURE__*/forwardRef((_ref2, ref
|
|
|
82
82
|
register,
|
|
83
83
|
onChange,
|
|
84
84
|
onEdit,
|
|
85
|
+
onComplete,
|
|
85
86
|
...props
|
|
86
87
|
} = _ref2;
|
|
87
88
|
const internalRef = useRef(null);
|
|
@@ -166,10 +167,11 @@ const SelectBox = /*#__PURE__*/forwardRef((_ref2, ref
|
|
|
166
167
|
const handleEdit = () => {
|
|
167
168
|
onEdit?.();
|
|
168
169
|
};
|
|
169
|
-
const handleComplete =
|
|
170
|
+
const handleComplete = completedValue => {
|
|
170
171
|
if (multiple) {
|
|
171
172
|
const tags = getSelectedTagsData();
|
|
172
173
|
setSelectedTags(tags);
|
|
174
|
+
onComplete?.(completedValue);
|
|
173
175
|
}
|
|
174
176
|
closeDropdown();
|
|
175
177
|
};
|
|
@@ -121,7 +121,8 @@ export const Tooltip = _ref => {
|
|
|
121
121
|
className,
|
|
122
122
|
zIndex,
|
|
123
123
|
forceVisible,
|
|
124
|
-
disablePortal = false
|
|
124
|
+
disablePortal = false,
|
|
125
|
+
panelClassName
|
|
125
126
|
} = _ref;
|
|
126
127
|
const iconSize = size === 'sm' ? ICON_SIZE_SM : ICON_SIZE_DEFAULT;
|
|
127
128
|
const anchorRef = useRef(null);
|
|
@@ -199,17 +200,17 @@ export const Tooltip = _ref => {
|
|
|
199
200
|
'ncua-tooltip--stroke': iconType === 'stroke',
|
|
200
201
|
'ncua-tooltip--auto': position === 'auto'
|
|
201
202
|
}, className), [size, type, hideArrow, iconType, position, className]);
|
|
202
|
-
const
|
|
203
|
+
const panelClasses = useMemo(() => classNames({
|
|
203
204
|
'ncua-tooltip-panel': !disablePortal
|
|
204
205
|
}, 'ncua-tooltip__bg', `ncua-tooltip__bg--${tooltipType}`, `ncua-tooltip__bg--${finalPosition}`, {
|
|
205
206
|
'ncua-tooltip__bg--visible': effectiveVisible,
|
|
206
207
|
'ncua-tooltip__bg--force-hidden': isManuallyClose && !forceVisible
|
|
207
|
-
}), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal]);
|
|
208
|
+
}, panelClassName), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal, panelClassName]);
|
|
208
209
|
const panelStyle = buildPanelStyle(disablePortal, coords, effectiveVisible, zIndex);
|
|
209
210
|
const portalTarget = mounted ? resolvePortalTarget() : null;
|
|
210
211
|
const panel = _jsxs("span", {
|
|
211
212
|
ref: panelRef,
|
|
212
|
-
className:
|
|
213
|
+
className: panelClasses,
|
|
213
214
|
style: panelStyle,
|
|
214
215
|
children: [title && _jsx("span", {
|
|
215
216
|
className: "ncua-tooltip__title",
|
|
@@ -136,8 +136,10 @@ const TableComponent = forwardRef(({ type = 'horizontal', fixedHeader = false, m
|
|
|
136
136
|
const fixedScrollEnabled = !!(fixedHeader && maxHeight);
|
|
137
137
|
const { handleThumbMouseDown } = useTableVerticalScrollbar({
|
|
138
138
|
enabled: fixedScrollEnabled,
|
|
139
|
+
layoutKey: horizontalScroll,
|
|
139
140
|
scrollContainerRef,
|
|
140
141
|
scrollAreaRef,
|
|
142
|
+
scrollbarRef,
|
|
141
143
|
thumbRef,
|
|
142
144
|
});
|
|
143
145
|
const { handleHThumbMouseDown } = useTableHorizontalScrollbar({
|
|
@@ -6,11 +6,13 @@ export declare const H_SCROLLBAR_SIDE_GAP = 8;
|
|
|
6
6
|
export declare const SCROLLBAR_TRACK_OFFSET = 16;
|
|
7
7
|
type VerticalScrollbarOptions = {
|
|
8
8
|
enabled: boolean;
|
|
9
|
+
layoutKey: unknown;
|
|
9
10
|
scrollContainerRef: RefObject<HTMLDivElement | null>;
|
|
10
11
|
scrollAreaRef: RefObject<HTMLDivElement | null>;
|
|
12
|
+
scrollbarRef: RefObject<HTMLDivElement | null>;
|
|
11
13
|
thumbRef: RefObject<HTMLDivElement | null>;
|
|
12
14
|
};
|
|
13
|
-
export declare const useTableVerticalScrollbar: ({ enabled, scrollContainerRef, scrollAreaRef, thumbRef, }: VerticalScrollbarOptions) => {
|
|
15
|
+
export declare const useTableVerticalScrollbar: ({ enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef, }: VerticalScrollbarOptions) => {
|
|
14
16
|
handleThumbMouseDown: (e: MouseEvent<HTMLDivElement>) => void;
|
|
15
17
|
};
|
|
16
18
|
type HorizontalScrollbarOptions = {
|
|
@@ -51,7 +51,8 @@ const startDrag = (e, options) => {
|
|
|
51
51
|
document.addEventListener('mousemove', onMove);
|
|
52
52
|
document.addEventListener('mouseup', onUp);
|
|
53
53
|
};
|
|
54
|
-
export const useTableVerticalScrollbar = ({ enabled, scrollContainerRef, scrollAreaRef, thumbRef, }) => {
|
|
54
|
+
export const useTableVerticalScrollbar = ({ enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef, }) => {
|
|
55
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: layoutKey는 직접 참조하지 않지만 horizontalScroll 토글로 스크롤바 DOM이 리마운트될 때 effect를 재바인딩하기 위한 트리거로 의도적으로 포함한다.
|
|
55
56
|
useEffect(() => {
|
|
56
57
|
if (!enabled)
|
|
57
58
|
return;
|
|
@@ -65,7 +66,8 @@ export const useTableVerticalScrollbar = ({ enabled, scrollContainerRef, scrollA
|
|
|
65
66
|
thumbEl.style.height = '0';
|
|
66
67
|
return;
|
|
67
68
|
}
|
|
68
|
-
const trackHeight =
|
|
69
|
+
const trackHeight = scrollbarRef.current?.clientHeight ||
|
|
70
|
+
(scrollAreaRef.current?.clientHeight ?? clientHeight) - TABLE_HEADER_HEIGHT - SCROLLBAR_TRACK_OFFSET;
|
|
69
71
|
const thumbHeight = Math.max(SCROLLBAR_THUMB_MIN_HEIGHT, (clientHeight / scrollHeight) * trackHeight);
|
|
70
72
|
const thumbTop = (scrollTop / (scrollHeight - clientHeight)) * (trackHeight - thumbHeight);
|
|
71
73
|
thumbEl.style.height = `${thumbHeight}px`;
|
|
@@ -76,12 +78,14 @@ export const useTableVerticalScrollbar = ({ enabled, scrollContainerRef, scrollA
|
|
|
76
78
|
observer.observe(scrollEl);
|
|
77
79
|
if (scrollAreaRef.current)
|
|
78
80
|
observer.observe(scrollAreaRef.current);
|
|
81
|
+
if (scrollbarRef.current)
|
|
82
|
+
observer.observe(scrollbarRef.current);
|
|
79
83
|
update();
|
|
80
84
|
return () => {
|
|
81
85
|
scrollEl.removeEventListener('scroll', update);
|
|
82
86
|
observer.disconnect();
|
|
83
87
|
};
|
|
84
|
-
}, [enabled, scrollContainerRef, scrollAreaRef, thumbRef]);
|
|
88
|
+
}, [enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef]);
|
|
85
89
|
const handleThumbMouseDown = (e) => {
|
|
86
90
|
const scrollEl = scrollContainerRef.current;
|
|
87
91
|
const thumbEl = thumbRef.current;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ColorTone } from '../../../../constant/color';
|
|
2
2
|
import type { Size } from '../../../../constant/size';
|
|
3
3
|
import type { SideSlotType } from '../../../types/side-slot';
|
|
4
|
-
type BadgeType = 'pill-outline' | 'pill-dark-color';
|
|
4
|
+
type BadgeType = 'pill-outline' | 'pill-dark-color' | 'new-badge';
|
|
5
5
|
type BadgeColor = Extract<ColorTone, 'neutral' | 'error' | 'warning' | 'success' | 'blue' | 'pink' | 'disabled'>;
|
|
6
6
|
type BadgeSize = Extract<Size, 'xs' | 'sm' | 'md'>;
|
|
7
7
|
type BadgeProps = {
|
|
8
|
-
|
|
8
|
+
/** new-badge 타입에서는 사용되지 않는다(무시됨). 그 외 타입에서는 필수로 전달한다. */
|
|
9
|
+
label?: string;
|
|
9
10
|
type?: BadgeType;
|
|
10
11
|
color?: BadgeColor;
|
|
11
12
|
className?: string;
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { New } from '@ncds/ui-admin-icon';
|
|
2
3
|
import classNames from 'classnames';
|
|
3
4
|
import { sideSlotRender } from './utils';
|
|
4
5
|
/**
|
|
5
6
|
* 뱃지 컴포넌트의 아이콘은 디자인 시스템에서 12px 고정으로 정의되어 있습니다.
|
|
6
7
|
*/
|
|
7
8
|
const BADGE_ICON_SIZE = 12;
|
|
9
|
+
/** new-badge 박스 안에 들어가는 'N' 아이콘 크기. 박스는 sm 16px / md 20px 이며 아이콘은 그보다 작다(패딩 포함). */
|
|
10
|
+
const NEW_BADGE_ICON_SIZE = { sm: 12, md: 16 };
|
|
8
11
|
const Badge = ({ label, type = 'pill-outline', color = 'neutral', className, leadingIcon, trailingIcon, size = 'xs', }) => {
|
|
12
|
+
// new-badge: 신규 콘텐츠 'N' 마크 전용 타입. label·color·leadingIcon·trailingIcon은 무시되고
|
|
13
|
+
// 색은 --pink-600 으로 고정된다. (디자이너 명세: Icon/FeaturedIcon/Badge로 대체 불가한 전용 표시)
|
|
14
|
+
if (type === 'new-badge') {
|
|
15
|
+
const newBadgeSize = size === 'md' ? 'md' : 'sm';
|
|
16
|
+
const iconSize = NEW_BADGE_ICON_SIZE[newBadgeSize];
|
|
17
|
+
return (_jsx("span", { className: classNames('ncua-badge', 'ncua-badge--new-badge', `ncua-badge--new-badge-${newBadgeSize}`, className), children: _jsx(New, { width: iconSize, height: iconSize }) }));
|
|
18
|
+
}
|
|
9
19
|
return (_jsxs("span", { className: classNames('ncua-badge', `ncua-badge--${type}`, `ncua-badge--${color}`, `ncua-badge--${size}`, className), children: [leadingIcon && sideSlotRender({ slot: leadingIcon, defaultIconSize: BADGE_ICON_SIZE }), _jsx("span", { className: "ncua-badge__label", children: label }), trailingIcon && sideSlotRender({ slot: trailingIcon, defaultIconSize: BADGE_ICON_SIZE })] }));
|
|
10
20
|
};
|
|
11
21
|
export { Badge };
|
|
@@ -30,6 +30,10 @@ interface ComboBoxProps extends Omit<ComponentPropsWithRef<'div'>, 'size' | 'onC
|
|
|
30
30
|
*/
|
|
31
31
|
maxSelection?: number | null;
|
|
32
32
|
onEdit?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* 완료 버튼 클릭 시 호출되는 콜백 — 현재 선택된 값을 인자로 받음 (multiple 모드에서만 트리거)
|
|
35
|
+
*/
|
|
36
|
+
onComplete?: OptionChangeHandler;
|
|
33
37
|
}
|
|
34
38
|
declare const ComboBox: import("react").ForwardRefExoticComponent<Omit<ComboBoxProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
35
39
|
export { defaultMaxHeight, ComboBox };
|
|
@@ -20,7 +20,7 @@ const notifyFormChange = (register, multiple, newValue) => {
|
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
};
|
|
23
|
-
const ComboBox = forwardRef(({ placeholder = '검색하세요', id, className, hintText, children, size = 'xs', destructive = false, value, optionItems = [], onChange, onSearch, disabled = false, register, maxHeight = defaultMaxHeight, searchValue = '', label, required = false, multiple = false, showFooterButtons = false, maxSelection, onEdit, ...props }, ref) => {
|
|
23
|
+
const ComboBox = forwardRef(({ placeholder = '검색하세요', id, className, hintText, children, size = 'xs', destructive = false, value, optionItems = [], onChange, onSearch, disabled = false, register, maxHeight = defaultMaxHeight, searchValue = '', label, required = false, multiple = false, showFooterButtons = false, maxSelection, onEdit, onComplete, ...props }, ref) => {
|
|
24
24
|
const internalRef = useRef(null);
|
|
25
25
|
const dropdownRef = useRef(null);
|
|
26
26
|
const inputRef = useRef(null);
|
|
@@ -122,13 +122,14 @@ const ComboBox = forwardRef(({ placeholder = '검색하세요', id, className, h
|
|
|
122
122
|
const handleEdit = () => {
|
|
123
123
|
onEdit?.();
|
|
124
124
|
};
|
|
125
|
-
const handleComplete = () => {
|
|
125
|
+
const handleComplete = (completedValue) => {
|
|
126
126
|
if (!multiple)
|
|
127
127
|
return;
|
|
128
128
|
const tags = getSelectedTagsData();
|
|
129
129
|
setSelectedTags(tags);
|
|
130
130
|
setInputValue('');
|
|
131
131
|
closeDropdown();
|
|
132
|
+
onComplete?.(completedValue);
|
|
132
133
|
};
|
|
133
134
|
const handleRemoveTag = (tagId) => {
|
|
134
135
|
if (!onChange)
|
|
@@ -7,6 +7,18 @@ import { Tooltip } from '../../overlays/tooltip';
|
|
|
7
7
|
import { HintText, Label } from '../../shared';
|
|
8
8
|
import { FileInputErrorType as ImageFileInputErrorType } from '../file-input/FileInput';
|
|
9
9
|
import { ImagePreview } from './components/ImagePreview';
|
|
10
|
+
const toInvalidFile = (file, errorType) => ({
|
|
11
|
+
name: file.name,
|
|
12
|
+
size: file.size,
|
|
13
|
+
type: file.type,
|
|
14
|
+
lastModified: file.lastModified,
|
|
15
|
+
webkitRelativePath: file.webkitRelativePath,
|
|
16
|
+
arrayBuffer: () => file.arrayBuffer(),
|
|
17
|
+
stream: () => file.stream(),
|
|
18
|
+
text: () => file.text(),
|
|
19
|
+
slice: (...args) => file.slice(...args),
|
|
20
|
+
errorType,
|
|
21
|
+
});
|
|
10
22
|
export const ImageFileInput = forwardRef(({ size = 'sm', accept, multiple = false, maxFileSize, maxFileCount, value, onChange, onFileSelect, onFail, buttonLabel = '파일 찾기', imagePreviewTooltipLabel = '이미지 업로드', disabled, label, hintItems, validation, destructive, isRequired, showHelpIcon, hintText, showFileTagList = true, showHintText = true, showFileInput = true, ...props }, ref) => {
|
|
11
23
|
const fileInputRef = useRef(null);
|
|
12
24
|
useImperativeHandle(ref, () => fileInputRef.current);
|
|
@@ -48,25 +60,16 @@ export const ImageFileInput = forwardRef(({ size = 'sm', accept, multiple = fals
|
|
|
48
60
|
const invalidFiles = [];
|
|
49
61
|
for (const file of fileList) {
|
|
50
62
|
if (files.some((f) => f.name === file.name && f.size === file.size)) {
|
|
51
|
-
invalidFiles.push(
|
|
52
|
-
...file,
|
|
53
|
-
errorType: ImageFileInputErrorType.ALREADY_UPLOADED,
|
|
54
|
-
});
|
|
63
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.ALREADY_UPLOADED));
|
|
55
64
|
continue;
|
|
56
65
|
}
|
|
57
66
|
if (!!maxFileSize && file.size > maxFileSize) {
|
|
58
|
-
invalidFiles.push(
|
|
59
|
-
...file,
|
|
60
|
-
errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_SIZE,
|
|
61
|
-
});
|
|
67
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.EXCEED_MAX_FILE_SIZE));
|
|
62
68
|
continue;
|
|
63
69
|
}
|
|
64
70
|
// Skip max count check if maxFileCount is 1 (allow replacement)
|
|
65
71
|
if (!!maxFileCount && maxFileCount !== 1 && files.length + validFiles.length >= maxFileCount) {
|
|
66
|
-
invalidFiles.push(
|
|
67
|
-
...file,
|
|
68
|
-
errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_COUNT,
|
|
69
|
-
});
|
|
72
|
+
invalidFiles.push(toInvalidFile(file, ImageFileInputErrorType.EXCEED_MAX_FILE_COUNT));
|
|
70
73
|
continue;
|
|
71
74
|
}
|
|
72
75
|
validFiles.push(file);
|
|
@@ -85,12 +88,12 @@ export const ImageFileInput = forwardRef(({ size = 'sm', accept, multiple = fals
|
|
|
85
88
|
};
|
|
86
89
|
const renderImagePreview = (files = []) => {
|
|
87
90
|
const showEmptySlot = maxFileCount ? files.length < maxFileCount : files.length === 0;
|
|
88
|
-
return (_jsxs("div", { className: "ncua-image-file-input__previews", children: [files.map((file, index) => (_jsx(ImagePreview, { file: file, onRemove: () => handleRemoveFile(index) }, `${file.name}-${index}`))), showEmptySlot && (_jsxs("div", { className: "ncua-image-file-input__empty-slot-wrapper", onMouseEnter: () => !disabled && setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), onClick: handleBrowseClick, children: [_jsx(Button, { onlyIcon: true, size: size, className: classNames('ncua-image-file-input__preview-container'), onClick: handleBrowseClick, disabled: disabled, label: imagePreviewTooltipLabel }), _jsx(Tooltip, { content: imagePreviewTooltipLabel, position: "bottom", tooltipType: "black", forceVisible: isButtonHovered && !disabled,
|
|
91
|
+
return (_jsxs("div", { className: "ncua-image-file-input__previews", children: [files.map((file, index) => (_jsx(ImagePreview, { file: file, onRemove: () => handleRemoveFile(index) }, `${file.name}-${index}`))), showEmptySlot && (_jsxs("div", { className: "ncua-image-file-input__empty-slot-wrapper", onMouseEnter: () => !disabled && setIsButtonHovered(true), onMouseLeave: () => setIsButtonHovered(false), onClick: handleBrowseClick, children: [_jsx(Button, { onlyIcon: true, size: size, className: classNames('ncua-image-file-input__preview-container'), onClick: handleBrowseClick, disabled: disabled, label: imagePreviewTooltipLabel }), _jsx(Tooltip, { content: imagePreviewTooltipLabel, position: "bottom", tooltipType: "black", forceVisible: isButtonHovered && !disabled, panelClassName: classNames('ncua-image-file-input__slot-tooltip', `ncua-image-file-input__slot-tooltip--${size}`) })] }))] }));
|
|
89
92
|
};
|
|
90
93
|
const renderHintList = () => {
|
|
91
94
|
if (!hintItems || hintItems.length === 0)
|
|
92
95
|
return null;
|
|
93
96
|
return (_jsx("ul", { className: "ncua-file-input__hint-list", children: hintItems.map((hint) => (_jsx("li", { className: "ncua-file-input__hint-item", children: hint }, hint))) }));
|
|
94
97
|
};
|
|
95
|
-
return (_jsxs("div", { className: classNames('ncua-image-file-input', `ncua-image-file-input--${size}`, { destructive: destructive }), children: [renderImagePreview(files),
|
|
98
|
+
return (_jsxs("div", { className: classNames('ncua-image-file-input', `ncua-image-file-input--${size}`, { destructive: destructive }), children: [renderImagePreview(files), _jsx("input", { hidden: true, ref: fileInputRef, type: "file", accept: accept, multiple: multiple, onChange: handleFileChange, tabIndex: -1, "aria-hidden": "true", ...props }), showFileInput && (_jsxs("div", { className: classNames('ncua-file-input', `ncua-file-input--${size}`), children: [_jsxs("div", { className: "ncua-file-input__input-container", children: [_jsxs("div", { className: "ncua-file-input__label", children: [_jsx(Label, { isRequired: isRequired, children: label }), showHelpIcon && _jsx(HelpCircle, { className: "ncua-input__help-icon" })] }), _jsx(Button, { size: "xs", onClick: handleBrowseClick, disabled: disabled, leadingIcon: { type: 'icon', icon: Upload01 }, label: buttonLabel }), showHintText && hintText && _jsx(HintText, { destructive: destructive, children: hintText })] }), showHintText && renderHintList()] }))] }));
|
|
96
99
|
});
|
|
@@ -28,6 +28,10 @@ type SelectBoxProps = Omit<ComponentPropsWithRef<'div'>, 'size' | 'onChange'> &
|
|
|
28
28
|
*/
|
|
29
29
|
maxSelection?: number | null;
|
|
30
30
|
onEdit?: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* 완료 버튼 클릭 시 호출되는 콜백 — 현재 선택된 값을 인자로 받음 (multiple 모드에서만 트리거)
|
|
33
|
+
*/
|
|
34
|
+
onComplete?: OptionChangeHandler;
|
|
31
35
|
align?: 'left' | 'right';
|
|
32
36
|
/**
|
|
33
37
|
* 옵션 패널을 React Portal로 body에 렌더한다.
|
|
@@ -40,7 +40,7 @@ function DisplayValue({ displayValue }) {
|
|
|
40
40
|
}
|
|
41
41
|
return (_jsxs("div", { className: "ncua-selectbox__value-container", children: [displayValue.icon && (_jsx("span", { className: "ncua-selectbox__value-icon", children: _jsx(displayValue.icon, { width: 16, height: 16 }) })), _jsx("span", { className: "ncua-selectbox__value-text", children: displayValue.label })] }));
|
|
42
42
|
}
|
|
43
|
-
const SelectBox = forwardRef(({ placeholder = '선택하세요', disabledPlaceholder = false, hintText, size = 'xs', type = 'default', autoWidth = true, destructive = false, value, optionItems = [], disabled = false, maxHeight = DEFAULT_MAX_HEIGHT, multiple = false, maxSelection, align = 'left', usePortal, id, className, children, register, onChange, onEdit, ...props }, ref
|
|
43
|
+
const SelectBox = forwardRef(({ placeholder = '선택하세요', disabledPlaceholder = false, hintText, size = 'xs', type = 'default', autoWidth = true, destructive = false, value, optionItems = [], disabled = false, maxHeight = DEFAULT_MAX_HEIGHT, multiple = false, maxSelection, align = 'left', usePortal, id, className, children, register, onChange, onEdit, onComplete, ...props }, ref
|
|
44
44
|
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: 옵션/멀티/태그/포탈 등 필수 분기 통합
|
|
45
45
|
) => {
|
|
46
46
|
const internalRef = useRef(null);
|
|
@@ -106,10 +106,11 @@ const SelectBox = forwardRef(({ placeholder = '선택하세요', disabledPlaceho
|
|
|
106
106
|
const handleEdit = () => {
|
|
107
107
|
onEdit?.();
|
|
108
108
|
};
|
|
109
|
-
const handleComplete = () => {
|
|
109
|
+
const handleComplete = (completedValue) => {
|
|
110
110
|
if (multiple) {
|
|
111
111
|
const tags = getSelectedTagsData();
|
|
112
112
|
setSelectedTags(tags);
|
|
113
|
+
onComplete?.(completedValue);
|
|
113
114
|
}
|
|
114
115
|
closeDropdown();
|
|
115
116
|
};
|
|
@@ -16,6 +16,7 @@ interface TooltipProps {
|
|
|
16
16
|
forceVisible?: boolean;
|
|
17
17
|
/** true이면 Portal 없이 앵커 내부에 패널을 인라인 렌더링. CSS 기반 위치 지정이 필요한 경우 사용 */
|
|
18
18
|
disablePortal?: boolean;
|
|
19
|
+
panelClassName?: string;
|
|
19
20
|
}
|
|
20
|
-
export declare const Tooltip: ({ tooltipType, iconType, position, size, title, content, hideArrow, type, iconColor, iconStyle, className, zIndex, forceVisible, disablePortal, }: TooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare const Tooltip: ({ tooltipType, iconType, position, size, title, content, hideArrow, type, iconColor, iconStyle, className, zIndex, forceVisible, disablePortal, panelClassName, }: TooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
22
|
export {};
|
|
@@ -86,7 +86,7 @@ const renderIcon = (iconStyle, iconType, iconSize, iconColor) => {
|
|
|
86
86
|
}
|
|
87
87
|
return iconType === 'stroke' ? (_jsx(AlertCircle, { width: iconSize, height: iconSize, color: iconColor })) : (_jsx(AlertCircleFill, { width: iconSize, height: iconSize, color: iconColor }));
|
|
88
88
|
};
|
|
89
|
-
export const Tooltip = ({ tooltipType = 'white', iconType = 'stroke', position = 'auto', size = 'sm', title, content, hideArrow = false, type = 'short', iconColor = 'var(--gray-300)', iconStyle = 'help-circle', className, zIndex, forceVisible, disablePortal = false, }) => {
|
|
89
|
+
export const Tooltip = ({ tooltipType = 'white', iconType = 'stroke', position = 'auto', size = 'sm', title, content, hideArrow = false, type = 'short', iconColor = 'var(--gray-300)', iconStyle = 'help-circle', className, zIndex, forceVisible, disablePortal = false, panelClassName, }) => {
|
|
90
90
|
const iconSize = size === 'sm' ? ICON_SIZE_SM : ICON_SIZE_DEFAULT;
|
|
91
91
|
const anchorRef = useRef(null);
|
|
92
92
|
const panelRef = useRef(null);
|
|
@@ -154,12 +154,12 @@ export const Tooltip = ({ tooltipType = 'white', iconType = 'stroke', position =
|
|
|
154
154
|
'ncua-tooltip--stroke': iconType === 'stroke',
|
|
155
155
|
'ncua-tooltip--auto': position === 'auto',
|
|
156
156
|
}, className), [size, type, hideArrow, iconType, position, className]);
|
|
157
|
-
const
|
|
157
|
+
const panelClasses = useMemo(() => classNames({ 'ncua-tooltip-panel': !disablePortal }, 'ncua-tooltip__bg', `ncua-tooltip__bg--${tooltipType}`, `ncua-tooltip__bg--${finalPosition}`, {
|
|
158
158
|
'ncua-tooltip__bg--visible': effectiveVisible,
|
|
159
159
|
'ncua-tooltip__bg--force-hidden': isManuallyClose && !forceVisible,
|
|
160
|
-
}), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal]);
|
|
160
|
+
}, panelClassName), [tooltipType, finalPosition, effectiveVisible, isManuallyClose, forceVisible, disablePortal, panelClassName]);
|
|
161
161
|
const panelStyle = buildPanelStyle(disablePortal, coords, effectiveVisible, zIndex);
|
|
162
162
|
const portalTarget = mounted ? resolvePortalTarget() : null;
|
|
163
|
-
const panel = (_jsxs("span", { ref: panelRef, className:
|
|
163
|
+
const panel = (_jsxs("span", { ref: panelRef, className: panelClasses, style: panelStyle, children: [title && _jsx("span", { className: "ncua-tooltip__title", children: title }), content && _jsx("span", { className: "ncua-tooltip__content", children: content }), type === 'long' && (_jsx(ButtonCloseX, { className: "ncua-tooltip__close-button", size: "xs", theme: tooltipType === 'white' ? 'dark' : 'light', onClick: handleCloseClick, "aria-label": "\uD234\uD301 \uB2EB\uAE30" }))] }));
|
|
164
164
|
return (_jsxs("span", { ref: anchorRef, className: tooltipClassName, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [renderIcon(iconStyle, iconType, iconSize, iconColor), disablePortal ? panel : portalTarget && createPortal(panel, portalTarget)] }));
|
|
165
165
|
};
|
|
@@ -25,7 +25,7 @@ type SelectDropdownProps = ComponentPropsWithRef<'div'> & {
|
|
|
25
25
|
showSelectAllAction?: boolean;
|
|
26
26
|
onSelectAll?: () => void;
|
|
27
27
|
onEdit?: () => void;
|
|
28
|
-
onComplete?: () => void;
|
|
28
|
+
onComplete?: (value: OptionValue) => void;
|
|
29
29
|
activeDescendantId?: string;
|
|
30
30
|
componentType?: 'selectbox' | 'combobox';
|
|
31
31
|
align?: 'left' | 'right';
|
|
@@ -20,7 +20,7 @@ const SelectDropdown = forwardRef(({ isOpen, direction = 'down', size = 'xs', op
|
|
|
20
20
|
'ncua-select-dropdown__option--selected': isSelected,
|
|
21
21
|
'ncua-select-dropdown__option--focused': isFocused,
|
|
22
22
|
}), onClick: () => onOptionSelect(option), onMouseEnter: handleMouseEnter, role: "option", "aria-selected": isSelected, children: [option.icon && (_jsx("span", { className: "ncua-select-dropdown__option-icon", children: _jsx(option.icon, { width: 16, height: 16 }) })), _jsx("span", { className: "ncua-select-dropdown__option-text", children: option.label })] }, option.id));
|
|
23
|
-
}), children] }) }), showFooterButtons && (_jsx("div", { className: "ncua-select-dropdown__footer", children: _jsxs("div", { className: "ncua-select-dropdown__footer-buttons", children: [_jsx("div", { className: "ncua-select-dropdown__footer-left", children: multiple && showSelectAllAction && (_jsx(Button, { label: selectAllButtonText, hierarchy: "text", size: "xs", onClick: onSelectAll, underline: true })) }), _jsxs("div", { className: "ncua-select-dropdown__footer-right", children: [_jsx(Button, { label: "\uD3B8\uC9D1", hierarchy: "secondary-gray", size: "xs", onClick: onEdit }), multiple && _jsx(Button, { label: "\uC120\uD0DD \uC644\uB8CC", hierarchy: "secondary", size: "xs", onClick: onComplete })] })] }) }))] }));
|
|
23
|
+
}), children] }) }), showFooterButtons && (_jsx("div", { className: "ncua-select-dropdown__footer", children: _jsxs("div", { className: "ncua-select-dropdown__footer-buttons", children: [_jsx("div", { className: "ncua-select-dropdown__footer-left", children: multiple && showSelectAllAction && (_jsx(Button, { label: selectAllButtonText, hierarchy: "text", size: "xs", onClick: onSelectAll, underline: true })) }), _jsxs("div", { className: "ncua-select-dropdown__footer-right", children: [_jsx(Button, { label: "\uD3B8\uC9D1", hierarchy: "secondary-gray", size: "xs", onClick: onEdit }), multiple && (_jsx(Button, { label: "\uC120\uD0DD \uC644\uB8CC", hierarchy: "secondary", size: "xs", onClick: () => onComplete?.(value ?? []) }))] })] }) }))] }));
|
|
24
24
|
});
|
|
25
25
|
SelectDropdown.displayName = 'SelectDropdown';
|
|
26
26
|
export { SelectDropdown };
|
|
@@ -6,11 +6,13 @@ export declare const H_SCROLLBAR_SIDE_GAP = 8;
|
|
|
6
6
|
export declare const SCROLLBAR_TRACK_OFFSET = 16;
|
|
7
7
|
type VerticalScrollbarOptions = {
|
|
8
8
|
enabled: boolean;
|
|
9
|
+
layoutKey: unknown;
|
|
9
10
|
scrollContainerRef: RefObject<HTMLDivElement | null>;
|
|
10
11
|
scrollAreaRef: RefObject<HTMLDivElement | null>;
|
|
12
|
+
scrollbarRef: RefObject<HTMLDivElement | null>;
|
|
11
13
|
thumbRef: RefObject<HTMLDivElement | null>;
|
|
12
14
|
};
|
|
13
|
-
export declare const useTableVerticalScrollbar: ({ enabled, scrollContainerRef, scrollAreaRef, thumbRef, }: VerticalScrollbarOptions) => {
|
|
15
|
+
export declare const useTableVerticalScrollbar: ({ enabled, layoutKey, scrollContainerRef, scrollAreaRef, scrollbarRef, thumbRef, }: VerticalScrollbarOptions) => {
|
|
14
16
|
handleThumbMouseDown: (e: MouseEvent<HTMLDivElement>) => void;
|
|
15
17
|
};
|
|
16
18
|
type HorizontalScrollbarOptions = {
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { ColorTone } from '../../../../constant/color';
|
|
2
2
|
import type { Size } from '../../../../constant/size';
|
|
3
3
|
import type { SideSlotType } from '../../../types/side-slot';
|
|
4
|
-
type BadgeType = 'pill-outline' | 'pill-dark-color';
|
|
4
|
+
type BadgeType = 'pill-outline' | 'pill-dark-color' | 'new-badge';
|
|
5
5
|
type BadgeColor = Extract<ColorTone, 'neutral' | 'error' | 'warning' | 'success' | 'blue' | 'pink' | 'disabled'>;
|
|
6
6
|
type BadgeSize = Extract<Size, 'xs' | 'sm' | 'md'>;
|
|
7
7
|
type BadgeProps = {
|
|
8
|
-
|
|
8
|
+
/** new-badge 타입에서는 사용되지 않는다(무시됨). 그 외 타입에서는 필수로 전달한다. */
|
|
9
|
+
label?: string;
|
|
9
10
|
type?: BadgeType;
|
|
10
11
|
color?: BadgeColor;
|
|
11
12
|
className?: string;
|
|
@@ -30,6 +30,10 @@ interface ComboBoxProps extends Omit<ComponentPropsWithRef<'div'>, 'size' | 'onC
|
|
|
30
30
|
*/
|
|
31
31
|
maxSelection?: number | null;
|
|
32
32
|
onEdit?: () => void;
|
|
33
|
+
/**
|
|
34
|
+
* 완료 버튼 클릭 시 호출되는 콜백 — 현재 선택된 값을 인자로 받음 (multiple 모드에서만 트리거)
|
|
35
|
+
*/
|
|
36
|
+
onComplete?: OptionChangeHandler;
|
|
33
37
|
}
|
|
34
38
|
declare const ComboBox: import("react").ForwardRefExoticComponent<Omit<ComboBoxProps, "ref"> & import("react").RefAttributes<HTMLDivElement>>;
|
|
35
39
|
export { defaultMaxHeight, ComboBox };
|
|
@@ -28,6 +28,10 @@ type SelectBoxProps = Omit<ComponentPropsWithRef<'div'>, 'size' | 'onChange'> &
|
|
|
28
28
|
*/
|
|
29
29
|
maxSelection?: number | null;
|
|
30
30
|
onEdit?: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* 완료 버튼 클릭 시 호출되는 콜백 — 현재 선택된 값을 인자로 받음 (multiple 모드에서만 트리거)
|
|
33
|
+
*/
|
|
34
|
+
onComplete?: OptionChangeHandler;
|
|
31
35
|
align?: 'left' | 'right';
|
|
32
36
|
/**
|
|
33
37
|
* 옵션 패널을 React Portal로 body에 렌더한다.
|
|
@@ -16,6 +16,7 @@ interface TooltipProps {
|
|
|
16
16
|
forceVisible?: boolean;
|
|
17
17
|
/** true이면 Portal 없이 앵커 내부에 패널을 인라인 렌더링. CSS 기반 위치 지정이 필요한 경우 사용 */
|
|
18
18
|
disablePortal?: boolean;
|
|
19
|
+
panelClassName?: string;
|
|
19
20
|
}
|
|
20
|
-
export declare const Tooltip: ({ tooltipType, iconType, position, size, title, content, hideArrow, type, iconColor, iconStyle, className, zIndex, forceVisible, disablePortal, }: TooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export declare const Tooltip: ({ tooltipType, iconType, position, size, title, content, hideArrow, type, iconColor, iconStyle, className, zIndex, forceVisible, disablePortal, panelClassName, }: TooltipProps) => import("react/jsx-runtime").JSX.Element;
|
|
21
22
|
export {};
|
|
@@ -25,7 +25,7 @@ type SelectDropdownProps = ComponentPropsWithRef<'div'> & {
|
|
|
25
25
|
showSelectAllAction?: boolean;
|
|
26
26
|
onSelectAll?: () => void;
|
|
27
27
|
onEdit?: () => void;
|
|
28
|
-
onComplete?: () => void;
|
|
28
|
+
onComplete?: (value: OptionValue) => void;
|
|
29
29
|
activeDescendantId?: string;
|
|
30
30
|
componentType?: 'selectbox' | 'combobox';
|
|
31
31
|
align?: 'left' | 'right';
|
|
@@ -5017,6 +5017,20 @@ button {
|
|
|
5017
5017
|
color: var(--gray-300);
|
|
5018
5018
|
background-color: var(--gray-100);
|
|
5019
5019
|
}
|
|
5020
|
+
.ncua-badge--new-badge {
|
|
5021
|
+
justify-content: center;
|
|
5022
|
+
color: var(--pink-600);
|
|
5023
|
+
background-color: var(--pink-200);
|
|
5024
|
+
border-radius: 5px;
|
|
5025
|
+
}
|
|
5026
|
+
.ncua-badge--new-badge-sm {
|
|
5027
|
+
width: 16px;
|
|
5028
|
+
height: 16px;
|
|
5029
|
+
}
|
|
5030
|
+
.ncua-badge--new-badge-md {
|
|
5031
|
+
width: 20px;
|
|
5032
|
+
height: 20px;
|
|
5033
|
+
}
|
|
5020
5034
|
|
|
5021
5035
|
.ncua-badge-group {
|
|
5022
5036
|
display: inline-flex;
|
|
@@ -5888,6 +5902,14 @@ button {
|
|
|
5888
5902
|
gap: 16px;
|
|
5889
5903
|
}
|
|
5890
5904
|
|
|
5905
|
+
.ncua-tooltip-panel.ncua-image-file-input__slot-tooltip--xs {
|
|
5906
|
+
transform: translateY(-19px);
|
|
5907
|
+
}
|
|
5908
|
+
|
|
5909
|
+
.ncua-tooltip-panel.ncua-image-file-input__slot-tooltip--sm {
|
|
5910
|
+
transform: translateY(-27px);
|
|
5911
|
+
}
|
|
5912
|
+
|
|
5891
5913
|
.ncua-table-wrapper {
|
|
5892
5914
|
display: flex;
|
|
5893
5915
|
flex-direction: column;
|
|
@@ -5957,6 +5979,8 @@ button {
|
|
|
5957
5979
|
}
|
|
5958
5980
|
|
|
5959
5981
|
.ncua-table {
|
|
5982
|
+
--ncua-table-header-height: 40px;
|
|
5983
|
+
--ncua-table-default-min-width: 1140px;
|
|
5960
5984
|
position: relative;
|
|
5961
5985
|
display: flex;
|
|
5962
5986
|
flex-direction: column;
|
|
@@ -6308,8 +6332,7 @@ button {
|
|
|
6308
6332
|
.ncua-table--vertical .ncua-table .ncua-table__body > .ncua-table__row:last-child > .ncua-table__cell:last-child {
|
|
6309
6333
|
border-bottom-right-radius: 0;
|
|
6310
6334
|
}
|
|
6311
|
-
.ncua-table--vertical .ncua-table__body > .ncua-table__row
|
|
6312
|
-
.ncua-table--vertical .ncua-table__body > .ncua-table__row th {
|
|
6335
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row {
|
|
6313
6336
|
height: 48px;
|
|
6314
6337
|
}
|
|
6315
6338
|
.ncua-table--vertical .ncua-table__body > .ncua-table__row td:first-child,
|
|
@@ -6332,6 +6355,42 @@ button {
|
|
|
6332
6355
|
text-align: left;
|
|
6333
6356
|
font-weight: var(--font-weights-commerce-sans-0);
|
|
6334
6357
|
}
|
|
6358
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row td:first-child > div:has(> .ncua-tooltip),
|
|
6359
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row th:first-child > div:has(> .ncua-tooltip) {
|
|
6360
|
+
display: flex;
|
|
6361
|
+
align-items: center;
|
|
6362
|
+
}
|
|
6363
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row td:first-child > div:has(> .ncua-tooltip) > .ncua-tooltip,
|
|
6364
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row th:first-child > div:has(> .ncua-tooltip) > .ncua-tooltip {
|
|
6365
|
+
display: inline-flex;
|
|
6366
|
+
align-items: center;
|
|
6367
|
+
line-height: 1;
|
|
6368
|
+
margin-left: var(--spacing-xs);
|
|
6369
|
+
}
|
|
6370
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row td:first-child > div:has(> .ncua-tooltip) > .ncua-tooltip > .ncua-tooltip__icon,
|
|
6371
|
+
.ncua-table--vertical .ncua-table__body > .ncua-table__row th:first-child > div:has(> .ncua-tooltip) > .ncua-tooltip > .ncua-tooltip__icon {
|
|
6372
|
+
display: inline-flex;
|
|
6373
|
+
align-items: center;
|
|
6374
|
+
line-height: 1;
|
|
6375
|
+
}
|
|
6376
|
+
.ncua-table--vertical .ncua-table .ncua-table__header > .ncua-table__row th:first-child {
|
|
6377
|
+
width: auto;
|
|
6378
|
+
min-width: 0;
|
|
6379
|
+
max-width: none;
|
|
6380
|
+
padding: 0 var(--spacing-m);
|
|
6381
|
+
font-size: var(--font-size-xs);
|
|
6382
|
+
font-weight: var(--font-weights-commerce-sans-1);
|
|
6383
|
+
line-height: var(--line-heights-xs);
|
|
6384
|
+
color: var(--gray-500);
|
|
6385
|
+
text-align: center;
|
|
6386
|
+
background: var(--gray-100);
|
|
6387
|
+
border-right: 1px solid var(--gray-200);
|
|
6388
|
+
}
|
|
6389
|
+
.ncua-table--vertical .ncua-table .ncua-table__header > .ncua-table__row th:last-child {
|
|
6390
|
+
padding: 0 var(--spacing-m);
|
|
6391
|
+
text-align: center;
|
|
6392
|
+
font-weight: var(--font-weights-commerce-sans-1);
|
|
6393
|
+
}
|
|
6335
6394
|
.ncua-table--vertical .ncua-table .ncua-table__body > .ncua-table__row td:first-child,
|
|
6336
6395
|
.ncua-table--vertical .ncua-table .ncua-table__body > .ncua-table__row th:first-child {
|
|
6337
6396
|
width: auto;
|
|
@@ -6355,7 +6414,7 @@ button {
|
|
|
6355
6414
|
color: var(--primary-red-500, #ec1d31);
|
|
6356
6415
|
font-size: var(--font-size-sm);
|
|
6357
6416
|
font-weight: var(--font-weights-commerce-sans-1);
|
|
6358
|
-
margin-right:
|
|
6417
|
+
margin-right: 4px;
|
|
6359
6418
|
}
|
|
6360
6419
|
|
|
6361
6420
|
.ncua-data-grid {
|
|
@@ -6476,7 +6535,15 @@ button {
|
|
|
6476
6535
|
border: 1px solid var(--gray-100);
|
|
6477
6536
|
border-radius: 12px;
|
|
6478
6537
|
box-shadow: var(--shadow-sm);
|
|
6479
|
-
|
|
6538
|
+
}
|
|
6539
|
+
.ncua-block-container > *:first-child {
|
|
6540
|
+
border-radius: 12px 12px 0 0;
|
|
6541
|
+
}
|
|
6542
|
+
.ncua-block-container > *:last-child {
|
|
6543
|
+
border-radius: 0 0 12px 12px;
|
|
6544
|
+
}
|
|
6545
|
+
.ncua-block-container > *:only-child, .ncua-block-container > *:first-child:has(~ *:last-child[style*="display: none"]), .ncua-block-container > *:first-child:has(~ *:last-child[hidden]) {
|
|
6546
|
+
border-radius: 12px;
|
|
6480
6547
|
}
|
|
6481
6548
|
.ncua-block-container__body {
|
|
6482
6549
|
display: flex;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ncds/ui-admin",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.9",
|
|
4
4
|
"description": "nhn-commerce의 어드민 디자인 시스템입니다.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"barrel": "node barrel.js",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@atlaskit/pragmatic-drag-and-drop-auto-scroll": "2.1.0",
|
|
71
71
|
"@atlaskit/pragmatic-drag-and-drop-hitbox": "1.1.0",
|
|
72
72
|
"@atlaskit/pragmatic-drag-and-drop-react-accessibility": "1.1.4",
|
|
73
|
-
"@ncds/ui-admin-icon": "0.1.
|
|
73
|
+
"@ncds/ui-admin-icon": "0.1.11",
|
|
74
74
|
"classnames": "2.5.1",
|
|
75
75
|
"dompurify": "3.4.1",
|
|
76
76
|
"flatpickr": "4.6.13",
|