@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.
@@ -0,0 +1,155 @@
1
+ /**
2
+ * validation; 인덱스 데이터 체크
3
+ * @util
4
+ * @validation
5
+ * @param {string | number} idxValue 인덱스 데이터
6
+ * @return {boolean}
7
+ */
8
+ export const isValidIdx = (idxValue?: number | string): boolean =>
9
+ typeof idxValue !== "undefined" &&
10
+ idxValue !== "" &&
11
+ !isNaN(Number(idxValue)) &&
12
+ Number(idxValue) > 0;
13
+
14
+ /**
15
+ * validation; 배열 데이터 체크
16
+ * @util
17
+ * @validation
18
+ * @param {unknown} array 배열 데이터
19
+ * @return {boolean}
20
+ */
21
+ export const isValidArray = (array?: unknown): boolean =>
22
+ typeof array !== "undefined" && Array.isArray(array);
23
+
24
+ /**
25
+ * validation; 사업자등록번호 데이터 체크
26
+ * @util
27
+ * @validation
28
+ * @param {unknown} businessCode 농장 사업자등록번호
29
+ * @return {boolean}
30
+ */
31
+ export const isValidBusinessCode = (businessCode: unknown): boolean => {
32
+ if (!businessCode) return false;
33
+ if (String(businessCode).length === 10) return !isNaN(Number(businessCode));
34
+ return false;
35
+ };
36
+
37
+ /**
38
+ * validation; 날짜 변환을 허용할 유효 number 시간값 체크
39
+ * @util
40
+ * @validation
41
+ * @param {number} dateNumber
42
+ * @return {boolean}
43
+ */
44
+ export const isValidDateNumber = (dateNumber: number): boolean => {
45
+ const DATE_MIN = 1672498800000; // 2023-01-01T00:00:00
46
+ const DATE_MAX = 4828172400000; // 2123-01-01T00:00:00
47
+ return DATE_MIN < dateNumber && dateNumber < DATE_MAX;
48
+ };
49
+
50
+ /**
51
+ * validation; 날짜 데이터 체크
52
+ * @util
53
+ * @validation
54
+ * @param {unknown} date 날짜
55
+ * @return {boolean}
56
+ */
57
+ export const isValidDateType = (date: unknown): boolean => {
58
+ if (!date) return false;
59
+ if (
60
+ typeof date === "string" ||
61
+ (typeof date === "number" && isValidDateNumber(date)) ||
62
+ date instanceof Date
63
+ )
64
+ return new Date(date) instanceof Date && !isNaN(new Date(date).valueOf());
65
+ return false;
66
+ };
67
+
68
+ /**
69
+ * validation; uuidx 계군 식별코드 데이터 체크
70
+ * @util
71
+ * @validation
72
+ * @param {string} uuidx 계군 식별코드
73
+ * @return {boolean}
74
+ */
75
+ export const isValidUuidx = (uuidx?: string): boolean =>
76
+ typeof uuidx !== "undefined" && uuidx !== "";
77
+
78
+ /**
79
+ * validation; 사육동 번호 데이터 체크
80
+ * @util
81
+ * @validation
82
+ * @param {string | number} buildingNo 사육동 번호
83
+ * @return {boolean}
84
+ */
85
+ export const isValidBuildingNo = (buildingNo?: string | number): boolean =>
86
+ typeof buildingNo !== "undefined" &&
87
+ buildingNo !== "" &&
88
+ !isNaN(Number(buildingNo)) &&
89
+ Number(buildingNo) > 0;
90
+
91
+ /**
92
+ * validation; building_no_s 사육동 번호 복수형 데이터 체크
93
+ * @util
94
+ * @validation
95
+ * @param {string} buildingNoList 사육동 번호 1,2,4,...
96
+ * @return {boolean}
97
+ */
98
+ export const isValidBuildingNoList = (buildingNoList?: string): boolean =>
99
+ typeof buildingNoList === "string" &&
100
+ buildingNoList !== "" &&
101
+ buildingNoList.split(",").length > 0;
102
+
103
+ /**
104
+ * validation; 숫자 데이터 체크
105
+ * @util
106
+ * @validation
107
+ * @param {unknown} value 숫자 확인 데이터
108
+ * @return {boolean}
109
+ */
110
+ export const isValidNumber = (value: unknown): boolean =>
111
+ typeof value !== "undefined" && !isNaN(Number(value)) && value !== "";
112
+
113
+ /**
114
+ * validation; 카테고리 이름이 업체인지 여부
115
+ * @util
116
+ * @validation
117
+ * @param {string} categoryName 카테고리 이름
118
+ * @return {boolean}
119
+ */
120
+ export const isValidFirmCategory = (categoryName?: string): boolean =>
121
+ typeof categoryName === "string" &&
122
+ /[농장|정부기관|유통사|수의사|병성기관|부화장|도계장|사료|약품|유니아이]/.test(
123
+ categoryName,
124
+ );
125
+
126
+ /**
127
+ * validation; 카테고리 이름이 약품/사료인지 여부
128
+ * @util
129
+ * @validation
130
+ * @param {string} categoryName 카테고리 이름
131
+ * @return {boolean}
132
+ */
133
+ export const isValidFeedMedicineCategory = (categoryName?: string): boolean =>
134
+ typeof categoryName === "string" && /[약품|사료]/.test(categoryName);
135
+
136
+ /**
137
+ * validation; 시간 데이터 HH:MM:SS 포맷 체크
138
+ * @util
139
+ * @validation
140
+ * @param {string} time
141
+ * @return {boolean}
142
+ */
143
+ export const isValidTimeStringFormat = (time: string): boolean => {
144
+ const hms = time.split(":");
145
+ if (hms.length !== 3 || hms.some(t => isNaN(Number(t)))) return false;
146
+
147
+ // hour
148
+ if (Number(hms[0]) < 0 || Number(hms[0]) > 24) return false;
149
+ // minutes
150
+ if (Number(hms[1]) < 0 || Number(hms[1]) > 59) return false;
151
+ // seconds
152
+ if (Number(hms[2]) < 0 || Number(hms[2]) > 59) return false;
153
+
154
+ return true;
155
+ };
package/src/index.tsx ADDED
@@ -0,0 +1,20 @@
1
+ export * from "./functions/reg-exp";
2
+ export * from "./functions/convert";
3
+ export * from "./functions/date";
4
+ export * from "./functions/format";
5
+ export * from "./functions/mask";
6
+ export * from "./functions/route";
7
+ export * from "./functions/sort";
8
+ export * from "./functions/validation";
9
+ export * from "./functions/api";
10
+ export * from "./functions/api.server";
11
+ export * from "./functions/log";
12
+ export * from "./functions/chart";
13
+ export * from "./functions/file";
14
+
15
+ export * from "./form/checkbox";
16
+
17
+ export * from "./react/convert";
18
+ export * from "./react/match";
19
+
20
+ export * from "./style/size";
@@ -0,0 +1,129 @@
1
+ import { Fragment } from "react";
2
+ import parse from "html-react-parser";
3
+
4
+ export const convertTextToHTML = (
5
+ text: string,
6
+ tagName: string,
7
+ key?: string,
8
+ ): React.ReactNode => {
9
+ if (!text.includes(`<${tagName}>`)) return text;
10
+ const HTML_KEY = key ? key : `util/convert/text-to-html/${tagName}`;
11
+ const parts = text.split(new RegExp(`(<${tagName}>|</${tagName}>)`, "g"));
12
+
13
+ let isTagContents = false;
14
+ return (
15
+ <>
16
+ {parts.map((part, index) => {
17
+ if (part === `<${tagName}>`) {
18
+ isTagContents = true;
19
+ return null;
20
+ }
21
+ if (part === `</${tagName}>`) {
22
+ isTagContents = false;
23
+ return null;
24
+ }
25
+
26
+ if (isTagContents)
27
+ return (
28
+ <span key={`${HTML_KEY}/${index}`} className={tagName}>
29
+ {part}
30
+ </span>
31
+ );
32
+ return <Fragment key={`${HTML_KEY}/${index}`}>{part}</Fragment>;
33
+ })}
34
+ </>
35
+ );
36
+ };
37
+
38
+ export const convertTextToLineArray = (text: string): string[] => {
39
+ if (text.includes("<br")) return text.split(/<br\s*\/?>|<br>/);
40
+ else if (text.includes("\n")) return text.split("\n");
41
+ return [text];
42
+ };
43
+
44
+ /**
45
+ * <br />이 포함된 텍스트를 jsx로 변환
46
+ * @util
47
+ * @param {object} params
48
+ * @param {string} params.text
49
+ * @param {string} [params.key] jsx 키
50
+ * @param {Function} [params.callback] 변환 콜백
51
+ * @return {string | React.ReactNode}
52
+ */
53
+ export const convertTextToJsx = ({
54
+ text,
55
+ key,
56
+ callback,
57
+ }: {
58
+ text: string;
59
+ } & Partial<{
60
+ key: string;
61
+ callback: (text: string) => string | React.ReactNode;
62
+ }>): string | React.ReactNode => {
63
+ // string 이외의 컨텐츠는 그대로 출력
64
+ if (typeof text !== "string") return text;
65
+ // console.log("[convertTextToJsx] text", { text, key, callback });
66
+
67
+ // HTML 태그(<p>, <br> 등)가 포함된 경우에는 우선적으로 HTML을 파싱하여
68
+ // 원래의 마크업 구조를 그대로 유지한다.
69
+ // html-react-parser 는 <br> 와 <br /> 모두 적절히 처리한다.
70
+ if (/<[a-z][\s\S]*>/i.test(text)) {
71
+ return <>{parse(text)}</>;
72
+ }
73
+
74
+ // <br> 또는 \n이 포함된 경우 기존 로직 사용
75
+ const lines = convertTextToLineArray(text);
76
+ // console.log("[convertTextToJsx] lines", lines);
77
+
78
+ if (lines.length > 1) {
79
+ const LINE_KEY = "util/convert/text-to-jsx";
80
+ return lines.map((line, index) => (
81
+ <Fragment key={key ? `${key}/${index}` : `${LINE_KEY}/${line}/${index}`}>
82
+ {index !== 0 && <br />}
83
+ {typeof callback === "function" ? callback(line) : line}
84
+ </Fragment>
85
+ ));
86
+ }
87
+
88
+ // 일반 텍스트 반환
89
+ return text;
90
+ };
91
+
92
+ /**
93
+ * 텍스트 배열을 <br />로 구분되는 jsx로 변환
94
+ * @util
95
+ * @param {React.ReactNode[]} text_array
96
+ * @param {object} [options]
97
+ * @param {string} [options.key] jsx 키
98
+ * @param {number} [options.phraseGap] 줄바꿈 간격
99
+ * @return {React.ReactNode}
100
+ */
101
+ export const convertArrayToJsx = (
102
+ text_array: React.ReactNode[],
103
+ options?: Partial<{ key: string; phraseGap: number }>,
104
+ ): React.ReactNode => {
105
+ if (typeof text_array === "string" || !Array.isArray(text_array))
106
+ return convertTextToJsx(text_array);
107
+
108
+ const LINE_KEY = "util/convert/array-to-jsx";
109
+ const commonKey = (
110
+ content: React.ReactNode,
111
+ index: number,
112
+ additionalKey?: string,
113
+ ) =>
114
+ options?.key
115
+ ? `${options.key}${additionalKey ? `/${additionalKey}` : ""}/${index}`
116
+ : `${LINE_KEY}/${String(content)}${additionalKey ? `/${additionalKey}` : ""}/${index}`;
117
+ return text_array.map((content, index) => (
118
+ <Fragment key={commonKey(content, index)}>
119
+ {index !== 0 && (
120
+ <>
121
+ {new Array(options?.phraseGap || 1).fill({}).map((_, i) => (
122
+ <br key={commonKey(content, i, "br")} />
123
+ ))}
124
+ </>
125
+ )}
126
+ {content}
127
+ </Fragment>
128
+ ));
129
+ };
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 일치 텍스트를 컴포넌트로 교체
3
+ * @util
4
+ * @param {string} origin
5
+ * @param {RegExp} exp
6
+ * @param {string} renderKey
7
+ * @return {ReactNode[] | string} 일치 키워드가 교체된 배열
8
+ * @see https://gist.github.com/edivados/0b24fd7e876d1940fd273361a31ffe1c
9
+ */
10
+ export function matchKeyword(
11
+ origin: string,
12
+ exp: RegExp,
13
+ renderKey: string,
14
+ ): React.ReactNode[] | string {
15
+ const matches = Array.from(origin.matchAll(exp));
16
+ if (!matches.length) return origin;
17
+
18
+ const jsxArray: React.ReactNode[] = [];
19
+ for (let i = 0; i < matches.length; i++) {
20
+ const match = matches[i];
21
+ const prevMatch = i ? matches[i - 1] : undefined;
22
+ const offset = prevMatch ? prevMatch.index! + prevMatch[0].length : 0;
23
+
24
+ jsxArray.push(origin.slice(offset, match.index));
25
+ jsxArray.push(
26
+ <i className="matched" key={`${renderKey}/${match}/${i}`}>
27
+ {match[0]}
28
+ </i>,
29
+ );
30
+
31
+ const isLastMatch = i === matches.length - 1;
32
+ const hasTextAfterLastMatch =
33
+ match.index! + match[0].length < origin.length;
34
+
35
+ if (isLastMatch && hasTextAfterLastMatch) {
36
+ jsxArray.push(origin.slice(match.index! + match[0].length));
37
+ }
38
+ }
39
+
40
+ return jsxArray.length ? jsxArray : origin;
41
+ }
@@ -0,0 +1,128 @@
1
+ import type { StyleSpacingArray, StyleSpacingType } from "@uniai/types";
2
+
3
+ /**
4
+ * 스타일 변수; rem 단위
5
+ * @util
6
+ */
7
+ export const styleRemAmount = 10;
8
+
9
+ /**
10
+ * 스타일 함수; px 단위를 rem 단위로 변환
11
+ * @util
12
+ * @param {number} v - px
13
+ */
14
+ export const styleRem = (v: number): string => `${v / styleRemAmount}rem`;
15
+
16
+ /**
17
+ * 스타일 함수; px 또는 string을 유효한 스타일 문자열로 변환
18
+ * @util
19
+ * @param {"px" | "rem"} unit - 단위 (px 또는 rem)
20
+ * @param {unknown} v - px 또는 string
21
+ * @param {number | string} [alt] - 대체 값 (px 또는 string)
22
+ * @returns {string} 유효한 스타일 문자열
23
+ */
24
+ export const styleBaseSize = (
25
+ unit: "px" | "rem",
26
+ v: unknown,
27
+ alt?: number | string,
28
+ ): string => {
29
+ // 유효하지 않은 값인 경우 "auto"
30
+ if (!["number", "string"].includes(typeof v) && typeof alt === "undefined")
31
+ return "auto";
32
+
33
+ if (typeof v === "number")
34
+ return unit === "rem" ? styleRem(v) : `${v}${unit}`;
35
+ // 유효한 스타일 문자인 경우
36
+ if (typeof v === "string" && v) return v;
37
+
38
+ // 대체 값이 있는 경우
39
+ if (typeof alt === "number")
40
+ return unit === "rem" ? styleRem(alt) : `${alt}${unit}`;
41
+ else if (typeof alt === "string" && alt) return alt;
42
+
43
+ return String(v);
44
+ };
45
+
46
+ /**
47
+ * 스타일 함수; padding, margin 배열을 스타일 문자열로 변환
48
+ * @util
49
+ * @param {"px" | "rem"} unit - 단위 (px 또는 rem)
50
+ * @param {FixedLengthArray<number | string, 2 | 3 | 4>} v - 스타일 배열
51
+ * @returns {string} 스타일 문자열
52
+ * @desc
53
+ * 파라미터 패턴
54
+ * - [상하좌우]: 일괄적용 처리
55
+ * - [상하, 좌우]: [top-bottom, left-right]
56
+ * - [상, 좌우, 하]: [top, left-right, bottom]
57
+ * - [상, 우, 하, 좌]: [top, right, bottom, left
58
+ */
59
+ export const styleSpacingArray = (
60
+ unit: "px" | "rem",
61
+ v: StyleSpacingArray,
62
+ ): string => v.map(space => styleBaseSize(unit, space)).join(" ");
63
+
64
+ /**
65
+ * 스타일 함수; padding, margin 옵션을 스타일 문자열로 변환
66
+ * @util
67
+ * @param {"px" | "rem"} unit - 단위 (px 또는 rem)
68
+ * @param {StyleSpacingType} spacing - 스타일 패딩 타입
69
+ * @param {StyleSpacingType} [alt] - 대체 값 (px 또는 string)
70
+ * @desc
71
+ * - number | string: 단일 값
72
+ * - [상하좌우]: 일괄적용 처리
73
+ * - [상하, 좌우]: [top-bottom, left-right]
74
+ * - [상, 좌우, 하]: [top, left-right, bottom]
75
+ * - [상, 우, 하, 좌]: [top, right, bottom, left
76
+ */
77
+ export const styleSpacingSize = (
78
+ unit: "px" | "rem",
79
+ spacing: StyleSpacingType | undefined,
80
+ alt?: StyleSpacingType,
81
+ ): string => {
82
+ if (typeof spacing === "undefined") {
83
+ if (typeof alt === "undefined") return "0";
84
+
85
+ // 대체 값이 있는 경우
86
+ if (Array.isArray(alt)) return styleSpacingArray(unit, alt);
87
+ else if (["number", "string"].includes(typeof alt) && alt !== "")
88
+ return styleBaseSize(unit, alt as number | string);
89
+ }
90
+ // 단일 값인 경우
91
+ if (["number", "string"].includes(typeof spacing) && spacing !== "")
92
+ return styleBaseSize(unit, spacing as number | string);
93
+
94
+ // 배열 지정하는 경우
95
+ if (Array.isArray(spacing)) return styleSpacingArray(unit, spacing);
96
+
97
+ return String(spacing);
98
+ };
99
+
100
+ /**
101
+ * 스타일 함수; padding 옵션을 스타일 문자열로 변환
102
+ * @util
103
+ * @param {"px" | "rem"} unit - 단위 (px 또는 rem)
104
+ * @param {StyleSpacingType} padding - 스타일 패딩 타입
105
+ * @param {StyleSpacingType} [alt] - 대체 값 (px 또는 string)
106
+ * @desc
107
+ * - number | string: 단일 값
108
+ * - [상하좌우]: 일괄적용 처리
109
+ * - [상하, 좌우]: [top-bottom, left-right]
110
+ * - [상, 좌우, 하]: [top, left-right, bottom]
111
+ * - [상, 우, 하, 좌]: [top, right, bottom, left]
112
+ */
113
+ export const stylePaddingSize = styleSpacingSize;
114
+
115
+ /**
116
+ * 스타일 함수; margin 옵션을 스타일 문자열로 변환
117
+ * @util
118
+ * @param {"px" | "rem"} unit - 단위 (px 또는 rem)
119
+ * @param {StyleSpacingType} margin - 스타일 마진 타입
120
+ * @param {StyleSpacingType} [alt] - 대체 값 (px 또는 string)
121
+ * @desc
122
+ * - number | string: 단일 값
123
+ * - [상하좌우]: 일괄적용 처리
124
+ * - [상하, 좌우]: [top-bottom, left-right]
125
+ * - [상, 좌우, 하]: [top, left-right, bottom]
126
+ * - [상, 우, 하, 좌]: [top, right, bottom, left]
127
+ */
128
+ export const styleMarginSize = styleSpacingSize;