@basiln/utils 0.1.1 → 0.1.3
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/CHANGELOG.md +120 -0
- package/dist/Choose.js +50 -0
- package/dist/Choose.js.map +1 -0
- package/dist/Choose.mjs +7 -0
- package/dist/Choose.mjs.map +1 -0
- package/dist/Flex.js +70 -0
- package/dist/Flex.js.map +1 -0
- package/dist/Flex.mjs +10 -0
- package/dist/Flex.mjs.map +1 -0
- package/dist/If.js +35 -0
- package/dist/If.js.map +1 -0
- package/dist/If.mjs +7 -0
- package/dist/If.mjs.map +1 -0
- package/dist/SafeArea.js +91 -0
- package/dist/SafeArea.js.map +1 -0
- package/dist/SafeArea.mjs +10 -0
- package/dist/SafeArea.mjs.map +1 -0
- package/dist/Spacing.js +49 -0
- package/dist/Spacing.js.map +1 -0
- package/dist/Spacing.mjs +8 -0
- package/dist/Spacing.mjs.map +1 -0
- package/dist/Validate.js +43 -0
- package/dist/Validate.js.map +1 -0
- package/dist/Validate.mjs +7 -0
- package/dist/Validate.mjs.map +1 -0
- package/dist/chunk-7WNTKDNW.mjs +26 -0
- package/dist/chunk-7WNTKDNW.mjs.map +1 -0
- package/dist/chunk-C7VOPXT2.mjs +23 -0
- package/dist/chunk-C7VOPXT2.mjs.map +1 -0
- package/dist/chunk-DD3KGUQX.mjs +22 -0
- package/dist/chunk-DD3KGUQX.mjs.map +1 -0
- package/dist/chunk-FUGA35PJ.mjs +22 -0
- package/dist/chunk-FUGA35PJ.mjs.map +1 -0
- package/dist/chunk-GQPOYY4X.mjs +77 -0
- package/dist/chunk-GQPOYY4X.mjs.map +1 -0
- package/dist/chunk-HLTXXENG.mjs +39 -0
- package/dist/chunk-HLTXXENG.mjs.map +1 -0
- package/dist/chunk-OIJ4AVT7.mjs +37 -0
- package/dist/chunk-OIJ4AVT7.mjs.map +1 -0
- package/dist/chunk-Q3B6WSD7.mjs +42 -0
- package/dist/chunk-Q3B6WSD7.mjs.map +1 -0
- package/dist/chunk-QD5QDTUG.mjs +14 -0
- package/dist/chunk-QD5QDTUG.mjs.map +1 -0
- package/dist/chunk-R23KSR7N.mjs +19 -0
- package/dist/chunk-R23KSR7N.mjs.map +1 -0
- package/dist/chunk-SJJHTYZC.mjs +30 -0
- package/dist/chunk-SJJHTYZC.mjs.map +1 -0
- package/dist/chunk-T7K7QYTL.mjs +9 -0
- package/dist/chunk-T7K7QYTL.mjs.map +1 -0
- package/dist/chunk-U4T3KW7L.mjs +67 -0
- package/dist/chunk-U4T3KW7L.mjs.map +1 -0
- package/dist/chunk-UFRXNT2I.mjs +11 -0
- package/dist/chunk-UFRXNT2I.mjs.map +1 -0
- package/dist/chunk-WHYNBO2G.mjs +12 -0
- package/dist/chunk-WHYNBO2G.mjs.map +1 -0
- package/dist/chunk-XBGZ3YNL.mjs +20 -0
- package/dist/chunk-XBGZ3YNL.mjs.map +1 -0
- package/dist/chunk-Z4QPISK7.mjs +36 -0
- package/dist/chunk-Z4QPISK7.mjs.map +1 -0
- package/dist/coerceCssPixelValue.js +33 -0
- package/dist/coerceCssPixelValue.js.map +1 -0
- package/dist/coerceCssPixelValue.mjs +7 -0
- package/dist/coerceCssPixelValue.mjs.map +1 -0
- package/dist/composeEventHandlers.js +38 -0
- package/dist/composeEventHandlers.js.map +1 -0
- package/dist/composeEventHandlers.mjs +7 -0
- package/dist/composeEventHandlers.mjs.map +1 -0
- package/dist/constants/josa.js +105 -0
- package/dist/constants/josa.js.map +1 -0
- package/dist/constants/josa.mjs +15 -0
- package/dist/constants/josa.mjs.map +1 -0
- package/dist/createContext.js +56 -0
- package/dist/createContext.js.map +1 -0
- package/dist/createContext.mjs +7 -0
- package/dist/createContext.mjs.map +1 -0
- package/dist/ellipsis.js +45 -0
- package/dist/ellipsis.js.map +1 -0
- package/dist/ellipsis.mjs +9 -0
- package/dist/ellipsis.mjs.map +1 -0
- package/dist/getVar.js +36 -0
- package/dist/getVar.js.map +1 -0
- package/dist/getVar.mjs +7 -0
- package/dist/getVar.mjs.map +1 -0
- package/dist/hasBatchim.js +111 -0
- package/dist/hasBatchim.js.map +1 -0
- package/dist/hasBatchim.mjs +8 -0
- package/dist/hasBatchim.mjs.map +1 -0
- package/dist/hexToRgba.js +61 -0
- package/dist/hexToRgba.js.map +1 -0
- package/dist/hexToRgba.mjs +7 -0
- package/dist/hexToRgba.mjs.map +1 -0
- package/dist/index.d.ts +174 -0
- package/dist/index.js +456 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +64 -0
- package/dist/index.mjs.map +1 -0
- package/dist/josa.js +133 -0
- package/dist/josa.js.map +1 -0
- package/dist/josa.mjs +9 -0
- package/dist/josa.mjs.map +1 -0
- package/dist/queryString.js +91 -0
- package/dist/queryString.js.map +1 -0
- package/dist/queryString.mjs +7 -0
- package/dist/queryString.mjs.map +1 -0
- package/dist/useSafeArea.js +49 -0
- package/dist/useSafeArea.js.map +1 -0
- package/dist/useSafeArea.mjs +8 -0
- package/dist/useSafeArea.mjs.map +1 -0
- package/package.json +5 -2
- package/src/Flex.tsx +51 -0
- package/src/SafeArea.tsx +46 -0
- package/src/Spacing.tsx +30 -0
- package/src/Validate.ts +25 -0
- package/src/coerceCssPixelValue.ts +5 -0
- package/src/constants/josa.ts +72 -0
- package/src/ellipsis.ts +19 -0
- package/src/hasBatchim.ts +31 -0
- package/src/hexToRgba.ts +45 -0
- package/src/index.ts +25 -0
- package/src/josa.ts +50 -0
- package/src/queryString.ts +134 -0
- package/src/useSafeArea.ts +24 -0
package/src/ellipsis.ts
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
import { css } from 'styled-components';
|
2
|
+
|
3
|
+
export type MultiLineEllipsisProps = {
|
4
|
+
line: number;
|
5
|
+
};
|
6
|
+
|
7
|
+
export const ellipsis = css`
|
8
|
+
overflow: hidden;
|
9
|
+
white-space: nowrap;
|
10
|
+
text-overflow: ellipsis;
|
11
|
+
`;
|
12
|
+
|
13
|
+
export const multiLineEllipsis = ({ line }: MultiLineEllipsisProps) => css`
|
14
|
+
display: -webkit-box;
|
15
|
+
overflow: hidden;
|
16
|
+
text-overflow: ellipsis;
|
17
|
+
-webkit-line-clamp: ${line};
|
18
|
+
-webkit-box-orient: vertical;
|
19
|
+
`;
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import {
|
2
|
+
COMPLETE_HANGUL_END_CHARCODE,
|
3
|
+
COMPLETE_HANGUL_START_CHARCODE,
|
4
|
+
NUMBER_OF_JONGSEONG,
|
5
|
+
} from './constants/josa';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* 한글 음절의 받침 유무를 확인합니다.
|
9
|
+
* @param str - 검사할 문자열
|
10
|
+
* @returns 받침이 있으면 true, 없으면 false
|
11
|
+
*/
|
12
|
+
export function hasBatchim(str: string): boolean {
|
13
|
+
const lastChar = str[str.length - 1];
|
14
|
+
if (!lastChar) return false;
|
15
|
+
|
16
|
+
const charCode = lastChar.charCodeAt(0);
|
17
|
+
|
18
|
+
// 한글 완성형 문자인지 확인
|
19
|
+
if (
|
20
|
+
charCode < COMPLETE_HANGUL_START_CHARCODE ||
|
21
|
+
charCode > COMPLETE_HANGUL_END_CHARCODE
|
22
|
+
) {
|
23
|
+
return false;
|
24
|
+
}
|
25
|
+
|
26
|
+
// 받침 코드 계산
|
27
|
+
const batchimCode =
|
28
|
+
(charCode - COMPLETE_HANGUL_START_CHARCODE) % NUMBER_OF_JONGSEONG;
|
29
|
+
|
30
|
+
return batchimCode > 0; // 받침이 있으면 true
|
31
|
+
}
|
package/src/hexToRgba.ts
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
export type HexToRgbaProps = { hex: string; alpha?: number };
|
2
|
+
|
3
|
+
function parseHexToDecimal(hex: string): number {
|
4
|
+
return parseInt(hex, 16);
|
5
|
+
}
|
6
|
+
|
7
|
+
function validateHex(hex: string): string {
|
8
|
+
const match = /^#?([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.exec(hex);
|
9
|
+
if (!match) {
|
10
|
+
throw new Error(
|
11
|
+
`Invalid hex value: "${hex}". Expected formats: "#RGB", "RGB", "#RRGGBB", or "RRGGBB".`
|
12
|
+
);
|
13
|
+
}
|
14
|
+
return match[1];
|
15
|
+
}
|
16
|
+
|
17
|
+
function expandHex(hex: string): string {
|
18
|
+
return hex.length === 3
|
19
|
+
? hex
|
20
|
+
.split('')
|
21
|
+
.map((char) => char + char)
|
22
|
+
.join('')
|
23
|
+
: hex;
|
24
|
+
}
|
25
|
+
|
26
|
+
function isValidAlpha(alpha: number): boolean {
|
27
|
+
return alpha >= 0 && alpha <= 1;
|
28
|
+
}
|
29
|
+
|
30
|
+
export function hexToRgba({ hex, alpha = 1 }: HexToRgbaProps): string {
|
31
|
+
if (!isValidAlpha(alpha)) {
|
32
|
+
throw new Error(
|
33
|
+
`Invalid alpha value. Must be between 0 and 1, received: ${alpha}`
|
34
|
+
);
|
35
|
+
}
|
36
|
+
|
37
|
+
const validatedHex = validateHex(hex);
|
38
|
+
const expandedHex = expandHex(validatedHex);
|
39
|
+
|
40
|
+
const r = parseHexToDecimal(expandedHex.slice(0, 2));
|
41
|
+
const g = parseHexToDecimal(expandedHex.slice(2, 4));
|
42
|
+
const b = parseHexToDecimal(expandedHex.slice(4, 6));
|
43
|
+
|
44
|
+
return `rgba(${r},${g},${b},${alpha})`;
|
45
|
+
}
|
package/src/index.ts
CHANGED
@@ -6,6 +6,31 @@ export {
|
|
6
6
|
} from './Choose';
|
7
7
|
|
8
8
|
export { composeEventHandlers } from './composeEventHandlers';
|
9
|
+
|
9
10
|
export { createContext } from './createContext';
|
11
|
+
|
10
12
|
export { getVar, type VariableType } from './getVar';
|
13
|
+
|
11
14
|
export { If, type IfProps } from './If';
|
15
|
+
|
16
|
+
export { Flex, type FlexProps } from './Flex';
|
17
|
+
|
18
|
+
export {
|
19
|
+
ellipsis,
|
20
|
+
multiLineEllipsis,
|
21
|
+
type MultiLineEllipsisProps,
|
22
|
+
} from './ellipsis';
|
23
|
+
|
24
|
+
export { hexToRgba, type HexToRgbaProps } from './hexToRgba';
|
25
|
+
|
26
|
+
export { josa, type JosaProps } from './josa';
|
27
|
+
|
28
|
+
export { Validate } from './Validate';
|
29
|
+
|
30
|
+
export { Spacing, type SpacingProps } from './Spacing';
|
31
|
+
|
32
|
+
export { useSafeArea, type SafeAreaCssValueProps } from './useSafeArea';
|
33
|
+
|
34
|
+
export { SafeArea, type SafeAreaProps } from './SafeArea';
|
35
|
+
|
36
|
+
export { queryString } from './queryString';
|
package/src/josa.ts
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
import { hasBatchim } from './hasBatchim';
|
2
|
+
|
3
|
+
type JosaOption = '이/가' | '을/를' | '은/는';
|
4
|
+
|
5
|
+
type ExtractJosaOption<T> = T extends `${infer A}/${infer B}` ? A | B : never;
|
6
|
+
|
7
|
+
export type JosaProps<U extends JosaOption> = {
|
8
|
+
word: string;
|
9
|
+
josa: U;
|
10
|
+
};
|
11
|
+
|
12
|
+
/**
|
13
|
+
* 주어진 단어와 조사 옵션을 기반으로 올바른 조사를 반환합니다.
|
14
|
+
* @returns 단어와 올바른 조사가 결합된 문자열
|
15
|
+
*/
|
16
|
+
export function josa<U extends JosaOption>(
|
17
|
+
props: JosaProps<U>
|
18
|
+
): `${JosaProps<U>['word']}${ExtractJosaOption<U>}` {
|
19
|
+
const { word, josa } = props;
|
20
|
+
|
21
|
+
if (!word) {
|
22
|
+
return word as `${JosaProps<U>['word']}${ExtractJosaOption<U>}`;
|
23
|
+
}
|
24
|
+
|
25
|
+
return `${word}${josaPicker({
|
26
|
+
word,
|
27
|
+
josa,
|
28
|
+
})}` as `${JosaProps<U>['word']}${ExtractJosaOption<U>}`;
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* 주어진 단어와 조사 옵션을 기반으로 올바른 조사를 선택합니다.
|
33
|
+
* @returns 올바른 조사
|
34
|
+
*/
|
35
|
+
function josaPicker<U extends JosaOption>(
|
36
|
+
props: JosaProps<U>
|
37
|
+
): ExtractJosaOption<U> {
|
38
|
+
const { word, josa } = props;
|
39
|
+
|
40
|
+
if (!word) {
|
41
|
+
return josa.split('/')[0] as ExtractJosaOption<U>;
|
42
|
+
}
|
43
|
+
|
44
|
+
const has받침 = hasBatchim(word);
|
45
|
+
const index = has받침 ? 0 : 1;
|
46
|
+
|
47
|
+
return josa.split('/')[index] as ExtractJosaOption<U>;
|
48
|
+
}
|
49
|
+
|
50
|
+
josa.pick = josaPicker;
|
@@ -0,0 +1,134 @@
|
|
1
|
+
/**
|
2
|
+
* @description 쿼리 스트링을 생성합니다.
|
3
|
+
* @example createSearchParamString({ a: 1, b: 2, c: 3 }) // 'a=1&b=2&c=3'
|
4
|
+
*/
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
6
|
+
function createSearchParamString(params: Record<string, any>) {
|
7
|
+
return (
|
8
|
+
new URLSearchParams(
|
9
|
+
Object.entries(params)
|
10
|
+
.filter(([, value]) => value != null)
|
11
|
+
.map(([key, value]) => {
|
12
|
+
if (Array.isArray(value)) {
|
13
|
+
return value.map((x) => [key, x]);
|
14
|
+
}
|
15
|
+
return [[key, value]];
|
16
|
+
})
|
17
|
+
.flat()
|
18
|
+
)
|
19
|
+
.toString()
|
20
|
+
// Convert space characters to '%20' according to RFC3986 spec, from RFC1738.
|
21
|
+
.replace(/\+/g, '%20')
|
22
|
+
);
|
23
|
+
}
|
24
|
+
|
25
|
+
/**
|
26
|
+
* @description 물음표를 포함한 쿼리 스트링을 생성합니다.
|
27
|
+
* @example createQueryString({ a: 1, b: 2, c: 3 }) // '?a=1&b=2&c=3'
|
28
|
+
*/
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
30
|
+
function createQueryString(params: Record<string, any>) {
|
31
|
+
const queryString = createSearchParamString(params);
|
32
|
+
|
33
|
+
if (queryString === '') {
|
34
|
+
return '';
|
35
|
+
}
|
36
|
+
|
37
|
+
return `?${queryString}`;
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* @description 이터러블에서 키와 값을 가진 객체로 변환합니다.
|
42
|
+
* @example fromEntries([['a', 1], ['b', 2], ['c', 3]]) // { a: 1, b: 2, c: 3 }
|
43
|
+
*/
|
44
|
+
function fromEntries<T extends readonly [string | number, unknown]>(
|
45
|
+
iterable: Iterable<T>
|
46
|
+
) {
|
47
|
+
const result: Record<string | number | symbol, T[1]> = {};
|
48
|
+
|
49
|
+
for (const [key, value] of Array.from(iterable)) {
|
50
|
+
if (result[key]) {
|
51
|
+
if (Array.isArray(result[key])) {
|
52
|
+
(result[key] as Array<string | number>).push(value as string | number);
|
53
|
+
} else {
|
54
|
+
result[key] = [result[key], value];
|
55
|
+
}
|
56
|
+
} else {
|
57
|
+
result[key] = value;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
return result;
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* @description 쿼리 스트링을 객체로 변환합니다.
|
66
|
+
* @example parseQueryString('?a=1&b=2&c=3') // { a: '1', b: '2', c: '3' }
|
67
|
+
*/
|
68
|
+
function parseQueryString<Result = Record<string, string>>(
|
69
|
+
queryString: string = typeof location !== 'undefined' ? location.search : ''
|
70
|
+
): Result {
|
71
|
+
const query = queryString.trim().replace(/^[?#&]/, '');
|
72
|
+
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
74
|
+
return fromEntries(new URLSearchParams(query)) as any;
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @description 쿼리 스트링에서 특정 키의 값을 가져옵니다.
|
79
|
+
*
|
80
|
+
* @param name 가져올 쿼리 스트링의 키
|
81
|
+
* @param parser 가져온 값을 파싱할 함수
|
82
|
+
* @returns 파싱된 값
|
83
|
+
*
|
84
|
+
* @example
|
85
|
+
* url: 'http://example.com/?a=1'
|
86
|
+
* getQueryString('a') // '1'
|
87
|
+
* getQueryString('a', parseInt) // 1
|
88
|
+
* getQueryString('b') // undefined
|
89
|
+
* getQueryString('b', parseInt) // undefined
|
90
|
+
*/
|
91
|
+
function getQueryString(name: string): string | undefined;
|
92
|
+
function getQueryString<T>(
|
93
|
+
name: string,
|
94
|
+
parser: (val: string) => T
|
95
|
+
): T | undefined;
|
96
|
+
function getQueryString<T = string>(name: string, parser?: (val: string) => T) {
|
97
|
+
const value = queryString.parse<{ [name: string]: string | undefined }>()[
|
98
|
+
name
|
99
|
+
];
|
100
|
+
|
101
|
+
if (parser == null || value == null) {
|
102
|
+
return value;
|
103
|
+
} else {
|
104
|
+
return parser(value);
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* @description 기존 쿼리 스트링에 새로운 쿼리 스트링을 추가합니다.
|
110
|
+
* @example setQueryString({ qs: '?a=1', key: 'b', value: '2' }) // '?a=1&b=2'
|
111
|
+
*/
|
112
|
+
function setQueryString({
|
113
|
+
qs,
|
114
|
+
key,
|
115
|
+
value,
|
116
|
+
}: {
|
117
|
+
qs: string;
|
118
|
+
key: string;
|
119
|
+
value: string;
|
120
|
+
}) {
|
121
|
+
const parsed = parseQueryString(qs);
|
122
|
+
|
123
|
+
return createQueryString({
|
124
|
+
...parsed,
|
125
|
+
[key]: value,
|
126
|
+
});
|
127
|
+
}
|
128
|
+
|
129
|
+
export const queryString = {
|
130
|
+
create: createQueryString,
|
131
|
+
parse: parseQueryString,
|
132
|
+
get: getQueryString,
|
133
|
+
set: setQueryString,
|
134
|
+
};
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { coerceCssPixelValue, type CSSPixelValue } from './coerceCssPixelValue';
|
2
|
+
|
3
|
+
export type SafeAreaCssValueProps = {
|
4
|
+
top?: CSSPixelValue;
|
5
|
+
bottom?: CSSPixelValue;
|
6
|
+
left?: CSSPixelValue;
|
7
|
+
right?: CSSPixelValue;
|
8
|
+
};
|
9
|
+
|
10
|
+
const useSafeArea = ({
|
11
|
+
top: T = 0,
|
12
|
+
bottom: B = 0,
|
13
|
+
left: L = 0,
|
14
|
+
right: R = 0,
|
15
|
+
}: SafeAreaCssValueProps = {}) => {
|
16
|
+
const top = `max(${coerceCssPixelValue(T)}, env(safe-area-inset-top))`;
|
17
|
+
const bottom = `max(${coerceCssPixelValue(B)}, env(safe-area-inset-bottom))`;
|
18
|
+
const left = `max(${coerceCssPixelValue(L)}, env(safe-area-inset-left))`;
|
19
|
+
const right = `max(${coerceCssPixelValue(R)}, env(safe-area-inset-right))`;
|
20
|
+
|
21
|
+
return { top, bottom, left, right };
|
22
|
+
};
|
23
|
+
|
24
|
+
export { useSafeArea };
|