@ncds/ui-admin 1.5.0 → 1.5.2

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.
@@ -17,10 +17,11 @@ var __rest = this && this.__rest || function (s, e) {
17
17
  return t;
18
18
  };
19
19
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
20
+ /** biome-ignore-all lint/correctness/noUnusedVariables: <explanation> */
20
21
  import classNames from 'classnames';
21
22
  import { Korean } from 'flatpickr/dist/l10n/ko';
22
23
  import moment from 'moment';
23
- import { forwardRef, useId, useMemo } from 'react';
24
+ import { forwardRef, useId, useMemo, useRef } from 'react';
24
25
  import Flatpickr from 'react-flatpickr';
25
26
  import { formatDateInput, formatHourInput, formatMinuteInput } from '../../utils/date-picker';
26
27
  import { CustomInput } from './CustomInput';
@@ -38,17 +39,117 @@ export var DatePicker = /*#__PURE__*/forwardRef(function (_a, ref) {
38
39
  datePickerOptions = _a.datePickerOptions,
39
40
  _d = _a.isEndDate,
40
41
  isEndDate = _d === void 0 ? false : _d,
41
- attrs = __rest(_a, ["shouldFocus", "currentDate", "size", "onChangeDate", "datePickerOptions", "isEndDate"]);
42
- var onChangeDateHandler = function (dateTimeStamp, dateStr) {
42
+ onValidationError = _a.onValidationError,
43
+ attrs = __rest(_a, ["shouldFocus", "currentDate", "size", "onChangeDate", "datePickerOptions", "isEndDate", "onValidationError"]);
44
+ var flatpickrInstanceRef = useRef(null);
45
+ var dateFormatRef = useRef('Y-m-d');
46
+ var minMaxDateRef = useRef({});
47
+ var checkDateViolations = function (date, minDate, maxDate) {
48
+ var violations = [];
49
+ var inputDate = moment(date);
50
+ if (!inputDate.isValid()) return violations;
51
+ if (minDate) {
52
+ var min = moment(minDate);
53
+ if (min.isValid() && inputDate.isBefore(min, 'day')) {
54
+ violations.push('minDate');
55
+ }
56
+ }
57
+ if (maxDate) {
58
+ var max = moment(maxDate);
59
+ if (max.isValid() && inputDate.isAfter(max, 'day')) {
60
+ violations.push('maxDate');
61
+ }
62
+ }
63
+ return violations;
64
+ };
65
+ var onChangeDateHandler = function (dateTimeStamp, dateStr, instance) {
66
+ var _a;
67
+ if (!dateTimeStamp || dateTimeStamp.length === 0) {
68
+ restorePreviousDate(instance.input, instance);
69
+ return;
70
+ }
71
+ var selectedDate = dateTimeStamp[0];
72
+ if (!selectedDate || !(selectedDate instanceof Date) || isNaN(selectedDate.getTime())) {
73
+ // 유효하지 않은 날짜 - 이전 값으로 복원하거나 빈 값으로 처리
74
+ var previousDate = instance === null || instance === void 0 ? void 0 : instance._previousDateBeforeInput;
75
+ if (previousDate && previousDate instanceof Date && !isNaN(previousDate.getTime())) {
76
+ // 이전 값이 있으면 이전 값으로 복원
77
+ instance.selectedDates = [previousDate];
78
+ instance.setDate(previousDate, false);
79
+ } else {
80
+ // 이전 값이 없으면 빈 값으로 처리
81
+ restorePreviousDate(instance.input, instance);
82
+ }
83
+ return;
84
+ }
85
+ var minDate = options.minDate,
86
+ maxDate = options.maxDate;
87
+ var violations = checkDateViolations(selectedDate, minDate, maxDate);
88
+ if (violations.length > 0) {
89
+ var prevDate = (_a = instance === null || instance === void 0 ? void 0 : instance.selectedDates) === null || _a === void 0 ? void 0 : _a[0];
90
+ if (onValidationError) {
91
+ onValidationError({
92
+ date: selectedDate,
93
+ minDate: minDate,
94
+ maxDate: maxDate,
95
+ violations: violations,
96
+ previousDate: prevDate instanceof Date && !isNaN(prevDate.getTime()) ? prevDate : undefined
97
+ });
98
+ return; // onValidationError가 있으면 날짜 변경 안 함
99
+ }
100
+ // onValidationError가 없으면 범위를 벗어난 날짜도 허용 (계속 진행)
101
+ }
102
+ // 유효한 날짜인 경우 이전 날짜로 저장
103
+ instance._previousDateBeforeInput = selectedDate;
43
104
  var formattedDate = formatDateInput(dateStr);
44
105
  isValidDate(formattedDate) ? onChangeDate(formattedDate) : onChangeDate(dateStr);
45
106
  };
107
+ var restorePreviousDate = function (target, instance) {
108
+ if (instance.selectedDates.length > 0) {
109
+ var prevDate = instance.selectedDates[0];
110
+ if (prevDate instanceof Date && !isNaN(prevDate.getTime())) {
111
+ var momentFormat = dateFormatRef.current.replace(/Y/g, 'YYYY').replace(/m/g, 'MM').replace(/d/g, 'DD');
112
+ target.value = moment(prevDate).format(momentFormat);
113
+ instance.setDate(prevDate, false);
114
+ return;
115
+ }
116
+ }
117
+ target.value = '';
118
+ instance.setDate('', false);
119
+ };
46
120
  var onInputHandler = function (e) {
47
121
  var target = e.target;
48
122
  var input = target.value;
123
+ var instance = flatpickrInstanceRef.current;
124
+ if (!instance) return;
125
+ if (!input || input.trim() === '') {
126
+ return;
127
+ }
128
+ // 숫자가 최소 하나는 포함되어야 함 (하이픈만 있는 경우 방지)
129
+ if (!/[0-9]/.test(input)) {
130
+ restorePreviousDate(target, instance);
131
+ return;
132
+ }
49
133
  var formattedInput = formatDateInput(input);
50
- if (formattedInput === input) return;
51
- target.value = formattedInput;
134
+ if (formattedInput !== input) {
135
+ target.value = formattedInput;
136
+ return;
137
+ }
138
+ if (formattedInput && formattedInput.length >= 10) {
139
+ var parsedDate = moment(formattedInput);
140
+ if (!parsedDate.isValid()) {
141
+ restorePreviousDate(target, instance);
142
+ return;
143
+ }
144
+ var parsedDateObj = parsedDate.toDate();
145
+ var violations = checkDateViolations(parsedDateObj, minMaxDateRef.current.minDate, minMaxDateRef.current.maxDate);
146
+ if (violations.length > 0) {
147
+ // onInputHandler에서는 onValidationError를 호출하지 않음
148
+ // validation은 onChangeDateHandler에서만 처리
149
+ // onValidationError가 없으면 아무것도 하지 않음 (범위를 벗어난 날짜도 허용)
150
+ return;
151
+ }
152
+ }
52
153
  };
53
154
  var onHourInputHandler = function (e) {
54
155
  var target = e.target;
@@ -72,13 +173,50 @@ export var DatePicker = /*#__PURE__*/forwardRef(function (_a, ref) {
72
173
  dateFormat: 'Y-m-d',
73
174
  clickOpens: true,
74
175
  time_24hr: true,
176
+ formatDate: function (date, format, locale) {
177
+ try {
178
+ // 유효한 날짜인지 확인
179
+ if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
180
+ return '';
181
+ }
182
+ // moment로 포맷팅
183
+ var momentDate = moment(date);
184
+ if (!momentDate.isValid()) {
185
+ return '';
186
+ }
187
+ // format을 moment 형식으로 변환
188
+ var momentFormat = format.replace(/Y/g, 'YYYY').replace(/y/g, 'YY').replace(/m/g, 'MM').replace(/d/g, 'DD').replace(/H/g, 'HH').replace(/i/g, 'mm').replace(/S/g, 'ss');
189
+ return momentDate.format(momentFormat);
190
+ } catch (error) {
191
+ // 오류 발생 시 빈 문자열 반환 (232-23 같은 잘못된 날짜 처리)
192
+ return '';
193
+ }
194
+ },
75
195
  onChange: onChangeDateHandler,
76
196
  allowInvalidPreload: true,
77
197
  onReady: function (selectedDates, dateStr, instance) {
78
198
  var _a;
79
199
  var input = instance.input;
80
200
  if (!input) return;
201
+ flatpickrInstanceRef.current = instance;
202
+ dateFormatRef.current = (datePickerOptions === null || datePickerOptions === void 0 ? void 0 : datePickerOptions.dateFormat) || options.dateFormat || 'Y-m-d';
203
+ minMaxDateRef.current = {
204
+ minDate: (datePickerOptions === null || datePickerOptions === void 0 ? void 0 : datePickerOptions.minDate) || options.minDate,
205
+ maxDate: (datePickerOptions === null || datePickerOptions === void 0 ? void 0 : datePickerOptions.maxDate) || options.maxDate
206
+ };
207
+ // blur 시점에 현재 selectedDates를 저장 (입력 전 상태)
208
+ var onBlurHandler = function (e) {
209
+ if (instance && instance.selectedDates.length > 0) {
210
+ // 이전 날짜를 별도로 저장 (onChange에서 사용)
211
+ instance._previousDateBeforeInput = instance.selectedDates[0];
212
+ }
213
+ };
81
214
  input.addEventListener('input', onInputHandler);
215
+ input.addEventListener('blur', onBlurHandler);
216
+ // 초기 날짜가 있으면 저장
217
+ if (instance.selectedDates.length > 0) {
218
+ instance._previousDateBeforeInput = instance.selectedDates[0];
219
+ }
82
220
  var timeInputWrapper = (_a = input.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('.flatpickr-time');
83
221
  if (!timeInputWrapper) return;
84
222
  var hourInput = timeInputWrapper.querySelector('.flatpickr-hour');
@@ -100,13 +238,20 @@ export var DatePicker = /*#__PURE__*/forwardRef(function (_a, ref) {
100
238
  };
101
239
  document.addEventListener('mousedown', handleMouseDown, true);
102
240
  instance._handleMouseDown = handleMouseDown;
241
+ instance._onBlurHandler = onBlurHandler;
103
242
  },
104
243
  onDestroy: function (selectedDates, dateStr, instance) {
105
244
  var _a;
106
245
  var input = instance.input;
107
246
  if (!input) return;
247
+ flatpickrInstanceRef.current = null;
108
248
  // 메인 input 이벤트 리스너 제거
109
249
  input.removeEventListener('input', onInputHandler);
250
+ // blur 이벤트 리스너 제거
251
+ var onBlurHandler = instance === null || instance === void 0 ? void 0 : instance._onBlurHandler;
252
+ if (onBlurHandler) {
253
+ input.removeEventListener('blur', onBlurHandler);
254
+ }
110
255
  var timeInputWrapper = (_a = input.parentElement) === null || _a === void 0 ? void 0 : _a.querySelector('.flatpickr-time');
111
256
  if (!timeInputWrapper) return;
112
257
  // 시간 input 이벤트 리스너 제거
@@ -159,7 +304,17 @@ export var DatePicker = /*#__PURE__*/forwardRef(function (_a, ref) {
159
304
  var defaultValue = _a.defaultValue,
160
305
  value = _a.value,
161
306
  props = __rest(_a, ["defaultValue", "value"]);
162
- return _jsx(CustomInput, __assign({}, props, {
307
+ // input에 반영 되면 안되는 attribute 제외
308
+ var _b = props,
309
+ allowInput = _b.allowInput,
310
+ dateFormat = _b.dateFormat,
311
+ minDate = _b.minDate,
312
+ maxDate = _b.maxDate,
313
+ enableTime = _b.enableTime,
314
+ enableSeconds = _b.enableSeconds,
315
+ noCalendar = _b.noCalendar,
316
+ inputProps = __rest(_b, ["allowInput", "dateFormat", "minDate", "maxDate", "enableTime", "enableSeconds", "noCalendar"]);
317
+ return _jsx(CustomInput, __assign({}, inputProps, {
163
318
  id: inputId,
164
319
  iconSize: size,
165
320
  disabled: !!attrs.disabled,
@@ -27,4 +27,5 @@ export * from './switch';
27
27
  export * from './tab';
28
28
  export * from './tag';
29
29
  export * from './toggle';
30
- export * from './tooltip';
30
+ export * from './tooltip';
31
+ export * from './image-file-input';
@@ -17,11 +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';
22
20
  import { Eye, EyeOff } from '@ncds/ui-admin-icon';
23
21
  import classNames from 'classnames';
24
- import { useMergeRefs, useCallbackRef } from '../../hooks';
22
+ import { forwardRef, useState } from 'react';
23
+ import { useCallbackRef, useMergeRefs } from '../../hooks';
24
+ import { InputBase } from './InputBase';
25
25
  var svgSize = {
26
26
  xs: 14,
27
27
  sm: 20
@@ -70,6 +70,7 @@ export var PasswordInput = /*#__PURE__*/forwardRef(function (_a, ref) {
70
70
  placement: 'inside',
71
71
  children: _jsx("button", __assign({
72
72
  className: classNames('ncua-input__icon-wrap', 'ncua-input__right-icon', 'ncua-input__password-icon'),
73
+ type: "button",
73
74
  onClick: handleVisibilityChange
74
75
  }, {
75
76
  children: isVisible ? _jsx(Eye, __assign({}, svgProps)) : _jsx(EyeOff, __assign({}, svgProps))
@@ -39,8 +39,8 @@ export var Pagination = function (_a) {
39
39
  var _f = useState(1),
40
40
  start = _f[0],
41
41
  setStart = _f[1];
42
- var noPrev = start === 1;
43
- var noNext = start + pageCount - 1 >= totalPage;
42
+ var noPrev = breakPoint === 'mo' ? currentPage === 1 : start === 1;
43
+ var noNext = breakPoint === 'mo' ? currentPage >= totalPage : start + pageCount - 1 >= totalPage;
44
44
  var showJumpPageButton = totalPage > pageCount;
45
45
  var handleClickButton = function (pageNum) {
46
46
  if (isFunction(onPageChange)) {
@@ -62,7 +62,7 @@ export var Pagination = function (_a) {
62
62
  setStart(1);
63
63
  }
64
64
  if (currentPage === totalPage) {
65
- var lastGroupStart = Math.max(1, totalPage - pageCount + 1);
65
+ var lastGroupStart = Math.floor((totalPage - 1) / pageCount) * pageCount + 1;
66
66
  setStart(lastGroupStart);
67
67
  }
68
68
  }, [currentPage]);
@@ -85,7 +85,7 @@ export var Pagination = function (_a) {
85
85
  noPrev: noPrev,
86
86
  noNext: noNext,
87
87
  onClick: function () {
88
- return handleClickButton(Math.max(start - pageCount, 1));
88
+ return breakPoint === 'mo' ? handleClickButton(Math.max(currentPage - 1, 1)) : handleClickButton(Math.max(start - pageCount, 1));
89
89
  }
90
90
  })]
91
91
  }), _jsx("ul", __assign({
@@ -122,7 +122,7 @@ export var Pagination = function (_a) {
122
122
  noPrev: noPrev,
123
123
  noNext: noNext,
124
124
  onClick: function () {
125
- return handleClickButton(Math.min(start + pageCount, totalPage));
125
+ return breakPoint === 'mo' ? handleClickButton(Math.min(currentPage + 1, totalPage)) : handleClickButton(Math.min(start + pageCount, totalPage));
126
126
  }
127
127
  }), NavButton({
128
128
  type: 'last',
@@ -1,10 +1,19 @@
1
1
  import { Options as FlatpickrOptions } from 'flatpickr/dist/types/options';
2
2
  export type DatePickerSize = 'xs' | 'sm';
3
+ export interface ValidationError {
4
+ index: number;
5
+ date: string | Date;
6
+ minDate?: string | Date;
7
+ maxDate?: string | Date;
8
+ violations: Array<'minDate' | 'maxDate'>;
9
+ previousDate?: string | Date;
10
+ }
3
11
  interface Options {
4
12
  buttons?: DatePickerButton[];
5
13
  size: DatePickerSize;
6
14
  autoComplete?: 'on' | 'off';
7
15
  datePickerOptions: DatePickerOptionWrapper[];
16
+ onValidationError?: (error: ValidationError) => void;
8
17
  }
9
18
  interface DatePickerOptionWrapper {
10
19
  element: string;
@@ -31,6 +40,7 @@ export declare class DatePicker {
31
40
  private flatpickrInstances;
32
41
  private currentButton;
33
42
  private dateFormat;
43
+ private onValidationError?;
34
44
  constructor(wrapper: HTMLElement, options: Options);
35
45
  private validateWrapper;
36
46
  private init;
@@ -41,11 +51,35 @@ export declare class DatePicker {
41
51
  private reRenderButtonGroup;
42
52
  private initDatePicker;
43
53
  private initializeFlatpickr;
54
+ private createBlurHandler;
55
+ private setupFlatpickrLocale;
56
+ private createInputHandler;
57
+ private findFlatpickrInstanceByInput;
58
+ private clearInputValue;
59
+ private restorePreviousDate;
60
+ private createHourInputHandler;
61
+ private createMinuteInputHandler;
62
+ private createMouseDownHandler;
63
+ private createOnChangeHandler;
64
+ private invokeOriginalOnChange;
65
+ private isValidDate;
66
+ private validateAndRestoreIfNeeded;
67
+ private findFlatpickrInstanceIndexByInput;
68
+ private restoreDateToInstance;
69
+ private createOnReadyHandler;
70
+ private createOnDestroyHandler;
71
+ private convertDateOption;
72
+ private validateDate;
73
+ private checkDateViolations;
74
+ setDate(dates: string[]): void;
75
+ private formatDateForOption;
76
+ private parseDateWithMultipleFormats;
77
+ private formatMomentDate;
78
+ private calculatePeriod;
44
79
  private updateDateWithButton;
45
80
  private setMultipleDates;
46
81
  private updateButtonsByPeriod;
47
82
  private calculateDatesFromButton;
48
- setDate(dates: string[]): void;
49
83
  private convertFlatpickrFormatToMoment;
50
84
  getDates(): string[];
51
85
  }
@@ -10,6 +10,13 @@ export type DatePickerProps = {
10
10
  placeholder?: string;
11
11
  isEndDate?: boolean;
12
12
  onChangeDate: (date: string) => void;
13
+ onValidationError?: (error: {
14
+ date: Date;
15
+ minDate?: any;
16
+ maxDate?: any;
17
+ violations: Array<'minDate' | 'maxDate'>;
18
+ previousDate?: Date;
19
+ }) => void;
13
20
  } & Omit<DateTimePickerProps, 'size' | 'options' | 'value'>;
14
21
  export declare const DatePicker: import("react").ForwardRefExoticComponent<Omit<DatePickerProps, "ref"> & import("react").RefAttributes<DateTimePickerHandle>>;
15
22
  //# sourceMappingURL=DatePicker.d.ts.map
@@ -28,4 +28,5 @@ export * from './tab';
28
28
  export * from './tag';
29
29
  export * from './toggle';
30
30
  export * from './tooltip';
31
+ export * from './image-file-input';
31
32
  //# sourceMappingURL=index.d.ts.map
@@ -58,8 +58,8 @@ export type ModalHeaderProps = {
58
58
  theme: FeaturedIconTheme;
59
59
  };
60
60
  align?: ModalHeaderAlign;
61
- title?: string;
62
- subtitle?: string;
61
+ title?: string | ReactNode;
62
+ subtitle?: string | ReactNode;
63
63
  showDivider?: boolean;
64
64
  hideCloseButton?: boolean;
65
65
  };
@@ -1,3 +1,4 @@
1
+ @charset "UTF-8";
1
2
  :root {
2
3
  --primary-red-50: #fffafd;
3
4
  --primary-red-100: #ffeaee;
@@ -1863,12 +1864,14 @@ button {
1863
1864
  line-height: var(--line-heights-sm);
1864
1865
  color: var(--gray-700);
1865
1866
  word-break: break-all;
1867
+ white-space: pre-line;
1866
1868
  }
1867
1869
  .ncua-modal__title .ncua-modal__title-subtitle {
1868
1870
  font-weight: var(--font-weights-commerce-sans-0);
1869
1871
  font-size: var(--font-size-xs);
1870
1872
  line-height: var(--line-heights-xs);
1871
1873
  color: var(--gray-400);
1874
+ white-space: pre-line;
1872
1875
  }
1873
1876
 
1874
1877
  .ncua-modal__close-button {
@@ -4908,12 +4911,8 @@ button {
4908
4911
  bottom: 0;
4909
4912
  left: 0;
4910
4913
  }
4911
- .ncua-horizontal-tab--button-primary .swiper-slide:first-child, .ncua-horizontal-tab--button-white .swiper-slide:first-child {
4912
- padding-left: 4px;
4913
- }
4914
- .ncua-horizontal-tab--button-primary .swiper-slide:last-child, .ncua-horizontal-tab--button-white .swiper-slide:last-child {
4915
- padding-right: 4px;
4916
- margin-right: 0;
4914
+ .ncua-horizontal-tab--button-primary .swiper-wrapper, .ncua-horizontal-tab--button-white .swiper-wrapper {
4915
+ padding: 0 4px;
4917
4916
  }
4918
4917
  .ncua-horizontal-tab--button-primary .ncua-tab-button:hover, .ncua-horizontal-tab--button-primary .ncua-tab-button.is-active {
4919
4918
  background-color: var(--gray-50);
@@ -5228,10 +5227,19 @@ button {
5228
5227
  }
5229
5228
  .ncua-file-input__hint-list {
5230
5229
  margin: 0;
5231
- list-style: disc;
5230
+ padding: 0;
5232
5231
  color: var(--gray-400);
5232
+ }
5233
+ .ncua-file-input__hint-item {
5234
+ position: relative;
5233
5235
  padding-left: 16px;
5234
5236
  }
5237
+ .ncua-file-input__hint-item::before {
5238
+ content: "•";
5239
+ position: absolute;
5240
+ left: 0;
5241
+ top: 0;
5242
+ }
5235
5243
  .ncua-file-input--xs {
5236
5244
  font-size: var(--font-size-xxs);
5237
5245
  line-height: var(--line-heights-xxs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncds/ui-admin",
3
- "version": "1.5.0",
3
+ "version": "1.5.2",
4
4
  "description": "nhn-commerce의 어드민 디자인 시스템입니다.",
5
5
  "scripts": {
6
6
  "barrel": "node barrel.js",