@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,304 @@
|
|
|
1
|
+
import type { SVGAttributes } from "react";
|
|
2
|
+
import { extent, quantile } from "d3-array";
|
|
3
|
+
import type { GetChartYTicksProps, RechartsXAxisTickProps } from "@uniai/types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 차트 유틸리티; 도메인 범위 제한값
|
|
7
|
+
* @util
|
|
8
|
+
* @param {object} props
|
|
9
|
+
* @param {number | null} props.value - 값
|
|
10
|
+
* @param {number} props.gap - 갭
|
|
11
|
+
* @param {number} [props.min] - 최소값
|
|
12
|
+
* @param {number} [props.max] - 최대값
|
|
13
|
+
* @returns {number | string} - 도메인 제한값
|
|
14
|
+
*/
|
|
15
|
+
export const getDomainLimit = <ReturnType extends number | string>({
|
|
16
|
+
value,
|
|
17
|
+
gap,
|
|
18
|
+
min,
|
|
19
|
+
max,
|
|
20
|
+
}: {
|
|
21
|
+
value: unknown;
|
|
22
|
+
gap: number;
|
|
23
|
+
min?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
}): ReturnType => {
|
|
26
|
+
if (typeof value !== "number") return "auto" as ReturnType;
|
|
27
|
+
|
|
28
|
+
const amount = value + gap;
|
|
29
|
+
|
|
30
|
+
if (typeof min === "number")
|
|
31
|
+
return (amount < min ? min : amount) as ReturnType;
|
|
32
|
+
else if (typeof max === "number")
|
|
33
|
+
return (amount > max ? max : amount) as ReturnType;
|
|
34
|
+
|
|
35
|
+
return amount as ReturnType;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 차트 유틸리티; Y축 ticks 생성
|
|
40
|
+
* @util
|
|
41
|
+
* @param {object} props
|
|
42
|
+
* @param {unknown[]} props.data - 데이터
|
|
43
|
+
* @param {number} props.length - 길이
|
|
44
|
+
* @param {number} [props.pad] - 패딩 (기본값 0.5)
|
|
45
|
+
* @param {number} [props.limitMin] - 최소값 (기본값 0)
|
|
46
|
+
* @param {number} [props.limitMax] - 최대값
|
|
47
|
+
* @param {number} [props.digit] - 소수점 자리수
|
|
48
|
+
* @returns {number[] | undefined} - Y축 ticks
|
|
49
|
+
*/
|
|
50
|
+
export const getChartYTicks = ({
|
|
51
|
+
data,
|
|
52
|
+
length,
|
|
53
|
+
pad = 0.5,
|
|
54
|
+
limitMin = 0,
|
|
55
|
+
limitMax,
|
|
56
|
+
digit = 1,
|
|
57
|
+
outliers,
|
|
58
|
+
log,
|
|
59
|
+
}: GetChartYTicksProps): number[] | undefined => {
|
|
60
|
+
if (data.length === 0) return undefined;
|
|
61
|
+
if (log) console.log("[Y Ticks]-----------------------------------");
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// 1) 유효 숫자 배열(validArr) 확보
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
const validArr: number[] = data
|
|
67
|
+
.filter(v => typeof v !== "undefined" && v !== null && !isNaN(Number(v)))
|
|
68
|
+
.map(Number)
|
|
69
|
+
.filter(v => Number.isFinite(v));
|
|
70
|
+
|
|
71
|
+
if (validArr.length < 2) return undefined;
|
|
72
|
+
|
|
73
|
+
if (log) console.log("validArr", validArr);
|
|
74
|
+
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// 2) 이상치(outlier) 제거 – IQR 1.5배 기준
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
let filtered = validArr;
|
|
79
|
+
if (outliers && validArr.length > 4) {
|
|
80
|
+
const sorted = [...validArr].sort((a, b) => a - b);
|
|
81
|
+
const q1 = quantile(sorted, 0.25) as number;
|
|
82
|
+
const q3 = quantile(sorted, 0.75) as number;
|
|
83
|
+
const iqr = q3 - q1;
|
|
84
|
+
const low = q1 - 1.5 * iqr;
|
|
85
|
+
const high = q3 + 1.5 * iqr;
|
|
86
|
+
filtered = validArr.filter(v => v >= low && v <= high);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
// 3) 도메인(domainMin/domainMax) 계산 + 동적 pad 적용
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
let [min, max] = extent(filtered) as [number, number];
|
|
93
|
+
min = typeof limitMin === "number" && min < limitMin ? limitMin : min;
|
|
94
|
+
max = typeof limitMax === "number" && max > limitMax ? limitMax : max;
|
|
95
|
+
|
|
96
|
+
// --- 분포 기반 pad -------------------------------------------------
|
|
97
|
+
const sorted = [...filtered].sort((a, b) => a - b);
|
|
98
|
+
const q1 = quantile(sorted, 0.25)!;
|
|
99
|
+
const q3 = quantile(sorted, 0.75)!;
|
|
100
|
+
const iqr = q3 - q1; // IQR 폭
|
|
101
|
+
const ratio = iqr / (max - min || 1); // 분포 대비 폭
|
|
102
|
+
// pad 는 '최대 0.3, IQR 기반(0.4×ratio)' 중 더 작은 값 사용
|
|
103
|
+
// 1·5·0 규칙을 깨면 pad 를 0 으로 무시하도록 이후 로직에서 재귀 호출
|
|
104
|
+
const dynPad = Math.min(0.3, ratio * 0.4);
|
|
105
|
+
const padRatio = (max - min) * dynPad;
|
|
106
|
+
let domainMin = min - padRatio;
|
|
107
|
+
let domainMax = max + padRatio;
|
|
108
|
+
|
|
109
|
+
// 하드 클램프: ±20% 이상 확장 금지
|
|
110
|
+
const span0 = max - min;
|
|
111
|
+
domainMin = Math.max(domainMin, min - span0 * 0.2);
|
|
112
|
+
domainMax = Math.min(domainMax, max + span0 * 0.2);
|
|
113
|
+
// ------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
if (length < 1) return undefined;
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// 4) niceStep 선택 + tick 배열 작성
|
|
119
|
+
// - 후보: (1|5)×10ⁿ 스텝 중 length-1 에 가장 근접
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
const span = Math.abs(domainMax - domainMin);
|
|
122
|
+
const rawStep = span / Math.max(length - 1, 1);
|
|
123
|
+
|
|
124
|
+
// --- niceStep 선정 로직 개선 -------------------------------------------
|
|
125
|
+
// 후보 스텝: (1 | 5) × 10^(exp-1 ... exp+1) 총 6개
|
|
126
|
+
const exp = Math.floor(Math.log10(rawStep));
|
|
127
|
+
const factors = [1, 2, 2.5, 5] as const; // 1·2·2.5·5 계열 ⇒ tick 끝 0·1·2·5
|
|
128
|
+
const candidates: number[] = [];
|
|
129
|
+
|
|
130
|
+
// exp 가 비정상(NaN, ±Infinity) 이면 기본값 0 사용
|
|
131
|
+
const expSafe = Number.isFinite(exp) ? exp : 0;
|
|
132
|
+
for (let e = expSafe - 1; e <= expSafe + 1; e++) {
|
|
133
|
+
for (const f of factors) {
|
|
134
|
+
const val = f * Math.pow(10, e);
|
|
135
|
+
if (Number.isFinite(val) && val > 0) {
|
|
136
|
+
candidates.push(val);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (candidates.length === 0) return undefined;
|
|
142
|
+
|
|
143
|
+
// span / step ≈ length-1 인 후보를 우선 선택
|
|
144
|
+
candidates.sort(
|
|
145
|
+
(a, b) =>
|
|
146
|
+
Math.abs(span / a - (length - 1)) - Math.abs(span / b - (length - 1)),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
let niceStep = candidates[0];
|
|
150
|
+
// -----------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
// domain 을 niceStep 배수로 맞춘다
|
|
153
|
+
let niceMin = Math.floor(domainMin / niceStep) * niceStep;
|
|
154
|
+
let niceMax = Math.ceil(domainMax / niceStep) * niceStep;
|
|
155
|
+
|
|
156
|
+
const fixEdge = (v: number) => {
|
|
157
|
+
const last = Math.abs(v) % 10;
|
|
158
|
+
if (last === 1 || last === 5 || last === 0) return v;
|
|
159
|
+
return Math.round(v / niceStep) * niceStep;
|
|
160
|
+
};
|
|
161
|
+
niceMin = fixEdge(niceMin);
|
|
162
|
+
niceMax = fixEdge(niceMax);
|
|
163
|
+
|
|
164
|
+
// ---------------------------------------------------------------------------
|
|
165
|
+
// 추가 보정: 차트 데이터 최소값(min) == 도메인 하한(niceMin)일 때
|
|
166
|
+
// → 현재 도메인 간격(span)만큼 하한을 더 내린다
|
|
167
|
+
// (막대가 0px 로 그려지는 현상 방지)
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
if (min === niceMin) {
|
|
170
|
+
const digit = String(niceMin).split(".")[1]
|
|
171
|
+
? String(niceMin).split(".")[1].length
|
|
172
|
+
: 1;
|
|
173
|
+
const corr = Math.pow(10, digit);
|
|
174
|
+
niceMin -= Math.round(niceStep * corr) / corr; // 도메인 간격만큼 하한 확장
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// limitMin / limitMax 로 한번 더 clamp (배수 유지)
|
|
178
|
+
if (typeof limitMin === "number" && niceMin < limitMin) {
|
|
179
|
+
niceMin = Math.ceil(limitMin / niceStep) * niceStep;
|
|
180
|
+
}
|
|
181
|
+
if (typeof limitMax === "number" && niceMax > limitMax) {
|
|
182
|
+
niceMax = Math.floor(limitMax / niceStep) * niceStep;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// ticks 생성
|
|
186
|
+
let ticksArr: number[] = [];
|
|
187
|
+
for (let v = niceMin; v <= niceMax + 1e-8; v += niceStep) {
|
|
188
|
+
ticksArr.push(v);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- tick 개수 보정 루프 (과다/과소 시 step 증감) -----------------------------
|
|
192
|
+
// tick 수가 목표보다 많으면 step 을 두 배씩 늘려가며 재계산
|
|
193
|
+
while (ticksArr.length > length) {
|
|
194
|
+
niceStep *= 2;
|
|
195
|
+
ticksArr = [];
|
|
196
|
+
for (let v = niceMin; v <= niceMax + 1e-8; v += niceStep) {
|
|
197
|
+
ticksArr.push(v);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// tick 이 여전히 부족하면 step 을 반복적으로 절반으로 줄여보고,
|
|
202
|
+
// 그때마다 niceMax 를 다시 계산해 domain 을 데이터 상한에 가깝게 조정한다
|
|
203
|
+
while (ticksArr.length < length) {
|
|
204
|
+
niceStep /= 2;
|
|
205
|
+
if (niceStep < 1e-8) break; // 안전장치: 너무 작은 step 방지
|
|
206
|
+
|
|
207
|
+
// 상한을 최신 step 에 맞춰 다시 산출
|
|
208
|
+
niceMax = Math.ceil(max / niceStep) * niceStep;
|
|
209
|
+
|
|
210
|
+
// 새로운 tick 배열 생성
|
|
211
|
+
ticksArr = [];
|
|
212
|
+
for (let v = niceMin; v <= niceMax + 1e-8; v += niceStep) {
|
|
213
|
+
ticksArr.push(v);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 과도하게 많아지면 루프 종료
|
|
217
|
+
if (ticksArr.length > length * 2) break;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ---------------------------------------------------------------------------
|
|
221
|
+
// 5) 자리수 반올림 및 중복 제거
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// 자리수(digit) 반올림
|
|
224
|
+
if (typeof digit === "number") {
|
|
225
|
+
const pow = Math.pow(10, digit);
|
|
226
|
+
ticksArr = ticksArr.map(v => Math.round(v * pow) / pow);
|
|
227
|
+
// 동일 값 중복 제거
|
|
228
|
+
ticksArr = [...new Set(ticksArr)];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// 1·5·0 검증: 실패하면 pad=0 으로 한번 더 시도
|
|
232
|
+
const bad = ticksArr.some(t => {
|
|
233
|
+
const d = Math.abs(t) % 10;
|
|
234
|
+
return d !== 1 && d !== 5 && d !== 0;
|
|
235
|
+
});
|
|
236
|
+
// 규칙 검증 실패 시 pad=0 으로 한 번 더 계산
|
|
237
|
+
if (bad && pad !== 0) {
|
|
238
|
+
return getChartYTicks({
|
|
239
|
+
data,
|
|
240
|
+
length,
|
|
241
|
+
pad: 0,
|
|
242
|
+
limitMin,
|
|
243
|
+
limitMax,
|
|
244
|
+
digit,
|
|
245
|
+
outliers,
|
|
246
|
+
log,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return ticksArr;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* 차트 유틸리티; 소숫점 자리수 추출
|
|
255
|
+
* @util
|
|
256
|
+
* @param {unknown} v - 값
|
|
257
|
+
* @returns {number} - 소숫점 자리수
|
|
258
|
+
*/
|
|
259
|
+
export const getDecimalIndex = (v: unknown): number =>
|
|
260
|
+
!isNaN(Number(v)) && String(v).includes(".")
|
|
261
|
+
? String(v)
|
|
262
|
+
.split(".")[1]
|
|
263
|
+
.split("")
|
|
264
|
+
.findIndex(d => Number(d) !== 0) + 1
|
|
265
|
+
: 0;
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* 차트 유틸리티; X축 텍스트 속성
|
|
269
|
+
* @util
|
|
270
|
+
* @param {RechartsXAxisTickProps} props - X축 속성
|
|
271
|
+
* @returns {SVGAttributes<SVGTextElement>} - X축 텍스트 속성
|
|
272
|
+
*/
|
|
273
|
+
export const getXAxisTextAttr = (
|
|
274
|
+
props: RechartsXAxisTickProps,
|
|
275
|
+
): SVGAttributes<SVGTextElement> => {
|
|
276
|
+
const {
|
|
277
|
+
width,
|
|
278
|
+
height,
|
|
279
|
+
x,
|
|
280
|
+
y,
|
|
281
|
+
stroke,
|
|
282
|
+
fill,
|
|
283
|
+
orientation,
|
|
284
|
+
textAnchor,
|
|
285
|
+
transform,
|
|
286
|
+
// verticalAnchor,
|
|
287
|
+
// 추가 기본 정보; index, visibleTicksCount,
|
|
288
|
+
// index,
|
|
289
|
+
// payload - 각 x의 y 데이터; coordinate, value, index, offset, tickCoord, isShow
|
|
290
|
+
} = props;
|
|
291
|
+
|
|
292
|
+
// <text> 기본 속성
|
|
293
|
+
return {
|
|
294
|
+
width,
|
|
295
|
+
height,
|
|
296
|
+
x,
|
|
297
|
+
y,
|
|
298
|
+
stroke,
|
|
299
|
+
fill,
|
|
300
|
+
textAnchor,
|
|
301
|
+
orientation,
|
|
302
|
+
transform,
|
|
303
|
+
};
|
|
304
|
+
};
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { isValidDateType } from "./validation";
|
|
2
|
+
import { dateFormat } from "./format";
|
|
3
|
+
import { consoleLog } from "./log";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 데이터를 스트링으로 변환
|
|
7
|
+
* @util
|
|
8
|
+
* @param {unknown} value
|
|
9
|
+
* @return {string}
|
|
10
|
+
*/
|
|
11
|
+
export const string = (value: unknown): string => {
|
|
12
|
+
if (typeof value === "string") return value;
|
|
13
|
+
return JSON.stringify(value);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 세션/로컬 스토리지 데이터 parsing
|
|
18
|
+
* @util
|
|
19
|
+
* @param {string} data 세션/로컬 스토리지 데이터
|
|
20
|
+
* @return {unknown}
|
|
21
|
+
*/
|
|
22
|
+
export const parseData = (data: string): unknown => {
|
|
23
|
+
// 괄호 여부 -> object | array
|
|
24
|
+
if (/^(\{|\[)/.test(data)) return JSON.parse(data);
|
|
25
|
+
// 숫자 여부 -> number
|
|
26
|
+
else if (!isNaN(Number(data))) return Number(data);
|
|
27
|
+
// 불리언 여부 -> boolean
|
|
28
|
+
else if (data === "true" || data === "false") return JSON.parse(data);
|
|
29
|
+
// undefined
|
|
30
|
+
else if (data === "undefined") return undefined;
|
|
31
|
+
|
|
32
|
+
return data; // string
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 키체인에 해당하는 데이터 추출
|
|
37
|
+
* @util
|
|
38
|
+
* @param {unknown} data 세션/로컬 스토리지 데이터
|
|
39
|
+
* @param {string} keyChain 키체인
|
|
40
|
+
* @return {unknown}
|
|
41
|
+
*/
|
|
42
|
+
export const getValueByKeyChain = (
|
|
43
|
+
data: unknown,
|
|
44
|
+
keyChain: string,
|
|
45
|
+
): unknown => {
|
|
46
|
+
// 객체 여부 판단
|
|
47
|
+
const isObject = (targetData: unknown): boolean =>
|
|
48
|
+
typeof targetData === "object" &&
|
|
49
|
+
!Array.isArray(targetData) &&
|
|
50
|
+
targetData !== null &&
|
|
51
|
+
typeof targetData !== "function"
|
|
52
|
+
? true
|
|
53
|
+
: false;
|
|
54
|
+
|
|
55
|
+
// 프로퍼티를 가진 객체인지 확인
|
|
56
|
+
if (isObject(data)) {
|
|
57
|
+
// 키 체인이 남았는지 확인
|
|
58
|
+
const firstChainIndex: number = keyChain.indexOf(".");
|
|
59
|
+
if (firstChainIndex === -1)
|
|
60
|
+
return (data as Record<string, unknown>)[keyChain];
|
|
61
|
+
|
|
62
|
+
// 키 체인이 끝나지 않은 경우
|
|
63
|
+
const currentKey: string = keyChain.slice(0, firstChainIndex);
|
|
64
|
+
const remainKeyChain: string = keyChain.slice(firstChainIndex + 1);
|
|
65
|
+
const foundData = (data as Record<string, unknown>)[currentKey];
|
|
66
|
+
return isObject(foundData)
|
|
67
|
+
? getValueByKeyChain(foundData, remainKeyChain)
|
|
68
|
+
: undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return data;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* object -> URLSearchParams 데이터 변환
|
|
76
|
+
* @param {unknown} data
|
|
77
|
+
* @return {URLSearchParams | string}
|
|
78
|
+
*/
|
|
79
|
+
export const convertObjectToSearchParams = (data: unknown): URLSearchParams => {
|
|
80
|
+
if (!data) {
|
|
81
|
+
consoleLog("[convertObjectToSearchParams] 데이터가 유효하지 않음", {
|
|
82
|
+
data,
|
|
83
|
+
});
|
|
84
|
+
return new URLSearchParams();
|
|
85
|
+
}
|
|
86
|
+
// searchParams 여부 체크
|
|
87
|
+
const isSearchParams = data instanceof URLSearchParams;
|
|
88
|
+
if (isSearchParams) {
|
|
89
|
+
// consoleLog(
|
|
90
|
+
// "[convertObjectToSearchParams] 이미 URLSearchParams 상태로 확인됨",
|
|
91
|
+
// {
|
|
92
|
+
// data,
|
|
93
|
+
// },
|
|
94
|
+
// );
|
|
95
|
+
return data;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 객체 여부 판단
|
|
99
|
+
const isObject =
|
|
100
|
+
typeof data === "object" &&
|
|
101
|
+
!Array.isArray(data) &&
|
|
102
|
+
Object.keys(data).length > 0;
|
|
103
|
+
|
|
104
|
+
if (isObject) {
|
|
105
|
+
const d = Object.fromEntries(
|
|
106
|
+
Object.entries(data as Record<string, unknown>).map(([key, value]) => [
|
|
107
|
+
key,
|
|
108
|
+
string(value),
|
|
109
|
+
]),
|
|
110
|
+
);
|
|
111
|
+
return new URLSearchParams(d);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
consoleLog("[convertObjectToSearchParams] 데이터가 객체가 아님", {
|
|
115
|
+
data,
|
|
116
|
+
type: typeof data,
|
|
117
|
+
key: Object.keys(data),
|
|
118
|
+
array: Array.isArray(data),
|
|
119
|
+
});
|
|
120
|
+
return new URLSearchParams();
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// /**
|
|
124
|
+
// * 배열 추가형 입력 테이블 register rule 필터링
|
|
125
|
+
// * @util
|
|
126
|
+
// * @param {RegisterOptionsType} ruleCollection
|
|
127
|
+
// * @param {boolean} isActive
|
|
128
|
+
// * @return {RegisterOptionsType}
|
|
129
|
+
// */
|
|
130
|
+
// export function getFieldArrayValidOptions(
|
|
131
|
+
// ruleCollection: RegisterOptionsType,
|
|
132
|
+
// isActive: boolean,
|
|
133
|
+
// ): RegisterOptionsType {
|
|
134
|
+
// const entries = Object.entries(ruleCollection);
|
|
135
|
+
// const filtered = entries.map(([dataName, rule]) => {
|
|
136
|
+
// const ruleEntries = Object.entries(rule).filter(
|
|
137
|
+
// ([ruleKey]) => ruleKey !== "required",
|
|
138
|
+
// );
|
|
139
|
+
// return [dataName, Object.fromEntries(ruleEntries)];
|
|
140
|
+
// });
|
|
141
|
+
|
|
142
|
+
// return isActive ? ruleCollection : Object.fromEntries(filtered);
|
|
143
|
+
// }
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 텍스트 capitalize 첫글자 대문자 변환
|
|
147
|
+
* @util
|
|
148
|
+
* @param {string} value 텍스트
|
|
149
|
+
* @return {string}
|
|
150
|
+
*/
|
|
151
|
+
export const capitalize = (value: string): string => {
|
|
152
|
+
const first = value.charAt(0).toUpperCase();
|
|
153
|
+
const others = value.slice(1);
|
|
154
|
+
return `${first}${others}`;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* kg -> g 변환
|
|
159
|
+
* @util
|
|
160
|
+
* @param {number | string} kg
|
|
161
|
+
* @return {number} gram
|
|
162
|
+
*/
|
|
163
|
+
export const covertGram = (kg: number | string): number => Number(kg) * 1000;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* g -> kg 변환
|
|
167
|
+
* @util
|
|
168
|
+
* @param {number | string} gram
|
|
169
|
+
* @return {number} kg
|
|
170
|
+
*/
|
|
171
|
+
export const covertKilogram = (gram: number | string): number =>
|
|
172
|
+
Number(gram) / 1000;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* ml -> L 변환
|
|
176
|
+
* @util
|
|
177
|
+
* @param {number | string} ml
|
|
178
|
+
* @return {number} liter
|
|
179
|
+
*/
|
|
180
|
+
export const convertLiter = (ml: number | string): number => Number(ml) / 1000;
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* L -> ml 변환
|
|
184
|
+
* @util
|
|
185
|
+
* @param {number | string} liter
|
|
186
|
+
* @return {number} ml
|
|
187
|
+
*/
|
|
188
|
+
export const convertMilliliter = (liter: number | string): number =>
|
|
189
|
+
Number(liter) * 1000;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 데이터 가공
|
|
193
|
+
* @util
|
|
194
|
+
* @param {unknown} value
|
|
195
|
+
* @return {unknown}
|
|
196
|
+
* @desc
|
|
197
|
+
* - 빈칸 또는 줄바꿈 텍스트 모두 제거
|
|
198
|
+
* - 날짜 데이터 yyyy-mm-ddTHH:MM:SS 변환
|
|
199
|
+
*/
|
|
200
|
+
export function extractCoreValue<TOriginType>(value: TOriginType): TOriginType {
|
|
201
|
+
if (isValidDateType(value)) return dateFormat(value) as TOriginType;
|
|
202
|
+
if (typeof value === "string")
|
|
203
|
+
return value.replace(/[\s\n\t]+/g, "") as TOriginType;
|
|
204
|
+
return value;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* 자연수로 된 숫자를, 지정된 자리수만큼 0을 채워서 반환
|
|
209
|
+
* @util
|
|
210
|
+
* @param {number} v 소수점 제외
|
|
211
|
+
* @param {number} [digitLength] 자릿수 (기본값 2)
|
|
212
|
+
* @return {string}
|
|
213
|
+
*/
|
|
214
|
+
export function convertDigit(v: number, digitLength: number = 2): string {
|
|
215
|
+
const valueLength = String(Math.floor(v)).length;
|
|
216
|
+
const restLength = digitLength - valueLength;
|
|
217
|
+
const fill = Array(restLength).fill(0).join("");
|
|
218
|
+
|
|
219
|
+
return `${fill}${v}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* 날짜 시간을 2-digit으로 맞춤
|
|
224
|
+
* @util
|
|
225
|
+
* @param {number} date_time
|
|
226
|
+
* @return {string}
|
|
227
|
+
*/
|
|
228
|
+
export const convert2Digit = (date_time: number): string =>
|
|
229
|
+
date_time < 10 ? `0${date_time}` : String(date_time);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// import CryptoJS from "crypto-js";
|
|
2
|
+
// import { parseData } from "./convert";
|
|
3
|
+
|
|
4
|
+
// /**
|
|
5
|
+
// * CryptoJS; 암호화
|
|
6
|
+
// * @util
|
|
7
|
+
// * @param {unknown} value 암호화할 데이터
|
|
8
|
+
// * @return {string | Error} 암호화 데이터 | 키 정보 없음
|
|
9
|
+
// */
|
|
10
|
+
// export const encrypt = (value: unknown): string | Error => {
|
|
11
|
+
// const kluchi = process.env.NEXT_PUBLIC_KLUCHI ?? undefined;
|
|
12
|
+
// if (!kluchi) return new Error("환경변수를 확인하세요.");
|
|
13
|
+
|
|
14
|
+
// // 데이터 stringify
|
|
15
|
+
// const stringified =
|
|
16
|
+
// typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
17
|
+
|
|
18
|
+
// // 데이터 암호화
|
|
19
|
+
// return CryptoJS.AES.encrypt(stringified, kluchi).toString();
|
|
20
|
+
// };
|
|
21
|
+
|
|
22
|
+
// /**
|
|
23
|
+
// * CryptoJS; 복호화
|
|
24
|
+
// * @util
|
|
25
|
+
// * @param {string} value 암호화된 데이터
|
|
26
|
+
// * @return {unknown} 복호화 데이터
|
|
27
|
+
// */
|
|
28
|
+
// export function decrypt<ExpectType>(value: string): ExpectType | Error {
|
|
29
|
+
// const kluchi = process.env.NEXT_PUBLIC_KLUCHI ?? undefined;
|
|
30
|
+
// if (!kluchi) return new Error("환경변수를 확인하세요.");
|
|
31
|
+
// if (typeof value !== "string") return new Error("데이터를 찾을 수 없습니다.");
|
|
32
|
+
|
|
33
|
+
// try {
|
|
34
|
+
// // 데이터 복호화
|
|
35
|
+
// const decrypted = CryptoJS.AES.decrypt(value, kluchi).toString(
|
|
36
|
+
// CryptoJS.enc.Utf8,
|
|
37
|
+
// );
|
|
38
|
+
|
|
39
|
+
// // 데이터 파싱을 통해 복원
|
|
40
|
+
// return parseData(decrypted) as ExpectType;
|
|
41
|
+
// } catch (error) {
|
|
42
|
+
// throw new Error(`데이터 복호화에 실패하였습니다. (${error})`);
|
|
43
|
+
// }
|
|
44
|
+
// }
|