@ncds/ui-admin 1.4.1 → 1.5.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.
Files changed (74) hide show
  1. package/dist/cjs/assets/scripts/comboBox.js +18 -0
  2. package/dist/cjs/assets/scripts/datePicker.js +60 -7
  3. package/dist/cjs/assets/scripts/imageFileInput/ImageFileInputModel.js +6 -19
  4. package/dist/cjs/assets/scripts/notification/MessageNotification.js +146 -0
  5. package/dist/cjs/assets/scripts/notification/Notification.js +6 -3
  6. package/dist/cjs/assets/scripts/notification/const/classNames.js +14 -0
  7. package/dist/cjs/assets/scripts/notification/const/index.js +14 -1
  8. package/dist/cjs/assets/scripts/notification/const/sizes.js +7 -1
  9. package/dist/cjs/assets/scripts/notification/const/types.js +10 -1
  10. package/dist/cjs/assets/scripts/notification/index.js +8 -0
  11. package/dist/cjs/assets/scripts/notification/utils.js +3 -3
  12. package/dist/cjs/assets/scripts/utils/selectbox/DropdownModel.js +7 -0
  13. package/dist/cjs/assets/scripts/utils/selectbox/UnifiedSelectBox.js +77 -43
  14. package/dist/cjs/src/components/button/ButtonStepper.js +22 -0
  15. package/dist/cjs/src/components/button/index.js +22 -0
  16. package/dist/cjs/src/components/date-picker/DatePicker.js +45 -6
  17. package/dist/cjs/src/components/date-picker/RangeDatePicker.js +3 -1
  18. package/dist/cjs/src/components/date-picker/RangeDatePickerWithButtons.js +7 -3
  19. package/dist/cjs/src/components/input/InputBase.js +1 -1
  20. package/dist/cjs/src/components/input/NumberInput.js +130 -0
  21. package/dist/cjs/src/components/input/index.js +11 -0
  22. package/dist/cjs/src/components/notification/MessageNotification.js +137 -0
  23. package/dist/cjs/src/components/notification/Notification.js +23 -9
  24. package/dist/cjs/src/components/notification/index.js +11 -0
  25. package/dist/cjs/src/components/tooltip/Tooltip.js +32 -21
  26. package/dist/esm/assets/scripts/comboBox.js +18 -0
  27. package/dist/esm/assets/scripts/datePicker.js +60 -7
  28. package/dist/esm/assets/scripts/imageFileInput/ImageFileInputModel.js +6 -19
  29. package/dist/esm/assets/scripts/notification/MessageNotification.js +141 -0
  30. package/dist/esm/assets/scripts/notification/Notification.js +6 -3
  31. package/dist/esm/assets/scripts/notification/const/classNames.js +14 -0
  32. package/dist/esm/assets/scripts/notification/const/index.js +2 -1
  33. package/dist/esm/assets/scripts/notification/const/sizes.js +6 -0
  34. package/dist/esm/assets/scripts/notification/const/types.js +8 -1
  35. package/dist/esm/assets/scripts/notification/index.js +1 -0
  36. package/dist/esm/assets/scripts/notification/utils.js +3 -3
  37. package/dist/esm/assets/scripts/utils/selectbox/DropdownModel.js +7 -0
  38. package/dist/esm/assets/scripts/utils/selectbox/UnifiedSelectBox.js +77 -43
  39. package/dist/esm/src/components/button/ButtonStepper.js +14 -0
  40. package/dist/esm/src/components/button/index.js +3 -1
  41. package/dist/esm/src/components/date-picker/DatePicker.js +46 -7
  42. package/dist/esm/src/components/date-picker/RangeDatePicker.js +3 -1
  43. package/dist/esm/src/components/date-picker/RangeDatePickerWithButtons.js +7 -3
  44. package/dist/esm/src/components/input/InputBase.js +1 -1
  45. package/dist/esm/src/components/input/NumberInput.js +124 -0
  46. package/dist/esm/src/components/input/index.js +1 -0
  47. package/dist/esm/src/components/notification/MessageNotification.js +130 -0
  48. package/dist/esm/src/components/notification/Notification.js +23 -9
  49. package/dist/esm/src/components/notification/index.js +2 -1
  50. package/dist/esm/src/components/tooltip/Tooltip.js +33 -22
  51. package/dist/types/assets/scripts/comboBox.d.ts +12 -0
  52. package/dist/types/assets/scripts/datePicker.d.ts +1 -0
  53. package/dist/types/assets/scripts/notification/MessageNotification.d.ts +23 -0
  54. package/dist/types/assets/scripts/notification/Notification.d.ts +1 -1
  55. package/dist/types/assets/scripts/notification/const/classNames.d.ts +14 -0
  56. package/dist/types/assets/scripts/notification/const/index.d.ts +2 -1
  57. package/dist/types/assets/scripts/notification/const/sizes.d.ts +5 -0
  58. package/dist/types/assets/scripts/notification/const/types.d.ts +1 -0
  59. package/dist/types/assets/scripts/notification/index.d.ts +1 -0
  60. package/dist/types/assets/scripts/utils/selectbox/DropdownModel.d.ts +4 -0
  61. package/dist/types/assets/scripts/utils/selectbox/UnifiedSelectBox.d.ts +20 -1
  62. package/dist/types/src/components/button/ButtonStepper.d.ts +10 -0
  63. package/dist/types/src/components/button/index.d.ts +2 -0
  64. package/dist/types/src/components/date-picker/DatePicker.d.ts +1 -0
  65. package/dist/types/src/components/date-picker/RangeDatePickerWithButtons.d.ts +4 -4
  66. package/dist/types/src/components/input/NumberInput.d.ts +10 -0
  67. package/dist/types/src/components/input/index.d.ts +1 -0
  68. package/dist/types/src/components/notification/MessageNotification.d.ts +40 -0
  69. package/dist/types/src/components/notification/Notification.d.ts +6 -1
  70. package/dist/types/src/components/notification/index.d.ts +1 -0
  71. package/dist/types/src/components/selectbox/SelectBox.d.ts +1 -1
  72. package/dist/types/src/components/tooltip/Tooltip.d.ts +4 -2
  73. package/dist/ui-admin/assets/styles/style.css +292 -10
  74. package/package.json +1 -1
@@ -245,6 +245,19 @@ export var DatePicker = /** @class */function () {
245
245
  if (formattedInput === input) return;
246
246
  target.value = formattedInput;
247
247
  };
248
+ // 외부 클릭 시 시간 입력 강제 blur 처리
249
+ var handleMouseDown = function (e) {
250
+ var flatpickrCalendar = wrapper.querySelector('.flatpickr-calendar.open');
251
+ if (flatpickrCalendar && !flatpickrCalendar.contains(e.target)) {
252
+ var timeInputs = flatpickrCalendar.querySelectorAll('.flatpickr-time input');
253
+ timeInputs.forEach(function (input) {
254
+ if (document.activeElement === input) {
255
+ input.blur();
256
+ }
257
+ });
258
+ }
259
+ };
260
+ document.addEventListener('mousedown', handleMouseDown, true);
248
261
  return flatpickr(input, __assign(__assign({}, options), {
249
262
  allowInput: (_b = options.allowInput) !== null && _b !== void 0 ? _b : true,
250
263
  appendTo: wrapper,
@@ -271,6 +284,7 @@ export var DatePicker = /** @class */function () {
271
284
  input.removeEventListener('input', onInputHandler);
272
285
  input.removeEventListener('input', onHourInputHandler);
273
286
  input.removeEventListener('input', onMinuteInputHandler);
287
+ document.removeEventListener('mousedown', handleMouseDown, true);
274
288
  }
275
289
  }));
276
290
  };
@@ -329,19 +343,58 @@ export var DatePicker = /** @class */function () {
329
343
  var startDate = dates[0],
330
344
  endDate = dates[1];
331
345
  var datesToSet = this.datePickerOptions.map(function (option, index) {
332
- var _a;
346
+ var _a, _b;
333
347
  var date = index === 0 ? startDate : endDate;
334
- if (!date) return '';
348
+ if (!date || date === '0000-00-00') return '';
335
349
  var hasTime = (_a = option.options) === null || _a === void 0 ? void 0 : _a.enableTime;
336
- if (!hasTime) return date;
337
- if (date.includes(':')) return date;
338
- var momentDate = moment(date);
339
- return index === 0 ? momentDate.startOf('day').format(_this.dateFormat) : momentDate.endOf('day').format(_this.dateFormat);
350
+ var flatpickrFormat = ((_b = option.options) === null || _b === void 0 ? void 0 : _b.dateFormat) || (hasTime ? 'Y-m-d H:i' : 'Y-m-d');
351
+ // dateFormat이 설정되어 있지 않을 때만 hasTime에 따라 기본값 설정
352
+ // dateFormat이 설정되어 있으면 그 형식을 그대로 사용 (enableTime과 무관하게)
353
+ var momentFormat = _this.convertFlatpickrFormatToMoment(flatpickrFormat);
354
+ var momentDate;
355
+ if (hasTime && !date.includes(':')) {
356
+ // 시간이 없는 날짜 형식으로 파싱
357
+ momentDate = moment(date);
358
+ if (!momentDate.isValid()) return '';
359
+ return index === 0 ? momentDate.startOf('day').format(momentFormat) : momentDate.endOf('day').format(momentFormat);
360
+ }
361
+ if (hasTime && date.includes(':')) {
362
+ momentDate = moment(date);
363
+ if (!momentDate.isValid()) return '';
364
+ return momentDate.format(momentFormat);
365
+ }
366
+ // 시간이 없는 경우 (hasTime이 false이거나 date에 시간이 없는 경우)
367
+ momentDate = moment(date);
368
+ if (!momentDate.isValid()) {
369
+ momentDate = moment(date, momentFormat, true);
370
+ }
371
+ // 둘 다 실패하면 다른 일반적인 형식들로 시도
372
+ if (!momentDate.isValid()) {
373
+ momentDate = moment(date, [momentFormat, 'YYYY-MM-DD', 'YYYY/MM/DD', 'MM/DD/YYYY'], true);
374
+ }
375
+ if (!momentDate.isValid()) return '';
376
+ // dateFormat 형식으로 포맷팅하여 반환 (flatpickr가 표시하는 형식과 일치)
377
+ return momentDate.format(momentFormat);
340
378
  });
341
- var period = moment(endDate).diff(moment(startDate), 'days');
379
+ var period = datesToSet[0] && datesToSet[1] ? moment(datesToSet[1]).diff(moment(datesToSet[0]), 'days') : null;
342
380
  this.updateButtonsByPeriod(period);
343
381
  this.setMultipleDates(datesToSet);
344
382
  };
383
+ // flatpickr 형식을 moment 형식으로 변환하는 헬퍼 메서드
384
+ DatePicker.prototype.convertFlatpickrFormatToMoment = function (flatpickrFormat) {
385
+ var map = {
386
+ Y: 'YYYY',
387
+ y: 'YY',
388
+ m: 'MM',
389
+ d: 'DD',
390
+ H: 'HH',
391
+ i: 'mm',
392
+ S: 'ss'
393
+ };
394
+ return flatpickrFormat.replace(/[YymdHiS]/g, function (matched) {
395
+ return map[matched] || matched;
396
+ });
397
+ };
345
398
  DatePicker.prototype.getDates = function () {
346
399
  var _this = this;
347
400
  return this.flatpickrInstances.map(function (instance) {
@@ -174,36 +174,23 @@ var ImageFileInputModel = /** @class */function () {
174
174
  var validFiles = [];
175
175
  var invalidFiles = [];
176
176
  var currentFiles = this.getFiles();
177
- var _loop_1 = function (file) {
178
- // 중복 체크
179
- if (currentFiles.some(function (f) {
180
- return f.name === file.name && f.size === file.size;
181
- })) {
182
- invalidFiles.push(__assign(__assign({}, file), {
183
- errorType: ImageFileInputErrorType.ALREADY_UPLOADED
184
- }));
185
- return "continue";
186
- }
177
+ for (var _i = 0, fileList_1 = fileList; _i < fileList_1.length; _i++) {
178
+ var file = fileList_1[_i];
187
179
  // 파일 크기 체크
188
- if (this_1.options.maxFileSize && file.size > this_1.options.maxFileSize) {
180
+ if (this.options.maxFileSize && file.size > this.options.maxFileSize) {
189
181
  invalidFiles.push(__assign(__assign({}, file), {
190
182
  errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_SIZE
191
183
  }));
192
- return "continue";
184
+ continue;
193
185
  }
194
186
  // 파일 개수 체크 (maxFileCount가 1이면 교체 가능하므로 체크하지 않음)
195
- if (this_1.options.maxFileCount && this_1.options.maxFileCount !== 1 && currentFiles.length + validFiles.length >= this_1.options.maxFileCount) {
187
+ if (this.options.maxFileCount && this.options.maxFileCount !== 1 && currentFiles.length + validFiles.length >= this.options.maxFileCount) {
196
188
  invalidFiles.push(__assign(__assign({}, file), {
197
189
  errorType: ImageFileInputErrorType.EXCEED_MAX_FILE_COUNT
198
190
  }));
199
- return "continue";
191
+ continue;
200
192
  }
201
193
  validFiles.push(file);
202
- };
203
- var this_1 = this;
204
- for (var _i = 0, fileList_1 = fileList; _i < fileList_1.length; _i++) {
205
- var file = fileList_1[_i];
206
- _loop_1(file);
207
194
  }
208
195
  return {
209
196
  validFiles: validFiles,
@@ -0,0 +1,141 @@
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
+ import { SVG_ICONS, CLASS_NAMES, FLOATING_ICON_MAP, MESSAGE_SIZES } from './const';
12
+ import { FeaturedIcon } from '../featuredIcon';
13
+ import { createWrapperElement, renderSupportingText, renderActions, bindNotificationEvents, setupAutoClose } from './utils';
14
+ var MessageNotification = /** @class */function () {
15
+ function MessageNotification(options) {
16
+ this.options = __assign({
17
+ color: 'neutral',
18
+ className: '',
19
+ actions: [],
20
+ autoClose: 0,
21
+ supportingText: undefined
22
+ }, options);
23
+ this.element = this.createElement();
24
+ this.bindEvents();
25
+ this.setupAutoClose();
26
+ }
27
+ MessageNotification.prototype.createElement = function () {
28
+ var _a = this.options,
29
+ title = _a.title,
30
+ supportingText = _a.supportingText,
31
+ color = _a.color,
32
+ className = _a.className,
33
+ actions = _a.actions,
34
+ onClose = _a.onClose,
35
+ onHidePermanently = _a.onHidePermanently;
36
+ // message 타입은 neutral, error, warning, success 4가지 색상만 지원
37
+ var actualColor = color;
38
+ if (color === 'info') {
39
+ console.warn('Message notification does not support "info" color. Using "neutral" instead.');
40
+ actualColor = 'neutral';
41
+ }
42
+ var wrapper = createWrapperElement(CLASS_NAMES.MESSAGE.BASE, actualColor, className);
43
+ var iconFunction = FLOATING_ICON_MAP[actualColor];
44
+ // FeaturedIcon 생성
45
+ var featuredIconElement = null;
46
+ if (iconFunction) {
47
+ var iconSvg = iconFunction(MESSAGE_SIZES.ICON_PIXEL);
48
+ this.featuredIcon = FeaturedIcon.create({
49
+ svgString: iconSvg,
50
+ theme: 'light-circle',
51
+ color: actualColor,
52
+ size: MESSAGE_SIZES.FEATURED_ICON
53
+ });
54
+ featuredIconElement = this.featuredIcon.getElement();
55
+ }
56
+ wrapper.innerHTML = this.buildTemplate({
57
+ title: title,
58
+ supportingText: supportingText,
59
+ actions: actions,
60
+ onClose: onClose,
61
+ onHidePermanently: onHidePermanently
62
+ });
63
+ // FeaturedIcon을 content-wrapper에 추가
64
+ if (featuredIconElement) {
65
+ var contentWrapper = wrapper.querySelector(".".concat(CLASS_NAMES.MESSAGE.CONTENT_WRAPPER));
66
+ if (contentWrapper) {
67
+ contentWrapper.insertBefore(featuredIconElement, contentWrapper.firstChild);
68
+ }
69
+ }
70
+ return wrapper;
71
+ };
72
+ MessageNotification.prototype.buildTemplate = function (params) {
73
+ var title = params.title,
74
+ supportingText = params.supportingText,
75
+ actions = params.actions,
76
+ onClose = params.onClose,
77
+ onHidePermanently = params.onHidePermanently;
78
+ return "\n <div class=\"".concat(CLASS_NAMES.MESSAGE.CONTAINER, "\">\n <div class=\"").concat(CLASS_NAMES.MESSAGE.CONTENT, "\">\n <div class=\"").concat(CLASS_NAMES.MESSAGE.CONTENT_WRAPPER, "\">\n <div class=\"").concat(CLASS_NAMES.MESSAGE.TEXT_CONTAINER, "\">\n <span class=\"").concat(CLASS_NAMES.MESSAGE.TITLE, "\">").concat(title, "</span>\n ").concat(renderSupportingText(supportingText, CLASS_NAMES.MESSAGE.SUPPORTING_TEXT), "\n </div>\n </div>\n <div class=\"").concat(CLASS_NAMES.MESSAGE.ACTIONS_CONTAINER, "\">\n ").concat(actions && actions.length > 0 ? renderActions(actions, CLASS_NAMES.MESSAGE.ACTIONS) : '', "\n </div>\n <div class=\"").concat(CLASS_NAMES.MESSAGE.ACTIONS_CONTAINER, "\">\n ").concat(this.renderHidePermanentlyButton(onHidePermanently), "\n ").concat(this.renderCloseButton(onClose), "\n </div>\n </div>\n </div>\n ");
79
+ };
80
+ MessageNotification.prototype.renderHidePermanentlyButton = function (onHidePermanently) {
81
+ if (!onHidePermanently) return '';
82
+ return "\n <button type=\"button\" class=\"".concat(CLASS_NAMES.COMMON.ACTION_BUTTON, " ").concat(CLASS_NAMES.COMMON.ACTION_BUTTON, "--text ").concat(CLASS_NAMES.MESSAGE.HIDE_LINK, "\" data-hide-permanently=\"true\">\n \uB2E4\uC2DC \uBCF4\uC9C0 \uC54A\uAE30\n </button>\n ");
83
+ };
84
+ MessageNotification.prototype.renderCloseButton = function (onClose) {
85
+ if (!onClose) return '';
86
+ return "\n <button type=\"button\" class=\"".concat(CLASS_NAMES.MESSAGE.CLOSE_BUTTON, "\" aria-label=\"\uC54C\uB9BC \uB2EB\uAE30\">\n ").concat(SVG_ICONS['x-close'](MESSAGE_SIZES.CLOSE_BUTTON).replace('stroke="currentColor"', "stroke=\"#2F2F30\""), "\n </button>\n ");
87
+ };
88
+ MessageNotification.prototype.bindEvents = function () {
89
+ var _this = this;
90
+ bindNotificationEvents(this.element, this.options.actions, this.options.onClose, function () {
91
+ return _this.remove();
92
+ });
93
+ // 다시보지 않기 버튼 이벤트 바인딩
94
+ if (this.options.onHidePermanently) {
95
+ var hidePermanentlyButton = this.element.querySelector('[data-hide-permanently="true"]');
96
+ if (hidePermanentlyButton) {
97
+ hidePermanentlyButton.addEventListener('click', function () {
98
+ var _a, _b;
99
+ (_b = (_a = _this.options).onHidePermanently) === null || _b === void 0 ? void 0 : _b.call(_a);
100
+ _this.remove();
101
+ });
102
+ }
103
+ }
104
+ };
105
+ MessageNotification.prototype.setupAutoClose = function () {
106
+ var _this = this;
107
+ this.autoCloseTimer = setupAutoClose(this.options.autoClose, this.options.onClose, function () {
108
+ return _this.remove();
109
+ });
110
+ };
111
+ // Public methods
112
+ MessageNotification.prototype.getElement = function () {
113
+ return this.element;
114
+ };
115
+ MessageNotification.prototype.appendTo = function (parent) {
116
+ parent.appendChild(this.element);
117
+ };
118
+ MessageNotification.prototype.remove = function () {
119
+ if (this.autoCloseTimer) {
120
+ clearTimeout(this.autoCloseTimer);
121
+ this.autoCloseTimer = undefined;
122
+ }
123
+ if (this.element && this.element.parentNode) {
124
+ this.element.parentNode.removeChild(this.element);
125
+ }
126
+ };
127
+ MessageNotification.prototype.destroy = function () {
128
+ // FeaturedIcon 정리
129
+ if (this.featuredIcon) {
130
+ this.featuredIcon.destroy();
131
+ this.featuredIcon = undefined;
132
+ }
133
+ this.remove();
134
+ };
135
+ // Static factory methods
136
+ MessageNotification.create = function (options) {
137
+ return new MessageNotification(options);
138
+ };
139
+ return MessageNotification;
140
+ }();
141
+ export { MessageNotification };
@@ -18,16 +18,19 @@ var __rest = this && this.__rest || function (s, e) {
18
18
  };
19
19
  import { FullWidthNotification } from './FullWidthNotification';
20
20
  import { FloatingNotification } from './FloatingNotification';
21
+ import { MessageNotification } from './MessageNotification';
21
22
  // 통합 Notification 클래스
22
23
  var Notification = /** @class */function () {
23
24
  function Notification(options) {
24
25
  var _a = options.type,
25
26
  type = _a === void 0 ? 'floating' : _a,
26
27
  baseOptions = __rest(options, ["type"]);
27
- if (type === 'floating') {
28
- this.instance = new FloatingNotification(baseOptions);
29
- } else {
28
+ if (type === 'message') {
29
+ this.instance = new MessageNotification(baseOptions);
30
+ } else if (type === 'full-width') {
30
31
  this.instance = new FullWidthNotification(baseOptions);
32
+ } else {
33
+ this.instance = new FloatingNotification(baseOptions);
31
34
  }
32
35
  }
33
36
  // 모든 메서드를 instance에 위임
@@ -24,6 +24,20 @@ export var CLASS_NAMES = {
24
24
  ACTIONS: 'ncua-floating-notification__actions',
25
25
  CLOSE_BUTTON: 'ncua-floating-notification__close-button'
26
26
  },
27
+ MESSAGE: {
28
+ BASE: 'ncua-message-notification',
29
+ CONTAINER: 'ncua-message-notification__container',
30
+ CONTENT: 'ncua-message-notification__content',
31
+ CONTENT_WRAPPER: 'ncua-message-notification__content-wrapper',
32
+ ICON: 'ncua-message-notification__icon',
33
+ TEXT_CONTAINER: 'ncua-message-notification__text-container',
34
+ TITLE: 'ncua-message-notification__title',
35
+ SUPPORTING_TEXT: 'ncua-message-notification__supporting-text',
36
+ ACTIONS_CONTAINER: 'ncua-message-notification__actions-container',
37
+ ACTIONS: 'ncua-message-notification__actions',
38
+ HIDE_LINK: 'ncua-message-notification__hide-link',
39
+ CLOSE_BUTTON: 'ncua-message-notification__close-button'
40
+ },
27
41
  COMMON: {
28
42
  ACTION_BUTTON: 'ncua-notification__action-button'
29
43
  }
@@ -1,3 +1,4 @@
1
1
  export { SVG_ICONS, FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP, ICON_MAP } from './icons';
2
2
  export { CLASS_NAMES } from './classNames';
3
- export { FEATURED_ICON_SIZES, ICON_PIXEL_SIZES, CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FULL_WIDTH_SIZES, getSizes } from './sizes';
3
+ export { FEATURED_ICON_SIZES, ICON_PIXEL_SIZES, CLOSE_BUTTON_SIZES, CLOSE_BUTTON_SVG_SIZES, FULL_WIDTH_SIZES, MESSAGE_SIZES, getSizes } from './sizes';
4
+ export { MESSAGE_CLOSE_ICON_COLORS } from './types';
@@ -25,6 +25,12 @@ export var FULL_WIDTH_SIZES = {
25
25
  ICON: '16',
26
26
  CLOSE_BUTTON: '20'
27
27
  };
28
+ // Message 알림 고정 사이즈
29
+ export var MESSAGE_SIZES = {
30
+ FEATURED_ICON: 'lg',
31
+ ICON_PIXEL: '24',
32
+ CLOSE_BUTTON: '20'
33
+ };
28
34
  // 사이즈 유틸리티 함수들
29
35
  export var getSizes = {
30
36
  featuredIcon: function (isMobile) {
@@ -1 +1,8 @@
1
- export {};
1
+ // Message Notification 닫기 버튼 아이콘 색상 맵
2
+ export var MESSAGE_CLOSE_ICON_COLORS = {
3
+ neutral: '#6B7280',
4
+ error: '#EF4444',
5
+ warning: '#F97316',
6
+ success: '#16A34A',
7
+ info: '#6B7280' // info는 message 타입에서 지원하지 않지만 fallback용
8
+ };
@@ -1,6 +1,7 @@
1
1
  export { Notification } from './Notification';
2
2
  export { FullWidthNotification } from './FullWidthNotification';
3
3
  export { FloatingNotification } from './FloatingNotification';
4
+ export { MessageNotification } from './MessageNotification';
4
5
  export * from './utils';
5
6
  export { SVG_ICONS, CLASS_NAMES, ICON_MAP, FLOATING_ICON_MAP, FULL_WIDTH_ICON_MAP } from './const';
6
7
  // 전역 등록 ( 추후 index.ts와 개별동작 할 수 있게끔 대비 )
@@ -27,7 +27,7 @@ export function renderActions(actions, wrapperClass) {
27
27
  return '';
28
28
  }
29
29
  var buttonsHtml = actions.map(function (action) {
30
- var buttonHtml = "\n <button \n class=\"ncua-notification__action-button ncua-notification__action-button--".concat(action.hierarchy || 'link', "\"\n data-action=\"").concat(action.label, "-").concat(action.hierarchy, "\"\n >\n ").concat(action.label, "\n </button>");
30
+ var buttonHtml = "\n <button \n class=\"ncua-btn ncua-btn--sm ncua-btn--".concat(action.hierarchy || 'text', "\"\n data-action=\"").concat(action.label, "-").concat(action.hierarchy, "\"\n >\n ").concat(action.label, "\n </button>");
31
31
  return buttonHtml;
32
32
  }).join('');
33
33
  return "<div class=\"".concat(wrapperClass, "\">").concat(buttonsHtml, "</div>");
@@ -37,13 +37,13 @@ export function bindNotificationEvents(element, actions, onClose, onRemove) {
37
37
  element.addEventListener('click', function (event) {
38
38
  var target = event.target;
39
39
  // 닫기 버튼 클릭 처리
40
- if (target.matches(".".concat(CLASS_NAMES.FULL_WIDTH.CLOSE_BUTTON, ", .").concat(CLASS_NAMES.FLOATING.CLOSE_BUTTON)) || target.closest(".".concat(CLASS_NAMES.FULL_WIDTH.CLOSE_BUTTON, ", .").concat(CLASS_NAMES.FLOATING.CLOSE_BUTTON))) {
40
+ if (target.matches(".".concat(CLASS_NAMES.FULL_WIDTH.CLOSE_BUTTON, ", .").concat(CLASS_NAMES.FLOATING.CLOSE_BUTTON)) || target.closest(".".concat(CLASS_NAMES.FULL_WIDTH.CLOSE_BUTTON, ", .").concat(CLASS_NAMES.FLOATING.CLOSE_BUTTON)) || target.closest(".".concat(CLASS_NAMES.MESSAGE.CLOSE_BUTTON))) {
41
41
  onClose === null || onClose === void 0 ? void 0 : onClose();
42
42
  onRemove === null || onRemove === void 0 ? void 0 : onRemove();
43
43
  return;
44
44
  }
45
45
  // 액션 버튼 클릭 처리
46
- var actionButton = target.closest('.ncua-notification__action-button[data-action]');
46
+ var actionButton = target.closest('.ncua-btn[data-action]');
47
47
  if (actionButton && actions) {
48
48
  var actionData = actionButton.getAttribute('data-action');
49
49
  if (actionData) {
@@ -284,6 +284,13 @@ var DropdownModel = /** @class */function () {
284
284
  this.updateFilteredOptions();
285
285
  this.notifyListeners(['searchValue', 'filteredOptions', 'showAllItems']);
286
286
  };
287
+ /**
288
+ * 검색어만 업데이트 (필터링 없이, API 모드용)
289
+ */
290
+ DropdownModel.prototype.updateSearchValue = function (searchValue) {
291
+ this.state.searchValue = searchValue;
292
+ this.notifyListeners(['searchValue']);
293
+ };
287
294
  /**
288
295
  * 드롭다운이 열릴 수 있는지 확인 (ComboBox용)
289
296
  */
@@ -336,30 +336,27 @@ var UnifiedSelectBox = /** @class */function () {
336
336
  };
337
337
  this.controller = new SelectBoxController(controllerConfig);
338
338
  };
339
- // 이벤트 핸들러들
340
- UnifiedSelectBox.prototype.handleInput = function (value) {
341
- var _this = this;
342
- var _a, _b;
343
- // 기존 디바운스 타이머 제거
339
+ /**
340
+ * 디바운싱 타이머 취소
341
+ */
342
+ UnifiedSelectBox.prototype.cancelDebounceTimer = function () {
344
343
  if (this.searchDebounceTimer) {
345
344
  clearTimeout(this.searchDebounceTimer);
346
345
  this.searchDebounceTimer = undefined;
346
+ this.currentDebounceTimerId = undefined;
347
347
  }
348
+ };
349
+ // 이벤트 핸들러들
350
+ UnifiedSelectBox.prototype.handleInput = function (value) {
351
+ var _this = this;
352
+ var _a, _b;
353
+ this.cancelDebounceTimer();
348
354
  // 필터링은 즉시 업데이트 (사용자가 입력하는 동안 필터링 결과를 즉시 보여줌)
349
355
  // 하지만 onSearch 콜백은 디바운싱 적용
350
356
  var isApiMode = this.config.type === 'combobox' && !this.hasInitialOptions;
351
- if (isApiMode) {
352
- // 텍스트가 변경되었거나 비워졌으면 즉시 옵션 완전히 제거
353
- if (this.lastSearchValue !== value) {
354
- if (!value.trim()) {
355
- // 검색어가 비워졌으면 옵션 완전히 비우기
356
- this.model.updateOptions([]);
357
- } else {
358
- // 검색어가 변경되었으면 이전 옵션 즉시 제거
359
- this.model.updateOptions([]);
360
- }
361
- this.lastSearchValue = value;
362
- }
357
+ // API 모드에서는 검색어 변경 추적만 하고, 옵션은 디바운싱 후 비우기
358
+ if (isApiMode && this.lastSearchValue !== value) {
359
+ this.lastSearchValue = value;
363
360
  }
364
361
  // 디바운싱이 설정되어 있으면 입력이 멈춘 후 onSearch 콜백만 실행
365
362
  if (this.config.searchDebounceMs > 0) {
@@ -367,26 +364,40 @@ var UnifiedSelectBox = /** @class */function () {
367
364
  var timerId_1 = Date.now() + Math.random();
368
365
  this.currentDebounceTimerId = timerId_1;
369
366
  this.searchDebounceTimer = window.setTimeout(function () {
370
- var _a, _b;
371
- // 타이머가 취소되었는지 확인 (다른 타이머가 시작되었으면 현재 타이머는 무시)
372
- if (_this.currentDebounceTimerId !== timerId_1) {
373
- return; // 이 타이머는 취소되었으므로 실행하지 않음
374
- }
375
- // 타이머가 여전히 유효하면 실행
376
- _this.searchDebounceTimer = undefined;
377
- _this.currentDebounceTimerId = undefined;
378
- // API 모드인 경우 필터링 업데이트 (디바운싱 후 실행)
379
- if (isApiMode) {
380
- _this.model.updateSearchAndFilter(value);
381
- }
382
- // 텍스트가 있을 때만 드롭다운 열기
383
- if (value.trim()) {
384
- _this.controller.open();
385
- } else if (!_this.model.getState().showAllItems) {
386
- _this.controller.close();
387
- }
388
- // onSearch 콜백 호출 (디바운싱 적용, 최종 타이머만 실행)
389
- (_b = (_a = _this.config).onSearch) === null || _b === void 0 ? void 0 : _b.call(_a, value);
367
+ return __awaiter(_this, void 0, void 0, function () {
368
+ var _a, _b;
369
+ return __generator(this, function (_c) {
370
+ switch (_c.label) {
371
+ case 0:
372
+ // 타이머가 취소되었는지 확인 (다른 타이머가 시작되었으면 현재 타이머는 무시)
373
+ if (this.currentDebounceTimerId !== timerId_1) {
374
+ return [2 /*return*/]; // 이 타이머는 취소되었으므로 실행하지 않음
375
+ }
376
+ // 타이머가 여전히 유효하면 실행
377
+ this.searchDebounceTimer = undefined;
378
+ this.currentDebounceTimerId = undefined;
379
+ // API 모드: 검색어만 업데이트 (필터링은 하지 않음, 서버에서 필터링된 결과를 받음)
380
+ // 일반 모드: 검색어 업데이트 + 로컬 필터링
381
+ if (isApiMode) {
382
+ this.model.updateSearchValue(value);
383
+ } else {
384
+ this.model.updateSearchAndFilter(value);
385
+ }
386
+ // onSearch 콜백 호출 완료 대기 (디바운싱 적용, 최종 타이머만 실행)
387
+ return [4 /*yield*/, (_b = (_a = this.config).onSearch) === null || _b === void 0 ? void 0 : _b.call(_a, value)];
388
+ case 1:
389
+ // onSearch 콜백 호출 및 완료 대기 (디바운싱 적용, 최종 타이머만 실행)
390
+ _c.sent();
391
+ // 텍스트가 있을 때만 드롭다운 열기
392
+ if (value.trim()) {
393
+ this.controller.open();
394
+ } else if (!this.model.getState().showAllItems) {
395
+ this.controller.close();
396
+ }
397
+ return [2 /*return*/];
398
+ }
399
+ });
400
+ });
390
401
  }, this.config.searchDebounceMs);
391
402
  } else {
392
403
  // 디바운싱이 0으로 명시적으로 설정되었으면 즉시 실행
@@ -415,6 +426,7 @@ var UnifiedSelectBox = /** @class */function () {
415
426
  }
416
427
  };
417
428
  UnifiedSelectBox.prototype.handleClearInput = function () {
429
+ this.cancelDebounceTimer();
418
430
  // input 필드 텍스트 지우기
419
431
  if (this.inputField) {
420
432
  this.inputField.setValue('');
@@ -459,6 +471,7 @@ var UnifiedSelectBox = /** @class */function () {
459
471
  };
460
472
  UnifiedSelectBox.prototype.handleSelectionChange = function (selectedValues) {
461
473
  var _a, _b;
474
+ this.cancelDebounceTimer();
462
475
  var result = this.config.multiple ? selectedValues : selectedValues[0] || '';
463
476
  (_b = (_a = this.config).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, result);
464
477
  // UI 업데이트
@@ -616,12 +629,7 @@ var UnifiedSelectBox = /** @class */function () {
616
629
  }
617
630
  };
618
631
  UnifiedSelectBox.prototype.destroy = function () {
619
- // 디바운스 타이머 정리
620
- if (this.searchDebounceTimer) {
621
- clearTimeout(this.searchDebounceTimer);
622
- this.searchDebounceTimer = undefined;
623
- }
624
- this.currentDebounceTimerId = undefined;
632
+ this.cancelDebounceTimer();
625
633
  this.controller.destroy();
626
634
  this.renderer.clearCache();
627
635
  this.inputField = undefined;
@@ -667,6 +675,32 @@ var UnifiedSelectBox = /** @class */function () {
667
675
  isOpen: this.model.getState().isOpen
668
676
  };
669
677
  };
678
+ /**
679
+ * 드롭다운을 스크롤의 바닥으로 이동
680
+ * 전체 선택 시 추가 데이터 로드를 트리거하기 위해 사용
681
+ */
682
+ UnifiedSelectBox.prototype.scrollToBottom = function () {
683
+ if (!this.dropdownElement) return;
684
+ var optionsList = this.dropdownElement.querySelector('.ncua-select-dropdown__options');
685
+ if (!optionsList) return;
686
+ // 스크롤을 바닥으로 이동
687
+ optionsList.scrollTop = optionsList.scrollHeight;
688
+ };
689
+ /**
690
+ * 전체 선택 버튼의 텍스트를 외부에서 변경
691
+ * @param text 버튼에 표시할 텍스트
692
+ */
693
+ UnifiedSelectBox.prototype.setSelectAllButtonText = function (text) {
694
+ if (!this.config.multiple || !this.footerElement) return;
695
+ this.renderer.updateSelectAllButtonText(this.footerElement, text);
696
+ };
697
+ /**
698
+ * 특정 인덱스의 옵션으로 포커스 이동
699
+ * @param index 포커스를 이동할 옵션의 인덱스
700
+ */
701
+ UnifiedSelectBox.prototype.setFocusIndex = function (index) {
702
+ this.controller.setFocus(index);
703
+ };
670
704
  return UnifiedSelectBox;
671
705
  }();
672
706
  export { UnifiedSelectBox };
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import classnames from 'classnames';
3
+ export var ButtonStepper = function (_a) {
4
+ var size = _a.size,
5
+ direction = _a.direction,
6
+ disabled = _a.disabled,
7
+ onClick = _a.onClick,
8
+ className = _a.className;
9
+ return _jsx("button", {
10
+ className: classnames('ncua-button-stepper', "ncua-button-stepper--".concat(size), "ncua-button-stepper--".concat(direction), className),
11
+ disabled: disabled,
12
+ onClick: onClick
13
+ });
14
+ };
@@ -1,2 +1,4 @@
1
1
  export * from './Button';
2
- export * from './ButtonGroup';
2
+ export * from './ButtonCloseX';
3
+ export * from './ButtonGroup';
4
+ export * from './ButtonStepper';