@uniai-fe/util-functions 0.0.1
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/package.json +64 -0
- package/src/form/checkbox.ts +41 -0
- package/src/functions/api.server.ts +114 -0
- package/src/functions/api.ts +515 -0
- package/src/functions/chart.ts +304 -0
- package/src/functions/convert.ts +229 -0
- package/src/functions/crypto.ts +44 -0
- package/src/functions/date.ts +386 -0
- package/src/functions/file.ts +57 -0
- package/src/functions/format.ts +318 -0
- package/src/functions/log.ts +9 -0
- package/src/functions/mask.ts +175 -0
- package/src/functions/reg-exp.ts +26 -0
- package/src/functions/route.ts +46 -0
- package/src/functions/sort.ts +71 -0
- package/src/functions/validation.ts +155 -0
- package/src/index.tsx +20 -0
- package/src/react/convert.tsx +129 -0
- package/src/react/match.tsx +41 -0
- package/src/style/size.ts +128 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { convert2Digit } from "./convert";
|
|
2
|
+
import { maskPhone } from "./mask";
|
|
3
|
+
import { isValidDateType } from "./validation";
|
|
4
|
+
import type { DateTimeFormatOptionsType } from "@uniai/types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* comma 마스킹된 숫자 Number 타입으로 변환
|
|
8
|
+
* @util
|
|
9
|
+
* @param {number | string} value 타입 변환 값
|
|
10
|
+
* @param {boolean} [isString]
|
|
11
|
+
* @return {number | string}
|
|
12
|
+
*/
|
|
13
|
+
export const getNumberReplaceComma = (
|
|
14
|
+
value: unknown,
|
|
15
|
+
isString?: boolean,
|
|
16
|
+
): number | string => {
|
|
17
|
+
if (
|
|
18
|
+
!["number", "string"].includes(typeof value) ||
|
|
19
|
+
value === null ||
|
|
20
|
+
value === ""
|
|
21
|
+
)
|
|
22
|
+
return "";
|
|
23
|
+
|
|
24
|
+
if (typeof value === "number") return value;
|
|
25
|
+
|
|
26
|
+
const v = Number(String(value).replace(/[^0-9.-]/g, ""));
|
|
27
|
+
if (isNaN(Number(v))) return isString ? "" : 0;
|
|
28
|
+
|
|
29
|
+
return isString ? String(v) : v;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 수량을 1000단위로 comma표기
|
|
34
|
+
* @util
|
|
35
|
+
* @param {unknown} value 숫자
|
|
36
|
+
* @param {number} [digit] 소수점 자리수 고정 옵션
|
|
37
|
+
* @return {string}
|
|
38
|
+
*/
|
|
39
|
+
export const lengthFormat = (value: unknown, digit?: number): string => {
|
|
40
|
+
if (
|
|
41
|
+
!["number", "string"].includes(typeof value) ||
|
|
42
|
+
value === null ||
|
|
43
|
+
value === ""
|
|
44
|
+
)
|
|
45
|
+
return "-";
|
|
46
|
+
|
|
47
|
+
if (isNaN(Number(getNumberReplaceComma(value)))) return String(value);
|
|
48
|
+
|
|
49
|
+
// 소수점 고정 옵션
|
|
50
|
+
const digitOptions: Intl.NumberFormatOptions =
|
|
51
|
+
typeof digit === "number"
|
|
52
|
+
? { minimumFractionDigits: digit, maximumFractionDigits: digit }
|
|
53
|
+
: {};
|
|
54
|
+
|
|
55
|
+
return new Intl.NumberFormat("en-US", digitOptions).format(Number(value));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 날짜 사용 옵션
|
|
60
|
+
*/
|
|
61
|
+
const DATE_FULL_OPTION: Intl.DateTimeFormatOptions = {
|
|
62
|
+
year: "numeric",
|
|
63
|
+
month: "2-digit",
|
|
64
|
+
day: "2-digit",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 날짜 사용 안함 옵션
|
|
69
|
+
*/
|
|
70
|
+
const NO_DATE_OPTION: Intl.DateTimeFormatOptions = {
|
|
71
|
+
year: undefined,
|
|
72
|
+
month: undefined,
|
|
73
|
+
day: undefined,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 24시간 사용 옵션
|
|
78
|
+
*/
|
|
79
|
+
const TIME_FULL_OPTION: Intl.DateTimeFormatOptions = {
|
|
80
|
+
hour12: false,
|
|
81
|
+
hour: "2-digit",
|
|
82
|
+
minute: "2-digit",
|
|
83
|
+
second: "2-digit",
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 시간 사용 안함 옵션
|
|
88
|
+
*/
|
|
89
|
+
const NO_TIME_OPTION: Intl.DateTimeFormatOptions = {
|
|
90
|
+
hour: undefined,
|
|
91
|
+
minute: undefined,
|
|
92
|
+
second: undefined,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* 접속위치에 따른 시간대 보정
|
|
97
|
+
* @util
|
|
98
|
+
* @param {Date} date 보정할 시간
|
|
99
|
+
* @return {Date} 보정된 시간
|
|
100
|
+
*/
|
|
101
|
+
// 변경: correctTimezone 함수를 UTC 산출 후 KST로 변경
|
|
102
|
+
export const correctTimezone = (date: string | Date): Date => {
|
|
103
|
+
const utc =
|
|
104
|
+
new Date(date).getTime() + new Date(date).getTimezoneOffset() * 60000;
|
|
105
|
+
return new Date(utc + 9 * 60 * 60000);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Date 타입 데이터를 yyyy-mm-dd로 변환
|
|
110
|
+
* @util
|
|
111
|
+
* @param {unknown} date
|
|
112
|
+
* @param {DateTimeFormatOptionsType} [options]
|
|
113
|
+
* @param {Intl.LocalesArgument} [options.formatTimezone] 시간 기준; 기본값 ko-KR
|
|
114
|
+
* @param {Intl.DateTimeFormatOptions} [options.formatOptions]
|
|
115
|
+
* @param {boolean | boolean[]} [options.isKorean] // ymd 또는 [year, month?, day?]
|
|
116
|
+
* @param {boolean} [options.isCorrection] 시간 보정
|
|
117
|
+
* @param {boolean} [options.isISOString] ISO 문자열
|
|
118
|
+
* @return {string} yyyy-mm-dd 또는 각 옵션에 대한 날짜포맷
|
|
119
|
+
*/
|
|
120
|
+
// 변경: dateFormat 함수에서 isCorrection 옵션 사용 시 toISOString()의 결과를 슬라이스하여 "YYYY-MM-DDTHH:MM:SS" 형식 반환
|
|
121
|
+
export const dateFormat = (
|
|
122
|
+
date: unknown,
|
|
123
|
+
options?: Partial<DateTimeFormatOptionsType>,
|
|
124
|
+
): string => {
|
|
125
|
+
if (isValidDateType(date)) {
|
|
126
|
+
const d = new Date(date as string | number | Date);
|
|
127
|
+
const corrDate = correctTimezone(d);
|
|
128
|
+
|
|
129
|
+
// toISOString()으로 변환 시, KST 적용되지 않음
|
|
130
|
+
const defaultDateFormat = options?.isCorrection
|
|
131
|
+
? `${corrDate.getFullYear()}-${convert2Digit(corrDate.getMonth() + 1)}-${convert2Digit(corrDate.getDate())}`
|
|
132
|
+
: `${d.getFullYear()}-${convert2Digit(d.getMonth() + 1)}-${convert2Digit(d.getDate())}`;
|
|
133
|
+
|
|
134
|
+
if (options?.isISOString) return defaultDateFormat;
|
|
135
|
+
|
|
136
|
+
const onlyDate = defaultDateFormat || "";
|
|
137
|
+
|
|
138
|
+
if (options?.isKorean) {
|
|
139
|
+
const [y, m, d] = onlyDate.split("-");
|
|
140
|
+
if (options.isKorean === true) return `${y}년 ${m}월 ${d}일`;
|
|
141
|
+
const __ymd = options.isKorean.map((apply, index) => {
|
|
142
|
+
switch (index) {
|
|
143
|
+
case 0:
|
|
144
|
+
return apply ? `${y}년` : "";
|
|
145
|
+
case 1:
|
|
146
|
+
return apply ? `${m}월` : "";
|
|
147
|
+
case 2:
|
|
148
|
+
return apply ? `${d}일` : "";
|
|
149
|
+
default:
|
|
150
|
+
return "";
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return __ymd.join(" ");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (typeof options?.formatOptions !== "undefined")
|
|
157
|
+
return new Intl.DateTimeFormat(
|
|
158
|
+
options?.formatTimezone ? options.formatTimezone : "ko-KR",
|
|
159
|
+
{ ...DATE_FULL_OPTION, ...options.formatOptions, ...NO_TIME_OPTION },
|
|
160
|
+
).format(d);
|
|
161
|
+
return onlyDate;
|
|
162
|
+
}
|
|
163
|
+
return String(date);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Date 타입 데이터를 HH:MM:SS 시간으로 변환
|
|
168
|
+
* @util
|
|
169
|
+
* @param {unknown} date
|
|
170
|
+
* @param {DateTimeFormatOptionsType} [options]
|
|
171
|
+
* @param {Intl.LocalesArgument} [options.formatTimezone] 시간 기준; 기본값 ko-KR
|
|
172
|
+
* @param {Intl.DateTimeFormatOptions} [options.formatOptions]
|
|
173
|
+
* @param {boolean | boolean[]} [options.isKorean] // ymd 또는 [year, month?, day?]
|
|
174
|
+
* @param {boolean} [options.isCorrection] 시간 보정
|
|
175
|
+
* @param {boolean} [options.isISOString] ISO 문자열
|
|
176
|
+
* @return {string} HH:MM:SS
|
|
177
|
+
*/
|
|
178
|
+
// 변경: timeFormat 함수에서 isCorrection 옵션 사용 시 toISOString()의 시간 부분만 슬라이스하여 "HH:MM:SS" 형식 반환
|
|
179
|
+
export const timeFormat = (
|
|
180
|
+
date: unknown,
|
|
181
|
+
options?: Partial<DateTimeFormatOptionsType>,
|
|
182
|
+
): string => {
|
|
183
|
+
if (isValidDateType(date)) {
|
|
184
|
+
const d = new Date(date as string | number | Date);
|
|
185
|
+
|
|
186
|
+
if (options?.isKST) {
|
|
187
|
+
const utc = d.getTime() + 60 * 60 * 1000 * 9;
|
|
188
|
+
d.setTime(utc);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const defaultDateFormat = options?.isCorrection
|
|
192
|
+
? correctTimezone(d).toISOString().split("T")[1].split(".")[0]
|
|
193
|
+
: new Intl.DateTimeFormat("en-US", {
|
|
194
|
+
...TIME_FULL_OPTION,
|
|
195
|
+
...NO_DATE_OPTION,
|
|
196
|
+
}).format(d);
|
|
197
|
+
|
|
198
|
+
if (options?.isISOString) return defaultDateFormat;
|
|
199
|
+
|
|
200
|
+
const onlyDate = defaultDateFormat || "";
|
|
201
|
+
|
|
202
|
+
if (options?.isKorean) {
|
|
203
|
+
const [hr, min, sec] = onlyDate.split(":");
|
|
204
|
+
if (options.isKorean === true) return `${hr}시간 ${min}분 ${sec}초`;
|
|
205
|
+
const __hms = options.isKorean.map((apply, index) => {
|
|
206
|
+
switch (index) {
|
|
207
|
+
case 0:
|
|
208
|
+
return apply ? `${hr}시간` : "";
|
|
209
|
+
case 1:
|
|
210
|
+
return apply ? `${min}분` : "";
|
|
211
|
+
case 2:
|
|
212
|
+
return apply ? `${d}초` : "";
|
|
213
|
+
default:
|
|
214
|
+
return "";
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
return __hms.join(" ");
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (typeof options?.formatOptions !== "undefined") {
|
|
221
|
+
const res = new Intl.DateTimeFormat(
|
|
222
|
+
options?.formatTimezone ? options.formatTimezone : "ko-KR",
|
|
223
|
+
{ ...TIME_FULL_OPTION, ...options.formatOptions, ...NO_DATE_OPTION },
|
|
224
|
+
).format(d);
|
|
225
|
+
|
|
226
|
+
// console.log("res", res);
|
|
227
|
+
return res;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return onlyDate;
|
|
231
|
+
}
|
|
232
|
+
return String(date);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* 소요시간 변환
|
|
237
|
+
* @util
|
|
238
|
+
* @param {number | null} duration 초 단위 시간값
|
|
239
|
+
*/
|
|
240
|
+
export const durationFormat = (
|
|
241
|
+
duration: number | null,
|
|
242
|
+
options?: Partial<{
|
|
243
|
+
hours: boolean;
|
|
244
|
+
minutes: boolean;
|
|
245
|
+
seconds: boolean;
|
|
246
|
+
}>,
|
|
247
|
+
): string => {
|
|
248
|
+
if (duration === null) return String(duration);
|
|
249
|
+
|
|
250
|
+
const hours: number | null =
|
|
251
|
+
options?.hours === false ? null : Math.floor(duration / (60 * 60));
|
|
252
|
+
const minutes: number | null =
|
|
253
|
+
options?.minutes === false ? null : Math.floor((duration % (60 * 60)) / 60);
|
|
254
|
+
const seconds: number | null =
|
|
255
|
+
options?.seconds === false ? null : duration % 60;
|
|
256
|
+
|
|
257
|
+
const HH = hours !== null ? `${hours}시간 ` : "";
|
|
258
|
+
const MM = minutes !== null ? `${minutes}분 ` : "";
|
|
259
|
+
const SS = seconds !== null ? `${seconds}초` : "";
|
|
260
|
+
|
|
261
|
+
return `${HH}${MM}${SS}`.trim();
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* 전화번호 타입 데이터로 변환
|
|
266
|
+
* @util
|
|
267
|
+
* @param {string | number} value
|
|
268
|
+
* @return {string}
|
|
269
|
+
* @desc
|
|
270
|
+
* - 규칙: [
|
|
271
|
+
* 999-9999(7),
|
|
272
|
+
* 9999-9999(8),
|
|
273
|
+
* 99-999-9999(9),
|
|
274
|
+
* 99-9999-9999(10),
|
|
275
|
+
* 999-999-9999(10),
|
|
276
|
+
* 999-9999-9999(11),
|
|
277
|
+
* ];
|
|
278
|
+
* - 지역번호 구분:
|
|
279
|
+
* 01X: 휴대전화
|
|
280
|
+
* 02 ~ 0XX: 지역번호
|
|
281
|
+
*/
|
|
282
|
+
export const phoneNumberFormat = maskPhone;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 정수 숫자를 +/-를 붙인 형태로 변환
|
|
286
|
+
* @util
|
|
287
|
+
* @param {unknown} value
|
|
288
|
+
* @param {boolean} [isMaskLength] 길이 마스킹 여부
|
|
289
|
+
* @return {string}
|
|
290
|
+
*/
|
|
291
|
+
export const deltaFormat = (
|
|
292
|
+
value: unknown,
|
|
293
|
+
digit: number = 0,
|
|
294
|
+
isMaskLength: boolean = true,
|
|
295
|
+
): string => {
|
|
296
|
+
if (typeof value === "undefined" || value === null || isNaN(Number(value)))
|
|
297
|
+
return "-";
|
|
298
|
+
|
|
299
|
+
const v = Number(value);
|
|
300
|
+
|
|
301
|
+
if (v > 0) return isMaskLength ? `+${lengthFormat(v, digit)}` : `+${v}`;
|
|
302
|
+
return lengthFormat(v, digit);
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* 거리를 m 또는 km로 변환
|
|
307
|
+
* @util
|
|
308
|
+
* @param {number | null} distance
|
|
309
|
+
* @return {string}
|
|
310
|
+
*/
|
|
311
|
+
export const distanceFormat = (
|
|
312
|
+
distance: number | null,
|
|
313
|
+
isUseUnit: boolean = true,
|
|
314
|
+
): string => {
|
|
315
|
+
if (distance === null) return "-";
|
|
316
|
+
if (distance < 1000) return `${distance}${isUseUnit ? "m" : ""}`;
|
|
317
|
+
return `${Math.floor(distance / 1000)}${isUseUnit ? "km" : ""}`;
|
|
318
|
+
};
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 전화번호 포맷 마스크
|
|
3
|
+
* @util
|
|
4
|
+
* @param {string | number} value
|
|
5
|
+
* @return {string}
|
|
6
|
+
* @desc
|
|
7
|
+
* - 규칙: [
|
|
8
|
+
* 999-9999(7),
|
|
9
|
+
* 9999-9999(8),
|
|
10
|
+
* 99-999-9999(9),
|
|
11
|
+
* 99-9999-9999(10),
|
|
12
|
+
* 999-999-9999(10),
|
|
13
|
+
* 999-9999-9999(11),
|
|
14
|
+
* ];
|
|
15
|
+
* - 지역번호 구분:
|
|
16
|
+
* 01X: 휴대전화
|
|
17
|
+
* 02 ~ 0XX: 지역번호
|
|
18
|
+
*/
|
|
19
|
+
export function maskPhone(value: string | number): string {
|
|
20
|
+
// 입력값
|
|
21
|
+
const originValue = String(value).replace(/[^0-9]/g, "");
|
|
22
|
+
|
|
23
|
+
if (isNaN(Number(originValue))) return originValue;
|
|
24
|
+
|
|
25
|
+
// 11자리 제한
|
|
26
|
+
const v = originValue.length > 11 ? originValue.slice(0, 11) : originValue;
|
|
27
|
+
|
|
28
|
+
let maskValue = v;
|
|
29
|
+
|
|
30
|
+
// 999-9999
|
|
31
|
+
if (3 < v.length && v.length <= 7) {
|
|
32
|
+
maskValue = `${v.slice(0, 3)}-${v.slice(3)}`;
|
|
33
|
+
}
|
|
34
|
+
// 9999-9999
|
|
35
|
+
else if (v.length === 8) {
|
|
36
|
+
maskValue = `${v.slice(0, 4)}-${v.slice(4)}`;
|
|
37
|
+
}
|
|
38
|
+
// 99-999-9999
|
|
39
|
+
else if (v.length === 9) {
|
|
40
|
+
maskValue = `${v.slice(0, 2)}-${v.slice(2, 5)}-${v.slice(5)}`;
|
|
41
|
+
}
|
|
42
|
+
// 99-9999-9999, 999-999-9999
|
|
43
|
+
else if (v.length === 10) {
|
|
44
|
+
const firstDigits = v.startsWith("02") ? 2 : 3;
|
|
45
|
+
// 02-9999-9999
|
|
46
|
+
if (firstDigits === 2) {
|
|
47
|
+
maskValue = `${v.slice(0, 2)}-${v.slice(2, 6)}-${v.slice(6)}`;
|
|
48
|
+
}
|
|
49
|
+
// 0XX-999-9999
|
|
50
|
+
else {
|
|
51
|
+
maskValue = `${v.slice(0, 3)}-${v.slice(3, 6)}-${v.slice(6)}`;
|
|
52
|
+
}
|
|
53
|
+
} else if (v.length > 10) {
|
|
54
|
+
maskValue = `${v.slice(0, 3)}-${v.slice(3, 7)}-${v.slice(7)}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return maskValue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 주민등록번호 포맷 마스크
|
|
62
|
+
* @util
|
|
63
|
+
* @param {string | number} value
|
|
64
|
+
* @return {string}
|
|
65
|
+
* @desc
|
|
66
|
+
* - 규칙: 999999-9999999
|
|
67
|
+
*/
|
|
68
|
+
export function maskResidentCode(value: string | number): string {
|
|
69
|
+
// 입력값
|
|
70
|
+
const originValue = String(value).replace(/[^0-9]/g, "");
|
|
71
|
+
|
|
72
|
+
if (isNaN(Number(originValue))) return originValue;
|
|
73
|
+
|
|
74
|
+
// 11자리 제한
|
|
75
|
+
const v = originValue.length > 6 ? originValue.slice(0, 6) : originValue;
|
|
76
|
+
return v.length > 6 ? v.slice(0, 6) : v;
|
|
77
|
+
|
|
78
|
+
let maskValue = v;
|
|
79
|
+
|
|
80
|
+
// 999999-9999999
|
|
81
|
+
if (6 < v.length) {
|
|
82
|
+
maskValue = `${v.slice(0, 6)}-${v.slice(6)}`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return maskValue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 사업자등록번호 포맷 마스크
|
|
90
|
+
* @util
|
|
91
|
+
* @param {string | number} value
|
|
92
|
+
* @return {string}
|
|
93
|
+
* @desc
|
|
94
|
+
* - 규칙: 999-99-99999
|
|
95
|
+
*/
|
|
96
|
+
export function maskBusinessCode(value: string | number): string {
|
|
97
|
+
// 입력값
|
|
98
|
+
const originValue = String(value).replace(/[^0-9]/g, "");
|
|
99
|
+
|
|
100
|
+
if (isNaN(Number(originValue))) return originValue;
|
|
101
|
+
|
|
102
|
+
// 11자리 제한
|
|
103
|
+
const v = originValue.length > 10 ? originValue.slice(0, 10) : originValue;
|
|
104
|
+
|
|
105
|
+
let maskValue = v;
|
|
106
|
+
|
|
107
|
+
// 999-99-99999
|
|
108
|
+
if (3 < v.length && v.length < 6) {
|
|
109
|
+
maskValue = `${v.slice(0, 3)}-${v.slice(3)}`;
|
|
110
|
+
} else if (6 <= v.length) {
|
|
111
|
+
maskValue = `${v.slice(0, 3)}-${v.slice(3, 5)}-${v.slice(5)}`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return maskValue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 1,000단위 숫자 구분 comma (decimal separator)
|
|
119
|
+
* @util
|
|
120
|
+
* @param {string | number} value
|
|
121
|
+
* @param {Intl.NumberFormatOptions} options
|
|
122
|
+
* @return {string}
|
|
123
|
+
*/
|
|
124
|
+
export function maskDecimalSeparator(
|
|
125
|
+
value: string | number,
|
|
126
|
+
options?: Intl.NumberFormatOptions,
|
|
127
|
+
): string {
|
|
128
|
+
// 입력값
|
|
129
|
+
const pureNumberValue = String(value).replace(/[^0-9.-]/g, "");
|
|
130
|
+
|
|
131
|
+
if (isNaN(Number(pureNumberValue))) return pureNumberValue;
|
|
132
|
+
|
|
133
|
+
const originValue = String(value).replace(/[^0-9,.-]/g, "");
|
|
134
|
+
const isPoint =
|
|
135
|
+
originValue !== "" && originValue.indexOf(".") === originValue.length - 1;
|
|
136
|
+
|
|
137
|
+
const v = String(value).replace(/[^0-9.-]/g, "");
|
|
138
|
+
if (isNaN(Number(v))) return String(originValue);
|
|
139
|
+
|
|
140
|
+
return v === ""
|
|
141
|
+
? ""
|
|
142
|
+
: `${new Intl.NumberFormat("en-US", {
|
|
143
|
+
style: "decimal",
|
|
144
|
+
...options,
|
|
145
|
+
}).format(Number(v))}${isPoint ? "." : ""}`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* 날짜 표현
|
|
150
|
+
* @util
|
|
151
|
+
* @param {string | number} value
|
|
152
|
+
* @return {string}
|
|
153
|
+
* @desc
|
|
154
|
+
* - 규칙 : 9999-99-99
|
|
155
|
+
*/
|
|
156
|
+
export function maskDate(value: string | number): string {
|
|
157
|
+
// 입력값
|
|
158
|
+
const originValue = String(value).replace(/[^0-9]/g, "");
|
|
159
|
+
|
|
160
|
+
if (isNaN(Number(originValue))) return originValue;
|
|
161
|
+
|
|
162
|
+
// 8자리 제한
|
|
163
|
+
const v = originValue.length > 8 ? originValue.slice(0, 8) : originValue;
|
|
164
|
+
|
|
165
|
+
let maskValue = v;
|
|
166
|
+
|
|
167
|
+
// 9999.99.99.
|
|
168
|
+
if (4 < v.length && v.length < 7) {
|
|
169
|
+
maskValue = `${v.slice(0, 4)}-${v.slice(4)}`;
|
|
170
|
+
} else if (7 <= v.length) {
|
|
171
|
+
maskValue = `${v.slice(0, 4)}-${v.slice(4, 6)}-${v.slice(6)}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return maskValue;
|
|
175
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 정규식 패턴으로 특수기호 제거
|
|
3
|
+
* @util
|
|
4
|
+
* @param {string} text
|
|
5
|
+
* @return {string}
|
|
6
|
+
*/
|
|
7
|
+
export const escapeSymbol = (text: string): string => {
|
|
8
|
+
// 정규식 패턴. 특수 문자를 찾습니다.
|
|
9
|
+
const specialCharPattern = /([.*+?^${}()|[\]\\])/g;
|
|
10
|
+
|
|
11
|
+
// 텍스트에서 특수 문자를 찾아서 escape 처리합니다.
|
|
12
|
+
const escapedText = text.replace(specialCharPattern, "\\$1");
|
|
13
|
+
|
|
14
|
+
return escapedText;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 정규식 패턴으로 따옴표 제거
|
|
19
|
+
* @util
|
|
20
|
+
* @param {string} text
|
|
21
|
+
* @return {string}
|
|
22
|
+
*/
|
|
23
|
+
export const escapeQuotes = (text: string): string => {
|
|
24
|
+
const quotesPattern = /["']/g;
|
|
25
|
+
return text.replace(quotesPattern, "");
|
|
26
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { SitemapDataType, SitemapMatchDataType } from "@uniai/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 현재 경로와 가장 근접한 경로 데이터 추출
|
|
5
|
+
* @param {SitemapDataType[]} routes 사이트맵 배열 데이터
|
|
6
|
+
* @param {string} currentPath 현재 경로
|
|
7
|
+
* @return {SitemapDataType | null} 경로 데이터
|
|
8
|
+
*/
|
|
9
|
+
export const getClosestRoute = (
|
|
10
|
+
routes: SitemapDataType[],
|
|
11
|
+
currentPath: string,
|
|
12
|
+
): SitemapDataType | null => {
|
|
13
|
+
let deepestMatch: SitemapDataType | null = null;
|
|
14
|
+
for (const route of routes) {
|
|
15
|
+
if (currentPath.startsWith(route.path)) {
|
|
16
|
+
if (!deepestMatch || route.path.length > deepestMatch.path.length) {
|
|
17
|
+
deepestMatch = route;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return deepestMatch;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 경로 일치 데이터 추출
|
|
26
|
+
* @util
|
|
27
|
+
* @param {SitemapDataType[]} routes 사이트맵 배열 데이터
|
|
28
|
+
* @param {string} currentPath 현재 경로
|
|
29
|
+
* @return {SitemapDataType | undefined} 경로 데이터
|
|
30
|
+
*/
|
|
31
|
+
export const getMatchRoute = (
|
|
32
|
+
routes: SitemapDataType[],
|
|
33
|
+
currentPath: string,
|
|
34
|
+
): SitemapMatchDataType => {
|
|
35
|
+
const res: SitemapMatchDataType = { category: null, depth: null };
|
|
36
|
+
|
|
37
|
+
// 1 depth
|
|
38
|
+
res.category = getClosestRoute(routes, currentPath);
|
|
39
|
+
|
|
40
|
+
// 2 depth
|
|
41
|
+
if (res.category && res.category.depth) {
|
|
42
|
+
res.depth = getClosestRoute(res.category.depth, currentPath);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return res;
|
|
46
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 오름차순 정렬
|
|
3
|
+
* @util
|
|
4
|
+
* @param {string | number | null} a
|
|
5
|
+
* @param {string | number | null} b
|
|
6
|
+
*/
|
|
7
|
+
export const sortAsc = (
|
|
8
|
+
a: string | number | null,
|
|
9
|
+
b: string | number | null,
|
|
10
|
+
) => {
|
|
11
|
+
if (a === null || b === null) {
|
|
12
|
+
if (a === null && b !== null) return 1;
|
|
13
|
+
if (a !== null && b === null) return -1;
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 내림차순 정렬
|
|
21
|
+
* @util
|
|
22
|
+
* @param {string | number | null} a
|
|
23
|
+
* @param {string | number | null} b
|
|
24
|
+
*/
|
|
25
|
+
export const sortDesc = (
|
|
26
|
+
a: string | number | null,
|
|
27
|
+
b: string | number | null,
|
|
28
|
+
) => {
|
|
29
|
+
if (a === null || b === null) {
|
|
30
|
+
if (a === null && b !== null) return 1;
|
|
31
|
+
if (a !== null && b === null) return -1;
|
|
32
|
+
return 0;
|
|
33
|
+
}
|
|
34
|
+
return a > b ? -1 : a < b ? 1 : 0;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 날짜순 오름차순 정렬
|
|
39
|
+
* @util
|
|
40
|
+
* @param {Date | number | string | null} a
|
|
41
|
+
* @param {Date | number | string | null} b
|
|
42
|
+
*/
|
|
43
|
+
export const sortDateAsc = (
|
|
44
|
+
a: string | number | Date | null,
|
|
45
|
+
b: string | number | Date | null,
|
|
46
|
+
) => {
|
|
47
|
+
if (a === null || b === null) {
|
|
48
|
+
if (a === null && b !== null) return 1;
|
|
49
|
+
if (a !== null && b === null) return -1;
|
|
50
|
+
return 0;
|
|
51
|
+
}
|
|
52
|
+
return sortAsc(new Date(a).getTime(), new Date(b).getTime());
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* 날짜순 내림차순 정렬
|
|
57
|
+
* @util
|
|
58
|
+
* @param {Date | number | string | null} a
|
|
59
|
+
* @param {Date | number | string | null} b
|
|
60
|
+
*/
|
|
61
|
+
export const sortDateDesc = (
|
|
62
|
+
a: string | number | Date | null,
|
|
63
|
+
b: string | number | Date | null,
|
|
64
|
+
) => {
|
|
65
|
+
if (a === null || b === null) {
|
|
66
|
+
if (a === null && b !== null) return 1;
|
|
67
|
+
if (a !== null && b === null) return -1;
|
|
68
|
+
return 0;
|
|
69
|
+
}
|
|
70
|
+
return sortDesc(new Date(a).getTime(), new Date(b).getTime());
|
|
71
|
+
};
|