@ncds/ui-admin 1.1.3 → 1.2.0
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/index.js +11 -0
- package/dist/cjs/src/components/input/FileInput.js +222 -0
- package/dist/cjs/src/components/input/PasswordInput.js +17 -25
- package/dist/cjs/src/components/input/index.js +11 -0
- package/dist/cjs/src/hooks/index.js +26 -0
- package/dist/cjs/src/hooks/useCallbackRef.js +49 -0
- package/dist/cjs/src/hooks/useMergeRefs.js +36 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/src/components/input/FileInput.js +215 -0
- package/dist/esm/src/components/input/PasswordInput.js +17 -25
- package/dist/esm/src/components/input/index.js +2 -1
- package/dist/esm/src/hooks/index.js +3 -0
- package/dist/esm/src/hooks/useCallbackRef.js +43 -0
- package/dist/esm/src/hooks/useMergeRefs.js +30 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/src/components/input/FileInput.d.ts +62 -0
- package/dist/types/src/components/input/index.d.ts +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -0
- package/dist/types/src/hooks/useCallbackRef.d.ts +28 -0
- package/dist/types/src/hooks/useMergeRefs.d.ts +21 -0
- package/dist/ui-admin/assets/styles/style.css +54 -0
- package/package.json +2 -2
package/dist/cjs/index.js
CHANGED
|
@@ -310,4 +310,15 @@ Object.keys(_components).forEach(function (key) {
|
|
|
310
310
|
return _components[key];
|
|
311
311
|
}
|
|
312
312
|
});
|
|
313
|
+
});
|
|
314
|
+
var _hooks = require("./src/hooks");
|
|
315
|
+
Object.keys(_hooks).forEach(function (key) {
|
|
316
|
+
if (key === "default" || key === "__esModule") return;
|
|
317
|
+
if (key in exports && exports[key] === _hooks[key]) return;
|
|
318
|
+
Object.defineProperty(exports, key, {
|
|
319
|
+
enumerable: true,
|
|
320
|
+
get: function () {
|
|
321
|
+
return _hooks[key];
|
|
322
|
+
}
|
|
323
|
+
});
|
|
313
324
|
});
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FileInputErrorType = exports.FileInput = void 0;
|
|
7
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
|
+
var _uiAdminIcon = _interopRequireDefault(require("@ncds/ui-admin-icon"));
|
|
11
|
+
var _Tag = require("../tag/Tag");
|
|
12
|
+
var _button = require("../button");
|
|
13
|
+
var _shared = require("../shared");
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
var __assign = void 0 && (void 0).__assign || function () {
|
|
16
|
+
__assign = Object.assign || function (t) {
|
|
17
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
18
|
+
s = arguments[i];
|
|
19
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
};
|
|
23
|
+
return __assign.apply(this, arguments);
|
|
24
|
+
};
|
|
25
|
+
var __rest = void 0 && (void 0).__rest || function (s, e) {
|
|
26
|
+
var t = {};
|
|
27
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
28
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
29
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
|
|
30
|
+
}
|
|
31
|
+
return t;
|
|
32
|
+
};
|
|
33
|
+
var __spreadArray = void 0 && (void 0).__spreadArray || function (to, from, pack) {
|
|
34
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
35
|
+
if (ar || !(i in from)) {
|
|
36
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
37
|
+
ar[i] = from[i];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
41
|
+
};
|
|
42
|
+
var FileInputErrorType;
|
|
43
|
+
(function (FileInputErrorType) {
|
|
44
|
+
FileInputErrorType["ALREADY_UPLOADED"] = "ALREADY_UPLOADED";
|
|
45
|
+
FileInputErrorType["EXCEED_MAX_FILE_SIZE"] = "EXCEED_MAX_FILE_SIZE";
|
|
46
|
+
FileInputErrorType["EXCEED_MAX_FILE_COUNT"] = "EXCEED_MAX_FILE_COUNT";
|
|
47
|
+
})(FileInputErrorType || (exports.FileInputErrorType = FileInputErrorType = {}));
|
|
48
|
+
var FileInput = exports.FileInput = /*#__PURE__*/(0, _react.forwardRef)(function (_a, ref) {
|
|
49
|
+
var _b = _a.size,
|
|
50
|
+
size = _b === void 0 ? 'xs' : _b,
|
|
51
|
+
accept = _a.accept,
|
|
52
|
+
_c = _a.multiple,
|
|
53
|
+
multiple = _c === void 0 ? false : _c,
|
|
54
|
+
maxFileSize = _a.maxFileSize,
|
|
55
|
+
maxFileCount = _a.maxFileCount,
|
|
56
|
+
value = _a.value,
|
|
57
|
+
onChange = _a.onChange,
|
|
58
|
+
onFileSelect = _a.onFileSelect,
|
|
59
|
+
onFail = _a.onFail,
|
|
60
|
+
_d = _a.buttonLabel,
|
|
61
|
+
buttonLabel = _d === void 0 ? '파일 찾기' : _d,
|
|
62
|
+
disabled = _a.disabled,
|
|
63
|
+
label = _a.label,
|
|
64
|
+
hintItems = _a.hintItems,
|
|
65
|
+
validation = _a.validation,
|
|
66
|
+
destructive = _a.destructive,
|
|
67
|
+
isRequired = _a.isRequired,
|
|
68
|
+
showHelpIcon = _a.showHelpIcon,
|
|
69
|
+
hintText = _a.hintText,
|
|
70
|
+
props = __rest(_a, ["size", "accept", "multiple", "maxFileSize", "maxFileCount", "value", "onChange", "onFileSelect", "onFail", "buttonLabel", "disabled", "label", "hintItems", "validation", "destructive", "isRequired", "showHelpIcon", "hintText"]);
|
|
71
|
+
var fileInputRef = (0, _react.useRef)(null);
|
|
72
|
+
var _e = (0, _react.useState)([]),
|
|
73
|
+
internalFiles = _e[0],
|
|
74
|
+
setInternalFiles = _e[1];
|
|
75
|
+
// Determine if component is controlled or uncontrolled
|
|
76
|
+
var isControlled = value !== undefined;
|
|
77
|
+
var files = isControlled ? value : internalFiles;
|
|
78
|
+
// Sync internal state with controlled value
|
|
79
|
+
(0, _react.useEffect)(function () {
|
|
80
|
+
if (isControlled && value) {
|
|
81
|
+
setInternalFiles(value);
|
|
82
|
+
}
|
|
83
|
+
}, [isControlled, value]);
|
|
84
|
+
var updateFiles = function (newFiles) {
|
|
85
|
+
if (isControlled) {
|
|
86
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newFiles);
|
|
87
|
+
} else {
|
|
88
|
+
setInternalFiles(newFiles);
|
|
89
|
+
onFileSelect === null || onFileSelect === void 0 ? void 0 : onFileSelect(newFiles);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var handleFileChange = function (event) {
|
|
93
|
+
var selectedFiles = event.target.files;
|
|
94
|
+
if (!selectedFiles || selectedFiles.length === 0) return;
|
|
95
|
+
var _a = validateFiles(Array.from(selectedFiles)),
|
|
96
|
+
validFiles = _a.validFiles,
|
|
97
|
+
invalidFiles = _a.invalidFiles;
|
|
98
|
+
var nextFiles = __spreadArray(__spreadArray([], files, true), validFiles, true);
|
|
99
|
+
updateFiles(nextFiles);
|
|
100
|
+
if (onFail && invalidFiles.length > 0) {
|
|
101
|
+
onFail(invalidFiles);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var validateFiles = function (fileList) {
|
|
105
|
+
var validFiles = [];
|
|
106
|
+
var invalidFiles = [];
|
|
107
|
+
var _loop_1 = function (file) {
|
|
108
|
+
if (files.some(function (f) {
|
|
109
|
+
return f.name === file.name && f.size === file.size;
|
|
110
|
+
})) {
|
|
111
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
112
|
+
errorType: FileInputErrorType.ALREADY_UPLOADED
|
|
113
|
+
}));
|
|
114
|
+
return "continue";
|
|
115
|
+
}
|
|
116
|
+
if (!!maxFileSize && file.size > maxFileSize) {
|
|
117
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
118
|
+
errorType: FileInputErrorType.EXCEED_MAX_FILE_SIZE
|
|
119
|
+
}));
|
|
120
|
+
return "continue";
|
|
121
|
+
}
|
|
122
|
+
if (!!maxFileCount && files.length + validFiles.length >= maxFileCount) {
|
|
123
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
124
|
+
errorType: FileInputErrorType.EXCEED_MAX_FILE_COUNT
|
|
125
|
+
}));
|
|
126
|
+
return "continue";
|
|
127
|
+
}
|
|
128
|
+
validFiles.push(file);
|
|
129
|
+
};
|
|
130
|
+
for (var _i = 0, fileList_1 = fileList; _i < fileList_1.length; _i++) {
|
|
131
|
+
var file = fileList_1[_i];
|
|
132
|
+
_loop_1(file);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
validFiles: validFiles,
|
|
136
|
+
invalidFiles: invalidFiles
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
var handleBrowseClick = function () {
|
|
140
|
+
if (fileInputRef.current && !disabled) {
|
|
141
|
+
fileInputRef.current.click();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var handleRemoveFile = function (index) {
|
|
145
|
+
var newFiles = __spreadArray([], files, true);
|
|
146
|
+
newFiles.splice(index, 1);
|
|
147
|
+
updateFiles(newFiles);
|
|
148
|
+
};
|
|
149
|
+
var renderFileTagList = function () {
|
|
150
|
+
if (files.length === 0) return null;
|
|
151
|
+
return (0, _jsxRuntime.jsx)("div", __assign({
|
|
152
|
+
className: "ncua-file-input__file-tags"
|
|
153
|
+
}, {
|
|
154
|
+
children: files.map(function (file, index) {
|
|
155
|
+
return (0, _jsxRuntime.jsx)(_Tag.Tag, {
|
|
156
|
+
text: file.name,
|
|
157
|
+
size: size === 'xs' ? 'sm' : 'md',
|
|
158
|
+
close: true,
|
|
159
|
+
onButtonClick: function () {
|
|
160
|
+
return handleRemoveFile(index);
|
|
161
|
+
}
|
|
162
|
+
}, "".concat(file.name, "-").concat(index));
|
|
163
|
+
})
|
|
164
|
+
}));
|
|
165
|
+
};
|
|
166
|
+
var renderHintList = function () {
|
|
167
|
+
if (!hintItems || hintItems.length === 0) return null;
|
|
168
|
+
return (0, _jsxRuntime.jsx)("ul", __assign({
|
|
169
|
+
className: "ncua-file-input__hint-list"
|
|
170
|
+
}, {
|
|
171
|
+
children: hintItems.map(function (hint, index) {
|
|
172
|
+
return (0, _jsxRuntime.jsx)("li", __assign({
|
|
173
|
+
className: "ncua-file-input__hint-item"
|
|
174
|
+
}, {
|
|
175
|
+
children: hint
|
|
176
|
+
}), index);
|
|
177
|
+
})
|
|
178
|
+
}));
|
|
179
|
+
};
|
|
180
|
+
return (0, _jsxRuntime.jsxs)("div", __assign({
|
|
181
|
+
className: (0, _classnames.default)('ncua-file-input', "ncua-file-input--".concat(size))
|
|
182
|
+
}, {
|
|
183
|
+
children: [(0, _jsxRuntime.jsx)("input", __assign({
|
|
184
|
+
hidden: true,
|
|
185
|
+
ref: fileInputRef,
|
|
186
|
+
type: "file",
|
|
187
|
+
accept: accept,
|
|
188
|
+
multiple: multiple,
|
|
189
|
+
onChange: handleFileChange,
|
|
190
|
+
tabIndex: -1,
|
|
191
|
+
"aria-hidden": "true"
|
|
192
|
+
}, props)), (0, _jsxRuntime.jsxs)("div", __assign({
|
|
193
|
+
className: "ncua-file-input__input-container"
|
|
194
|
+
}, {
|
|
195
|
+
children: [(0, _jsxRuntime.jsxs)("div", __assign({
|
|
196
|
+
className: "ncua-file-input__label"
|
|
197
|
+
}, {
|
|
198
|
+
children: [(0, _jsxRuntime.jsx)(_shared.Label, __assign({
|
|
199
|
+
isRequired: isRequired
|
|
200
|
+
}, {
|
|
201
|
+
children: label
|
|
202
|
+
})), showHelpIcon && (0, _jsxRuntime.jsx)(_uiAdminIcon.default, {
|
|
203
|
+
className: "ncua-input__help-icon",
|
|
204
|
+
name: "help-circle"
|
|
205
|
+
})]
|
|
206
|
+
})), (0, _jsxRuntime.jsx)(_button.Button, {
|
|
207
|
+
size: size,
|
|
208
|
+
onClick: handleBrowseClick,
|
|
209
|
+
disabled: disabled,
|
|
210
|
+
leadingIcon: {
|
|
211
|
+
type: 'icon',
|
|
212
|
+
icon: 'upload-cloud-02'
|
|
213
|
+
},
|
|
214
|
+
label: buttonLabel
|
|
215
|
+
}), hintText && (0, _jsxRuntime.jsx)(_shared.HintText, __assign({
|
|
216
|
+
destructive: destructive
|
|
217
|
+
}, {
|
|
218
|
+
children: hintText
|
|
219
|
+
}))]
|
|
220
|
+
})), renderHintList(), renderFileTagList()]
|
|
221
|
+
}));
|
|
222
|
+
});
|
|
@@ -5,10 +5,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.PasswordInput = void 0;
|
|
7
7
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
-
var _uiAdminIcon = require("@ncds/ui-admin-icon");
|
|
9
|
-
var _classnames = _interopRequireDefault(require("classnames"));
|
|
10
8
|
var _react = require("react");
|
|
11
9
|
var _InputBase = require("./InputBase");
|
|
10
|
+
var _uiAdminIcon = require("@ncds/ui-admin-icon");
|
|
11
|
+
var _classnames = _interopRequireDefault(require("classnames"));
|
|
12
|
+
var _hooks = require("../../hooks");
|
|
12
13
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
14
|
var __assign = void 0 && (void 0).__assign || function () {
|
|
14
15
|
__assign = Object.assign || function (t) {
|
|
@@ -36,13 +37,25 @@ var PasswordInput = exports.PasswordInput = /*#__PURE__*/(0, _react.forwardRef)(
|
|
|
36
37
|
var _b = _a.size,
|
|
37
38
|
size = _b === void 0 ? 'xs' : _b,
|
|
38
39
|
props = __rest(_a, ["size"]);
|
|
39
|
-
var inputRef = (0, _react.useRef)(null);
|
|
40
40
|
var _c = (0, _react.useState)(false),
|
|
41
41
|
isVisible = _c[0],
|
|
42
42
|
setIsVisible = _c[1];
|
|
43
43
|
var _d = (0, _react.useState)(false),
|
|
44
44
|
hasContent = _d[0],
|
|
45
45
|
setHasContent = _d[1];
|
|
46
|
+
var callbackRef = (0, _hooks.useCallbackRef)(function (node) {
|
|
47
|
+
if (node) {
|
|
48
|
+
setHasContent(!!node.value);
|
|
49
|
+
var handleInput_1 = function () {
|
|
50
|
+
setHasContent(!!node.value);
|
|
51
|
+
};
|
|
52
|
+
node.addEventListener('input', handleInput_1);
|
|
53
|
+
return function () {
|
|
54
|
+
node.removeEventListener('input', handleInput_1);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
var mergedRef = (0, _hooks.useMergeRefs)([ref, callbackRef]);
|
|
46
59
|
var svgProps = {
|
|
47
60
|
width: svgSize[size],
|
|
48
61
|
height: svgSize[size]
|
|
@@ -50,29 +63,8 @@ var PasswordInput = exports.PasswordInput = /*#__PURE__*/(0, _react.forwardRef)(
|
|
|
50
63
|
var handleVisibilityChange = function () {
|
|
51
64
|
setIsVisible(!isVisible);
|
|
52
65
|
};
|
|
53
|
-
(0, _react.useEffect)(function () {
|
|
54
|
-
if (inputRef.current) {
|
|
55
|
-
setHasContent(!!inputRef.current.value);
|
|
56
|
-
var handleInput_1 = function () {
|
|
57
|
-
var _a;
|
|
58
|
-
setHasContent(!!((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value));
|
|
59
|
-
};
|
|
60
|
-
inputRef.current.addEventListener('input', handleInput_1);
|
|
61
|
-
return function () {
|
|
62
|
-
var _a;
|
|
63
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('input', handleInput_1);
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
}, []);
|
|
67
66
|
return (0, _jsxRuntime.jsx)(_InputBase.InputBase, __assign({
|
|
68
|
-
ref:
|
|
69
|
-
if (typeof ref === 'function') {
|
|
70
|
-
ref(node);
|
|
71
|
-
} else if (ref) {
|
|
72
|
-
ref.current = node;
|
|
73
|
-
}
|
|
74
|
-
inputRef.current = node;
|
|
75
|
-
},
|
|
67
|
+
ref: mergedRef,
|
|
76
68
|
size: size,
|
|
77
69
|
type: isVisible ? 'text' : 'password',
|
|
78
70
|
leadingElement: {
|
|
@@ -35,4 +35,15 @@ Object.keys(_Textarea).forEach(function (key) {
|
|
|
35
35
|
return _Textarea[key];
|
|
36
36
|
}
|
|
37
37
|
});
|
|
38
|
+
});
|
|
39
|
+
var _FileInput = require("./FileInput");
|
|
40
|
+
Object.keys(_FileInput).forEach(function (key) {
|
|
41
|
+
if (key === "default" || key === "__esModule") return;
|
|
42
|
+
if (key in exports && exports[key] === _FileInput[key]) return;
|
|
43
|
+
Object.defineProperty(exports, key, {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function () {
|
|
46
|
+
return _FileInput[key];
|
|
47
|
+
}
|
|
48
|
+
});
|
|
38
49
|
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "useCallbackRef", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _useCallbackRef.useCallbackRef;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "useMediaQuery", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _useMediaQuery.useMediaQuery;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "useMergeRefs", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _useMergeRefs.useMergeRefs;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
var _useMediaQuery = require("./useMediaQuery");
|
|
25
|
+
var _useMergeRefs = require("./useMergeRefs");
|
|
26
|
+
var _useCallbackRef = require("./useCallbackRef");
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useCallbackRef = useCallbackRef;
|
|
7
|
+
var _react = require("react");
|
|
8
|
+
/**
|
|
9
|
+
* A callback ref hook that supports cleanup functions.
|
|
10
|
+
* This is useful when you need to perform setup/cleanup operations when the ref changes.
|
|
11
|
+
*
|
|
12
|
+
* @param callback - Function that receives the node and optionally returns a cleanup function
|
|
13
|
+
* @returns A callback ref
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```tsx
|
|
17
|
+
* const MyComponent = () => {
|
|
18
|
+
* const ref = useCallbackRef<HTMLInputElement>((node) => {
|
|
19
|
+
* if (node) {
|
|
20
|
+
* const handleInput = () => console.log('input changed');
|
|
21
|
+
* node.addEventListener('input', handleInput);
|
|
22
|
+
*
|
|
23
|
+
* // Return cleanup function
|
|
24
|
+
* return () => {
|
|
25
|
+
* node.removeEventListener('input', handleInput);
|
|
26
|
+
* };
|
|
27
|
+
* }
|
|
28
|
+
* });
|
|
29
|
+
*
|
|
30
|
+
* return <input ref={ref} />;
|
|
31
|
+
* };
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
function useCallbackRef(callback) {
|
|
35
|
+
var cleanupRef = (0, _react.useRef)();
|
|
36
|
+
return (0, _react.useCallback)(function (node) {
|
|
37
|
+
// Cleanup previous ref
|
|
38
|
+
if (cleanupRef.current) {
|
|
39
|
+
cleanupRef.current();
|
|
40
|
+
cleanupRef.current = undefined;
|
|
41
|
+
}
|
|
42
|
+
// Set up new ref
|
|
43
|
+
if (node) {
|
|
44
|
+
cleanupRef.current = callback(node);
|
|
45
|
+
} else {
|
|
46
|
+
callback(null);
|
|
47
|
+
}
|
|
48
|
+
}, [callback]);
|
|
49
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.useMergeRefs = useMergeRefs;
|
|
7
|
+
/**
|
|
8
|
+
* Merges multiple refs into a single callback ref.
|
|
9
|
+
* This is useful when you need to forward a ref while also keeping an internal ref.
|
|
10
|
+
*
|
|
11
|
+
* @param refs - Array of refs to merge
|
|
12
|
+
* @returns A callback ref that will update all provided refs
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```tsx
|
|
16
|
+
* const MyComponent = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
|
17
|
+
* const internalRef = useRef<HTMLInputElement>(null);
|
|
18
|
+
* const mergedRef = useMergeRefs([ref, internalRef]);
|
|
19
|
+
*
|
|
20
|
+
* return <input ref={mergedRef} />;
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function useMergeRefs(refs) {
|
|
25
|
+
return function (node) {
|
|
26
|
+
refs.forEach(function (ref) {
|
|
27
|
+
if (ref) {
|
|
28
|
+
if (typeof ref === 'function') {
|
|
29
|
+
ref(node);
|
|
30
|
+
} else {
|
|
31
|
+
ref.current = node;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -25,4 +25,5 @@ export * from './src/components/tab';
|
|
|
25
25
|
export * from './src/components/tag';
|
|
26
26
|
export * from './src/components/toggle';
|
|
27
27
|
export * from './src/components/tooltip';
|
|
28
|
-
export * from './src/components';
|
|
28
|
+
export * from './src/components';
|
|
29
|
+
export * from './src/hooks';
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __assign = this && this.__assign || function () {
|
|
2
|
+
__assign = Object.assign || function (t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
6
|
+
}
|
|
7
|
+
return t;
|
|
8
|
+
};
|
|
9
|
+
return __assign.apply(this, arguments);
|
|
10
|
+
};
|
|
11
|
+
var __rest = this && this.__rest || function (s, e) {
|
|
12
|
+
var t = {};
|
|
13
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
14
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
15
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
|
|
16
|
+
}
|
|
17
|
+
return t;
|
|
18
|
+
};
|
|
19
|
+
var __spreadArray = this && this.__spreadArray || function (to, from, pack) {
|
|
20
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
21
|
+
if (ar || !(i in from)) {
|
|
22
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
23
|
+
ar[i] = from[i];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
27
|
+
};
|
|
28
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
29
|
+
import { forwardRef, useState, useRef, useEffect } from 'react';
|
|
30
|
+
import classNames from 'classnames';
|
|
31
|
+
import Icon from '@ncds/ui-admin-icon';
|
|
32
|
+
import { Tag } from '../tag/Tag';
|
|
33
|
+
import { Button } from '../button';
|
|
34
|
+
import { HintText, Label } from '../shared';
|
|
35
|
+
export var FileInputErrorType;
|
|
36
|
+
(function (FileInputErrorType) {
|
|
37
|
+
FileInputErrorType["ALREADY_UPLOADED"] = "ALREADY_UPLOADED";
|
|
38
|
+
FileInputErrorType["EXCEED_MAX_FILE_SIZE"] = "EXCEED_MAX_FILE_SIZE";
|
|
39
|
+
FileInputErrorType["EXCEED_MAX_FILE_COUNT"] = "EXCEED_MAX_FILE_COUNT";
|
|
40
|
+
})(FileInputErrorType || (FileInputErrorType = {}));
|
|
41
|
+
export var FileInput = /*#__PURE__*/forwardRef(function (_a, ref) {
|
|
42
|
+
var _b = _a.size,
|
|
43
|
+
size = _b === void 0 ? 'xs' : _b,
|
|
44
|
+
accept = _a.accept,
|
|
45
|
+
_c = _a.multiple,
|
|
46
|
+
multiple = _c === void 0 ? false : _c,
|
|
47
|
+
maxFileSize = _a.maxFileSize,
|
|
48
|
+
maxFileCount = _a.maxFileCount,
|
|
49
|
+
value = _a.value,
|
|
50
|
+
onChange = _a.onChange,
|
|
51
|
+
onFileSelect = _a.onFileSelect,
|
|
52
|
+
onFail = _a.onFail,
|
|
53
|
+
_d = _a.buttonLabel,
|
|
54
|
+
buttonLabel = _d === void 0 ? '파일 찾기' : _d,
|
|
55
|
+
disabled = _a.disabled,
|
|
56
|
+
label = _a.label,
|
|
57
|
+
hintItems = _a.hintItems,
|
|
58
|
+
validation = _a.validation,
|
|
59
|
+
destructive = _a.destructive,
|
|
60
|
+
isRequired = _a.isRequired,
|
|
61
|
+
showHelpIcon = _a.showHelpIcon,
|
|
62
|
+
hintText = _a.hintText,
|
|
63
|
+
props = __rest(_a, ["size", "accept", "multiple", "maxFileSize", "maxFileCount", "value", "onChange", "onFileSelect", "onFail", "buttonLabel", "disabled", "label", "hintItems", "validation", "destructive", "isRequired", "showHelpIcon", "hintText"]);
|
|
64
|
+
var fileInputRef = useRef(null);
|
|
65
|
+
var _e = useState([]),
|
|
66
|
+
internalFiles = _e[0],
|
|
67
|
+
setInternalFiles = _e[1];
|
|
68
|
+
// Determine if component is controlled or uncontrolled
|
|
69
|
+
var isControlled = value !== undefined;
|
|
70
|
+
var files = isControlled ? value : internalFiles;
|
|
71
|
+
// Sync internal state with controlled value
|
|
72
|
+
useEffect(function () {
|
|
73
|
+
if (isControlled && value) {
|
|
74
|
+
setInternalFiles(value);
|
|
75
|
+
}
|
|
76
|
+
}, [isControlled, value]);
|
|
77
|
+
var updateFiles = function (newFiles) {
|
|
78
|
+
if (isControlled) {
|
|
79
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(newFiles);
|
|
80
|
+
} else {
|
|
81
|
+
setInternalFiles(newFiles);
|
|
82
|
+
onFileSelect === null || onFileSelect === void 0 ? void 0 : onFileSelect(newFiles);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
var handleFileChange = function (event) {
|
|
86
|
+
var selectedFiles = event.target.files;
|
|
87
|
+
if (!selectedFiles || selectedFiles.length === 0) return;
|
|
88
|
+
var _a = validateFiles(Array.from(selectedFiles)),
|
|
89
|
+
validFiles = _a.validFiles,
|
|
90
|
+
invalidFiles = _a.invalidFiles;
|
|
91
|
+
var nextFiles = __spreadArray(__spreadArray([], files, true), validFiles, true);
|
|
92
|
+
updateFiles(nextFiles);
|
|
93
|
+
if (onFail && invalidFiles.length > 0) {
|
|
94
|
+
onFail(invalidFiles);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
var validateFiles = function (fileList) {
|
|
98
|
+
var validFiles = [];
|
|
99
|
+
var invalidFiles = [];
|
|
100
|
+
var _loop_1 = function (file) {
|
|
101
|
+
if (files.some(function (f) {
|
|
102
|
+
return f.name === file.name && f.size === file.size;
|
|
103
|
+
})) {
|
|
104
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
105
|
+
errorType: FileInputErrorType.ALREADY_UPLOADED
|
|
106
|
+
}));
|
|
107
|
+
return "continue";
|
|
108
|
+
}
|
|
109
|
+
if (!!maxFileSize && file.size > maxFileSize) {
|
|
110
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
111
|
+
errorType: FileInputErrorType.EXCEED_MAX_FILE_SIZE
|
|
112
|
+
}));
|
|
113
|
+
return "continue";
|
|
114
|
+
}
|
|
115
|
+
if (!!maxFileCount && files.length + validFiles.length >= maxFileCount) {
|
|
116
|
+
invalidFiles.push(__assign(__assign({}, file), {
|
|
117
|
+
errorType: FileInputErrorType.EXCEED_MAX_FILE_COUNT
|
|
118
|
+
}));
|
|
119
|
+
return "continue";
|
|
120
|
+
}
|
|
121
|
+
validFiles.push(file);
|
|
122
|
+
};
|
|
123
|
+
for (var _i = 0, fileList_1 = fileList; _i < fileList_1.length; _i++) {
|
|
124
|
+
var file = fileList_1[_i];
|
|
125
|
+
_loop_1(file);
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
validFiles: validFiles,
|
|
129
|
+
invalidFiles: invalidFiles
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
var handleBrowseClick = function () {
|
|
133
|
+
if (fileInputRef.current && !disabled) {
|
|
134
|
+
fileInputRef.current.click();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
var handleRemoveFile = function (index) {
|
|
138
|
+
var newFiles = __spreadArray([], files, true);
|
|
139
|
+
newFiles.splice(index, 1);
|
|
140
|
+
updateFiles(newFiles);
|
|
141
|
+
};
|
|
142
|
+
var renderFileTagList = function () {
|
|
143
|
+
if (files.length === 0) return null;
|
|
144
|
+
return _jsx("div", __assign({
|
|
145
|
+
className: "ncua-file-input__file-tags"
|
|
146
|
+
}, {
|
|
147
|
+
children: files.map(function (file, index) {
|
|
148
|
+
return _jsx(Tag, {
|
|
149
|
+
text: file.name,
|
|
150
|
+
size: size === 'xs' ? 'sm' : 'md',
|
|
151
|
+
close: true,
|
|
152
|
+
onButtonClick: function () {
|
|
153
|
+
return handleRemoveFile(index);
|
|
154
|
+
}
|
|
155
|
+
}, "".concat(file.name, "-").concat(index));
|
|
156
|
+
})
|
|
157
|
+
}));
|
|
158
|
+
};
|
|
159
|
+
var renderHintList = function () {
|
|
160
|
+
if (!hintItems || hintItems.length === 0) return null;
|
|
161
|
+
return _jsx("ul", __assign({
|
|
162
|
+
className: "ncua-file-input__hint-list"
|
|
163
|
+
}, {
|
|
164
|
+
children: hintItems.map(function (hint, index) {
|
|
165
|
+
return _jsx("li", __assign({
|
|
166
|
+
className: "ncua-file-input__hint-item"
|
|
167
|
+
}, {
|
|
168
|
+
children: hint
|
|
169
|
+
}), index);
|
|
170
|
+
})
|
|
171
|
+
}));
|
|
172
|
+
};
|
|
173
|
+
return _jsxs("div", __assign({
|
|
174
|
+
className: classNames('ncua-file-input', "ncua-file-input--".concat(size))
|
|
175
|
+
}, {
|
|
176
|
+
children: [_jsx("input", __assign({
|
|
177
|
+
hidden: true,
|
|
178
|
+
ref: fileInputRef,
|
|
179
|
+
type: "file",
|
|
180
|
+
accept: accept,
|
|
181
|
+
multiple: multiple,
|
|
182
|
+
onChange: handleFileChange,
|
|
183
|
+
tabIndex: -1,
|
|
184
|
+
"aria-hidden": "true"
|
|
185
|
+
}, props)), _jsxs("div", __assign({
|
|
186
|
+
className: "ncua-file-input__input-container"
|
|
187
|
+
}, {
|
|
188
|
+
children: [_jsxs("div", __assign({
|
|
189
|
+
className: "ncua-file-input__label"
|
|
190
|
+
}, {
|
|
191
|
+
children: [_jsx(Label, __assign({
|
|
192
|
+
isRequired: isRequired
|
|
193
|
+
}, {
|
|
194
|
+
children: label
|
|
195
|
+
})), showHelpIcon && _jsx(Icon, {
|
|
196
|
+
className: "ncua-input__help-icon",
|
|
197
|
+
name: "help-circle"
|
|
198
|
+
})]
|
|
199
|
+
})), _jsx(Button, {
|
|
200
|
+
size: size,
|
|
201
|
+
onClick: handleBrowseClick,
|
|
202
|
+
disabled: disabled,
|
|
203
|
+
leadingIcon: {
|
|
204
|
+
type: 'icon',
|
|
205
|
+
icon: 'upload-cloud-02'
|
|
206
|
+
},
|
|
207
|
+
label: buttonLabel
|
|
208
|
+
}), hintText && _jsx(HintText, __assign({
|
|
209
|
+
destructive: destructive
|
|
210
|
+
}, {
|
|
211
|
+
children: hintText
|
|
212
|
+
}))]
|
|
213
|
+
})), renderHintList(), renderFileTagList()]
|
|
214
|
+
}));
|
|
215
|
+
});
|
|
@@ -17,10 +17,11 @@ var __rest = this && this.__rest || function (s, e) {
|
|
|
17
17
|
return t;
|
|
18
18
|
};
|
|
19
19
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
20
|
+
import { forwardRef, useState } from 'react';
|
|
21
|
+
import { InputBase } from './InputBase';
|
|
20
22
|
import { Eye, EyeOff } from '@ncds/ui-admin-icon';
|
|
21
23
|
import classNames from 'classnames';
|
|
22
|
-
import {
|
|
23
|
-
import { InputBase } from './InputBase';
|
|
24
|
+
import { useMergeRefs, useCallbackRef } from '../../hooks';
|
|
24
25
|
var svgSize = {
|
|
25
26
|
xs: 14,
|
|
26
27
|
sm: 20
|
|
@@ -29,13 +30,25 @@ export var PasswordInput = /*#__PURE__*/forwardRef(function (_a, ref) {
|
|
|
29
30
|
var _b = _a.size,
|
|
30
31
|
size = _b === void 0 ? 'xs' : _b,
|
|
31
32
|
props = __rest(_a, ["size"]);
|
|
32
|
-
var inputRef = useRef(null);
|
|
33
33
|
var _c = useState(false),
|
|
34
34
|
isVisible = _c[0],
|
|
35
35
|
setIsVisible = _c[1];
|
|
36
36
|
var _d = useState(false),
|
|
37
37
|
hasContent = _d[0],
|
|
38
38
|
setHasContent = _d[1];
|
|
39
|
+
var callbackRef = useCallbackRef(function (node) {
|
|
40
|
+
if (node) {
|
|
41
|
+
setHasContent(!!node.value);
|
|
42
|
+
var handleInput_1 = function () {
|
|
43
|
+
setHasContent(!!node.value);
|
|
44
|
+
};
|
|
45
|
+
node.addEventListener('input', handleInput_1);
|
|
46
|
+
return function () {
|
|
47
|
+
node.removeEventListener('input', handleInput_1);
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
var mergedRef = useMergeRefs([ref, callbackRef]);
|
|
39
52
|
var svgProps = {
|
|
40
53
|
width: svgSize[size],
|
|
41
54
|
height: svgSize[size]
|
|
@@ -43,29 +56,8 @@ export var PasswordInput = /*#__PURE__*/forwardRef(function (_a, ref) {
|
|
|
43
56
|
var handleVisibilityChange = function () {
|
|
44
57
|
setIsVisible(!isVisible);
|
|
45
58
|
};
|
|
46
|
-
useEffect(function () {
|
|
47
|
-
if (inputRef.current) {
|
|
48
|
-
setHasContent(!!inputRef.current.value);
|
|
49
|
-
var handleInput_1 = function () {
|
|
50
|
-
var _a;
|
|
51
|
-
setHasContent(!!((_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.value));
|
|
52
|
-
};
|
|
53
|
-
inputRef.current.addEventListener('input', handleInput_1);
|
|
54
|
-
return function () {
|
|
55
|
-
var _a;
|
|
56
|
-
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.removeEventListener('input', handleInput_1);
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}, []);
|
|
60
59
|
return _jsx(InputBase, __assign({
|
|
61
|
-
ref:
|
|
62
|
-
if (typeof ref === 'function') {
|
|
63
|
-
ref(node);
|
|
64
|
-
} else if (ref) {
|
|
65
|
-
ref.current = node;
|
|
66
|
-
}
|
|
67
|
-
inputRef.current = node;
|
|
68
|
-
},
|
|
60
|
+
ref: mergedRef,
|
|
69
61
|
size: size,
|
|
70
62
|
type: isVisible ? 'text' : 'password',
|
|
71
63
|
leadingElement: {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A callback ref hook that supports cleanup functions.
|
|
4
|
+
* This is useful when you need to perform setup/cleanup operations when the ref changes.
|
|
5
|
+
*
|
|
6
|
+
* @param callback - Function that receives the node and optionally returns a cleanup function
|
|
7
|
+
* @returns A callback ref
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const MyComponent = () => {
|
|
12
|
+
* const ref = useCallbackRef<HTMLInputElement>((node) => {
|
|
13
|
+
* if (node) {
|
|
14
|
+
* const handleInput = () => console.log('input changed');
|
|
15
|
+
* node.addEventListener('input', handleInput);
|
|
16
|
+
*
|
|
17
|
+
* // Return cleanup function
|
|
18
|
+
* return () => {
|
|
19
|
+
* node.removeEventListener('input', handleInput);
|
|
20
|
+
* };
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* return <input ref={ref} />;
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function useCallbackRef(callback) {
|
|
29
|
+
var cleanupRef = useRef();
|
|
30
|
+
return useCallback(function (node) {
|
|
31
|
+
// Cleanup previous ref
|
|
32
|
+
if (cleanupRef.current) {
|
|
33
|
+
cleanupRef.current();
|
|
34
|
+
cleanupRef.current = undefined;
|
|
35
|
+
}
|
|
36
|
+
// Set up new ref
|
|
37
|
+
if (node) {
|
|
38
|
+
cleanupRef.current = callback(node);
|
|
39
|
+
} else {
|
|
40
|
+
callback(null);
|
|
41
|
+
}
|
|
42
|
+
}, [callback]);
|
|
43
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges multiple refs into a single callback ref.
|
|
3
|
+
* This is useful when you need to forward a ref while also keeping an internal ref.
|
|
4
|
+
*
|
|
5
|
+
* @param refs - Array of refs to merge
|
|
6
|
+
* @returns A callback ref that will update all provided refs
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const MyComponent = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
|
11
|
+
* const internalRef = useRef<HTMLInputElement>(null);
|
|
12
|
+
* const mergedRef = useMergeRefs([ref, internalRef]);
|
|
13
|
+
*
|
|
14
|
+
* return <input ref={mergedRef} />;
|
|
15
|
+
* });
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export function useMergeRefs(refs) {
|
|
19
|
+
return function (node) {
|
|
20
|
+
refs.forEach(function (ref) {
|
|
21
|
+
if (ref) {
|
|
22
|
+
if (typeof ref === 'function') {
|
|
23
|
+
ref(node);
|
|
24
|
+
} else {
|
|
25
|
+
ref.current = node;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { InputBaseProps } from './InputBase';
|
|
2
|
+
export declare enum FileInputErrorType {
|
|
3
|
+
ALREADY_UPLOADED = "ALREADY_UPLOADED",
|
|
4
|
+
EXCEED_MAX_FILE_SIZE = "EXCEED_MAX_FILE_SIZE",
|
|
5
|
+
EXCEED_MAX_FILE_COUNT = "EXCEED_MAX_FILE_COUNT"
|
|
6
|
+
}
|
|
7
|
+
export type InvalidFile = File & {
|
|
8
|
+
errorType: FileInputErrorType;
|
|
9
|
+
};
|
|
10
|
+
export interface FileInputProps extends Omit<InputBaseProps, 'clearText' | 'onClearText' | 'hintText' | 'value' | 'onChange'> {
|
|
11
|
+
/**
|
|
12
|
+
* Accepted file types
|
|
13
|
+
* e.g. '.jpg,.png,.pdf' or 'image/*'
|
|
14
|
+
*/
|
|
15
|
+
accept?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Maximum number of files
|
|
18
|
+
*/
|
|
19
|
+
maxFileCount?: number;
|
|
20
|
+
/**
|
|
21
|
+
* Maximum file size in bytes
|
|
22
|
+
*/
|
|
23
|
+
maxFileSize?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Current files (controlled mode)
|
|
26
|
+
*/
|
|
27
|
+
value?: File[];
|
|
28
|
+
/**
|
|
29
|
+
* Callback when files change (controlled mode)
|
|
30
|
+
*/
|
|
31
|
+
onChange?: (files: File[]) => void;
|
|
32
|
+
/**
|
|
33
|
+
* Callback when files are selected (uncontrolled mode)
|
|
34
|
+
*/
|
|
35
|
+
onFileSelect?: (files: File[]) => void;
|
|
36
|
+
/**
|
|
37
|
+
* Callback when file selection fails
|
|
38
|
+
*/
|
|
39
|
+
onFail?: (files: InvalidFile[]) => void;
|
|
40
|
+
/**
|
|
41
|
+
* Label shown on the button
|
|
42
|
+
*/
|
|
43
|
+
buttonLabel?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Hint text items to display as a list
|
|
46
|
+
*/
|
|
47
|
+
hintItems?: string[];
|
|
48
|
+
/**
|
|
49
|
+
* Whether the input is required
|
|
50
|
+
*/
|
|
51
|
+
isRequired?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Whether to show the help icon
|
|
54
|
+
*/
|
|
55
|
+
showHelpIcon?: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Hint text to display
|
|
58
|
+
*/
|
|
59
|
+
hintText?: string;
|
|
60
|
+
}
|
|
61
|
+
export declare const FileInput: import("react").ForwardRefExoticComponent<FileInputProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
62
|
+
//# sourceMappingURL=FileInput.d.ts.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A callback ref hook that supports cleanup functions.
|
|
3
|
+
* This is useful when you need to perform setup/cleanup operations when the ref changes.
|
|
4
|
+
*
|
|
5
|
+
* @param callback - Function that receives the node and optionally returns a cleanup function
|
|
6
|
+
* @returns A callback ref
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const MyComponent = () => {
|
|
11
|
+
* const ref = useCallbackRef<HTMLInputElement>((node) => {
|
|
12
|
+
* if (node) {
|
|
13
|
+
* const handleInput = () => console.log('input changed');
|
|
14
|
+
* node.addEventListener('input', handleInput);
|
|
15
|
+
*
|
|
16
|
+
* // Return cleanup function
|
|
17
|
+
* return () => {
|
|
18
|
+
* node.removeEventListener('input', handleInput);
|
|
19
|
+
* };
|
|
20
|
+
* }
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* return <input ref={ref} />;
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function useCallbackRef<T>(callback: (node: T | null) => (() => void) | void): React.RefCallback<T>;
|
|
28
|
+
//# sourceMappingURL=useCallbackRef.d.ts.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
type Ref<T> = React.RefCallback<T> | React.MutableRefObject<T | null> | null | undefined;
|
|
2
|
+
/**
|
|
3
|
+
* Merges multiple refs into a single callback ref.
|
|
4
|
+
* This is useful when you need to forward a ref while also keeping an internal ref.
|
|
5
|
+
*
|
|
6
|
+
* @param refs - Array of refs to merge
|
|
7
|
+
* @returns A callback ref that will update all provided refs
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* const MyComponent = forwardRef<HTMLInputElement, Props>((props, ref) => {
|
|
12
|
+
* const internalRef = useRef<HTMLInputElement>(null);
|
|
13
|
+
* const mergedRef = useMergeRefs([ref, internalRef]);
|
|
14
|
+
*
|
|
15
|
+
* return <input ref={mergedRef} />;
|
|
16
|
+
* });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare function useMergeRefs<T>(refs: Array<Ref<T>>): React.RefCallback<T>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=useMergeRefs.d.ts.map
|
|
@@ -1188,6 +1188,60 @@ button {
|
|
|
1188
1188
|
color: var(--primary-red-600);
|
|
1189
1189
|
}
|
|
1190
1190
|
|
|
1191
|
+
.ncua-file-input {
|
|
1192
|
+
display: inline-flex;
|
|
1193
|
+
flex-direction: column;
|
|
1194
|
+
align-items: flex-start;
|
|
1195
|
+
gap: 6px;
|
|
1196
|
+
}
|
|
1197
|
+
.ncua-file-input__input-container {
|
|
1198
|
+
display: flex;
|
|
1199
|
+
flex-direction: column;
|
|
1200
|
+
align-items: flex-start;
|
|
1201
|
+
gap: 4px;
|
|
1202
|
+
}
|
|
1203
|
+
.ncua-file-input__label {
|
|
1204
|
+
display: flex;
|
|
1205
|
+
align-items: center;
|
|
1206
|
+
gap: 4px;
|
|
1207
|
+
}
|
|
1208
|
+
.ncua-file-input__file-tags {
|
|
1209
|
+
display: flex;
|
|
1210
|
+
padding: 12px 8px;
|
|
1211
|
+
flex-direction: column;
|
|
1212
|
+
align-items: flex-start;
|
|
1213
|
+
gap: 6px;
|
|
1214
|
+
align-self: stretch;
|
|
1215
|
+
background: var(--gray-50);
|
|
1216
|
+
}
|
|
1217
|
+
.ncua-file-input__hint-list {
|
|
1218
|
+
margin: 0;
|
|
1219
|
+
list-style: disc;
|
|
1220
|
+
color: var(--gray-400);
|
|
1221
|
+
}
|
|
1222
|
+
.ncua-file-input--xs {
|
|
1223
|
+
font-size: var(--font-size-xxs);
|
|
1224
|
+
line-height: var(--line-heights-xxs);
|
|
1225
|
+
}
|
|
1226
|
+
.ncua-file-input--sm {
|
|
1227
|
+
font-size: var(--font-size-xs);
|
|
1228
|
+
line-height: var(--line-heights-xs);
|
|
1229
|
+
}
|
|
1230
|
+
.ncua-file-input--xs .ncua-file-input__input-container {
|
|
1231
|
+
gap: 4px;
|
|
1232
|
+
}
|
|
1233
|
+
.ncua-file-input--sm .ncua-file-input__input-container {
|
|
1234
|
+
gap: 6px;
|
|
1235
|
+
}
|
|
1236
|
+
.ncua-file-input--xs .ncua-file-input__hint-list {
|
|
1237
|
+
font-size: var(--font-size-xxxs);
|
|
1238
|
+
line-height: var(--line-heights-xxxs);
|
|
1239
|
+
}
|
|
1240
|
+
.ncua-file-input--md .ncua-file-input__hint-list {
|
|
1241
|
+
font-size: inherit;
|
|
1242
|
+
line-height: inherit;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1191
1245
|
.ncua-input {
|
|
1192
1246
|
display: inline-flex;
|
|
1193
1247
|
flex-direction: column;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ncds/ui-admin",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "nhn-commerce의 어드민 디자인 시스템입니다.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"barrel": "node barrel.js",
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@ncds/types-common": "^1.0.0",
|
|
65
65
|
"@ncds/types-layout": "^1.0.0",
|
|
66
|
-
"@ncds/ui-admin-icon": "0.0.
|
|
66
|
+
"@ncds/ui-admin-icon": "0.0.17",
|
|
67
67
|
"classnames": "^2.3.2",
|
|
68
68
|
"lodash": "^4.17.21",
|
|
69
69
|
"lodash-es": "^4.17.21",
|