goodchuck-utils 1.2.0 → 1.4.0

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.
Files changed (66) hide show
  1. package/dist/form/__tests__/formatter.test.d.ts +2 -0
  2. package/dist/form/__tests__/formatter.test.d.ts.map +1 -0
  3. package/dist/form/__tests__/formatter.test.js +74 -0
  4. package/dist/form/__tests__/helpers.test.d.ts +2 -0
  5. package/dist/form/__tests__/helpers.test.d.ts.map +1 -0
  6. package/dist/form/__tests__/helpers.test.js +42 -0
  7. package/dist/form/__tests__/validation.test.d.ts +2 -0
  8. package/dist/form/__tests__/validation.test.d.ts.map +1 -0
  9. package/dist/form/__tests__/validation.test.js +67 -0
  10. package/dist/form/formatter.d.ts +34 -0
  11. package/dist/form/formatter.d.ts.map +1 -0
  12. package/dist/form/formatter.js +76 -0
  13. package/dist/form/helpers.d.ts +16 -0
  14. package/dist/form/helpers.d.ts.map +1 -0
  15. package/dist/form/helpers.js +34 -0
  16. package/dist/form/index.d.ts +9 -0
  17. package/dist/form/index.d.ts.map +1 -0
  18. package/dist/form/index.js +11 -0
  19. package/dist/form/validation.d.ts +33 -0
  20. package/dist/form/validation.d.ts.map +1 -0
  21. package/dist/form/validation.js +56 -0
  22. package/dist/hooks/index.d.ts +11 -0
  23. package/dist/hooks/index.d.ts.map +1 -0
  24. package/dist/hooks/index.js +12 -0
  25. package/dist/hooks/useClickOutside.d.ts +49 -0
  26. package/dist/hooks/useClickOutside.d.ts.map +1 -0
  27. package/dist/hooks/useClickOutside.js +94 -0
  28. package/dist/hooks/useLocalStorage.d.ts +19 -0
  29. package/dist/hooks/useLocalStorage.d.ts.map +1 -0
  30. package/dist/hooks/useLocalStorage.js +91 -0
  31. package/dist/hooks/useMediaQuery.d.ts +56 -0
  32. package/dist/hooks/useMediaQuery.d.ts.map +1 -0
  33. package/dist/hooks/useMediaQuery.js +104 -0
  34. package/dist/hooks/useSessionStorage.d.ts +19 -0
  35. package/dist/hooks/useSessionStorage.d.ts.map +1 -0
  36. package/dist/hooks/useSessionStorage.js +85 -0
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +7 -2
  40. package/dist/string/__tests__/case.test.d.ts +2 -0
  41. package/dist/string/__tests__/case.test.d.ts.map +1 -0
  42. package/dist/string/__tests__/case.test.js +61 -0
  43. package/dist/string/__tests__/manipulation.test.d.ts +2 -0
  44. package/dist/string/__tests__/manipulation.test.d.ts.map +1 -0
  45. package/dist/string/__tests__/manipulation.test.js +109 -0
  46. package/dist/string/__tests__/validation.test.d.ts +2 -0
  47. package/dist/string/__tests__/validation.test.d.ts.map +1 -0
  48. package/dist/string/__tests__/validation.test.js +101 -0
  49. package/dist/string/case.d.ts +42 -0
  50. package/dist/string/case.d.ts.map +1 -0
  51. package/dist/string/case.js +71 -0
  52. package/dist/string/index.d.ts +9 -0
  53. package/dist/string/index.d.ts.map +1 -0
  54. package/dist/string/index.js +11 -0
  55. package/dist/string/manipulation.d.ts +61 -0
  56. package/dist/string/manipulation.d.ts.map +1 -0
  57. package/dist/string/manipulation.js +106 -0
  58. package/dist/string/validation.d.ts +79 -0
  59. package/dist/string/validation.d.ts.map +1 -0
  60. package/dist/string/validation.js +115 -0
  61. package/package.json +22 -7
  62. package/src/date/index.test.ts +0 -206
  63. package/src/date/index.ts +0 -123
  64. package/src/index.ts +0 -11
  65. package/tsconfig.json +0 -18
  66. package/vitest.config.ts +0 -13
@@ -0,0 +1,94 @@
1
+ import { useEffect } from 'react';
2
+ /**
3
+ * 요소 외부 클릭을 감지하는 hook
4
+ *
5
+ * @param ref - 외부 클릭을 감지할 요소의 ref
6
+ * @param handler - 외부 클릭 시 실행할 콜백 함수
7
+ * @param enabled - hook 활성화 여부 (기본값: true)
8
+ *
9
+ * @example
10
+ * function Dropdown() {
11
+ * const [isOpen, setIsOpen] = useState(false);
12
+ * const dropdownRef = useRef<HTMLDivElement>(null);
13
+ *
14
+ * useClickOutside(dropdownRef, () => setIsOpen(false));
15
+ *
16
+ * return (
17
+ * <div ref={dropdownRef}>
18
+ * <button onClick={() => setIsOpen(!isOpen)}>Toggle</button>
19
+ * {isOpen && <div>Dropdown Content</div>}
20
+ * </div>
21
+ * );
22
+ * }
23
+ *
24
+ * @example
25
+ * // 조건부 활성화
26
+ * useClickOutside(modalRef, handleClose, isModalOpen);
27
+ */
28
+ export function useClickOutside(ref, handler, enabled = true) {
29
+ useEffect(() => {
30
+ if (!enabled) {
31
+ return;
32
+ }
33
+ const listener = (event) => {
34
+ const element = ref.current;
35
+ // ref가 없거나, 클릭한 요소가 ref 내부인 경우 무시
36
+ if (!element || element.contains(event.target)) {
37
+ return;
38
+ }
39
+ // 외부 클릭 시 handler 실행
40
+ handler(event);
41
+ };
42
+ // mousedown과 touchstart 이벤트 모두 처리 (모바일 지원)
43
+ document.addEventListener('mousedown', listener);
44
+ document.addEventListener('touchstart', listener);
45
+ return () => {
46
+ document.removeEventListener('mousedown', listener);
47
+ document.removeEventListener('touchstart', listener);
48
+ };
49
+ }, [ref, handler, enabled]);
50
+ }
51
+ /**
52
+ * 여러 요소의 외부 클릭을 감지하는 hook
53
+ *
54
+ * @param refs - 외부 클릭을 감지할 요소들의 ref 배열
55
+ * @param handler - 외부 클릭 시 실행할 콜백 함수
56
+ * @param enabled - hook 활성화 여부 (기본값: true)
57
+ *
58
+ * @example
59
+ * function Modal() {
60
+ * const modalRef = useRef<HTMLDivElement>(null);
61
+ * const triggerRef = useRef<HTMLButtonElement>(null);
62
+ *
63
+ * // 모달과 트리거 버튼 외부 클릭 시 닫기
64
+ * useClickOutsideMultiple(
65
+ * [modalRef, triggerRef],
66
+ * () => setIsOpen(false)
67
+ * );
68
+ * }
69
+ */
70
+ export function useClickOutsideMultiple(refs, handler, enabled = true) {
71
+ useEffect(() => {
72
+ if (!enabled) {
73
+ return;
74
+ }
75
+ const listener = (event) => {
76
+ // 모든 ref를 확인하여 하나라도 내부 클릭이면 무시
77
+ const isInside = refs.some((ref) => {
78
+ const element = ref.current;
79
+ return element && element.contains(event.target);
80
+ });
81
+ if (isInside) {
82
+ return;
83
+ }
84
+ // 모든 요소 외부 클릭 시 handler 실행
85
+ handler(event);
86
+ };
87
+ document.addEventListener('mousedown', listener);
88
+ document.addEventListener('touchstart', listener);
89
+ return () => {
90
+ document.removeEventListener('mousedown', listener);
91
+ document.removeEventListener('touchstart', listener);
92
+ };
93
+ }, [refs, handler, enabled]);
94
+ }
@@ -0,0 +1,19 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ /**
3
+ * localStorage와 동기화되는 state hook
4
+ *
5
+ * @param key - localStorage 키
6
+ * @param initialValue - 초기값
7
+ * @returns [storedValue, setValue, removeValue]
8
+ *
9
+ * @example
10
+ * const [name, setName, removeName] = useLocalStorage('username', 'Guest');
11
+ *
12
+ * // 값 설정
13
+ * setName('John');
14
+ *
15
+ * // 값 제거
16
+ * removeName();
17
+ */
18
+ export declare function useLocalStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>, () => void];
19
+ //# sourceMappingURL=useLocalStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLocalStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocalStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEnF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAC/B,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CA0F9C"}
@@ -0,0 +1,91 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ /**
3
+ * localStorage와 동기화되는 state hook
4
+ *
5
+ * @param key - localStorage 키
6
+ * @param initialValue - 초기값
7
+ * @returns [storedValue, setValue, removeValue]
8
+ *
9
+ * @example
10
+ * const [name, setName, removeName] = useLocalStorage('username', 'Guest');
11
+ *
12
+ * // 값 설정
13
+ * setName('John');
14
+ *
15
+ * // 값 제거
16
+ * removeName();
17
+ */
18
+ export function useLocalStorage(key, initialValue) {
19
+ // SSR 안전성 체크
20
+ const isBrowser = typeof window !== 'undefined';
21
+ // 초기값을 localStorage에서 가져오기
22
+ const readValue = useCallback(() => {
23
+ if (!isBrowser) {
24
+ return initialValue;
25
+ }
26
+ try {
27
+ const item = window.localStorage.getItem(key);
28
+ return item ? JSON.parse(item) : initialValue;
29
+ }
30
+ catch (error) {
31
+ console.warn(`Error reading localStorage key "${key}":`, error);
32
+ return initialValue;
33
+ }
34
+ }, [initialValue, key, isBrowser]);
35
+ const [storedValue, setStoredValue] = useState(readValue);
36
+ // 값 설정 함수
37
+ const setValue = useCallback((value) => {
38
+ if (!isBrowser) {
39
+ console.warn(`Tried setting localStorage key "${key}" even though environment is not a client`);
40
+ return;
41
+ }
42
+ try {
43
+ // useState와 동일하게 함수형 업데이트 지원
44
+ const newValue = value instanceof Function ? value(storedValue) : value;
45
+ // localStorage에 저장
46
+ window.localStorage.setItem(key, JSON.stringify(newValue));
47
+ // state 업데이트
48
+ setStoredValue(newValue);
49
+ // storage event 발생 (다른 탭/윈도우에 알림)
50
+ window.dispatchEvent(new Event('local-storage'));
51
+ }
52
+ catch (error) {
53
+ console.warn(`Error setting localStorage key "${key}":`, error);
54
+ }
55
+ }, [key, storedValue, isBrowser]);
56
+ // 값 제거 함수
57
+ const removeValue = useCallback(() => {
58
+ if (!isBrowser) {
59
+ return;
60
+ }
61
+ try {
62
+ window.localStorage.removeItem(key);
63
+ setStoredValue(initialValue);
64
+ window.dispatchEvent(new Event('local-storage'));
65
+ }
66
+ catch (error) {
67
+ console.warn(`Error removing localStorage key "${key}":`, error);
68
+ }
69
+ }, [key, initialValue, isBrowser]);
70
+ // 다른 탭/윈도우의 변경사항 감지
71
+ useEffect(() => {
72
+ if (!isBrowser) {
73
+ return;
74
+ }
75
+ const handleStorageChange = (e) => {
76
+ if ('key' in e && e.key && e.key !== key) {
77
+ return;
78
+ }
79
+ setStoredValue(readValue());
80
+ };
81
+ // storage event 리스너 (다른 탭의 변경사항)
82
+ window.addEventListener('storage', handleStorageChange);
83
+ // 같은 페이지 내의 변경사항
84
+ window.addEventListener('local-storage', handleStorageChange);
85
+ return () => {
86
+ window.removeEventListener('storage', handleStorageChange);
87
+ window.removeEventListener('local-storage', handleStorageChange);
88
+ };
89
+ }, [key, readValue, isBrowser]);
90
+ return [storedValue, setValue, removeValue];
91
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 미디어 쿼리 매칭 여부를 반환하는 hook
3
+ *
4
+ * @param query - CSS 미디어 쿼리 문자열
5
+ * @returns 미디어 쿼리 매칭 여부
6
+ *
7
+ * @example
8
+ * // 모바일 체크
9
+ * const isMobile = useMediaQuery('(max-width: 768px)');
10
+ *
11
+ * @example
12
+ * // 태블릿 체크
13
+ * const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
14
+ *
15
+ * @example
16
+ * // 데스크톱 체크
17
+ * const isDesktop = useMediaQuery('(min-width: 1025px)');
18
+ *
19
+ * @example
20
+ * // 다크모드 체크
21
+ * const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
22
+ *
23
+ * @example
24
+ * // 가로 모드 체크
25
+ * const isLandscape = useMediaQuery('(orientation: landscape)');
26
+ */
27
+ export declare function useMediaQuery(query: string): boolean;
28
+ /**
29
+ * 일반적인 브레이크포인트를 위한 헬퍼 hook들
30
+ */
31
+ /**
32
+ * 모바일 디바이스 체크 (768px 이하)
33
+ * @example
34
+ * const isMobile = useIsMobile();
35
+ * if (isMobile) return <MobileView />;
36
+ */
37
+ export declare function useIsMobile(): boolean;
38
+ /**
39
+ * 태블릿 디바이스 체크 (769px ~ 1024px)
40
+ * @example
41
+ * const isTablet = useIsTablet();
42
+ */
43
+ export declare function useIsTablet(): boolean;
44
+ /**
45
+ * 데스크톱 디바이스 체크 (1025px 이상)
46
+ * @example
47
+ * const isDesktop = useIsDesktop();
48
+ */
49
+ export declare function useIsDesktop(): boolean;
50
+ /**
51
+ * 다크모드 체크
52
+ * @example
53
+ * const isDarkMode = useIsDarkMode();
54
+ */
55
+ export declare function useIsDarkMode(): boolean;
56
+ //# sourceMappingURL=useMediaQuery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useMediaQuery.d.ts","sourceRoot":"","sources":["../../src/hooks/useMediaQuery.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CA8CpD;AAED;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC"}
@@ -0,0 +1,104 @@
1
+ import { useState, useEffect } from 'react';
2
+ /**
3
+ * 미디어 쿼리 매칭 여부를 반환하는 hook
4
+ *
5
+ * @param query - CSS 미디어 쿼리 문자열
6
+ * @returns 미디어 쿼리 매칭 여부
7
+ *
8
+ * @example
9
+ * // 모바일 체크
10
+ * const isMobile = useMediaQuery('(max-width: 768px)');
11
+ *
12
+ * @example
13
+ * // 태블릿 체크
14
+ * const isTablet = useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
15
+ *
16
+ * @example
17
+ * // 데스크톱 체크
18
+ * const isDesktop = useMediaQuery('(min-width: 1025px)');
19
+ *
20
+ * @example
21
+ * // 다크모드 체크
22
+ * const isDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
23
+ *
24
+ * @example
25
+ * // 가로 모드 체크
26
+ * const isLandscape = useMediaQuery('(orientation: landscape)');
27
+ */
28
+ export function useMediaQuery(query) {
29
+ // SSR 안전성을 위한 초기값 설정
30
+ const getMatches = (query) => {
31
+ if (typeof window !== 'undefined') {
32
+ return window.matchMedia(query).matches;
33
+ }
34
+ return false;
35
+ };
36
+ const [matches, setMatches] = useState(getMatches(query));
37
+ useEffect(() => {
38
+ if (typeof window === 'undefined') {
39
+ return;
40
+ }
41
+ const mediaQuery = window.matchMedia(query);
42
+ // 초기 상태 설정
43
+ setMatches(mediaQuery.matches);
44
+ // 미디어 쿼리 변경 감지 (최신 API)
45
+ const handleChange = (event) => {
46
+ setMatches(event.matches);
47
+ };
48
+ // 이벤트 리스너 등록
49
+ // addEventListener를 지원하는 브라우저
50
+ if (mediaQuery.addEventListener) {
51
+ mediaQuery.addEventListener('change', handleChange);
52
+ }
53
+ else {
54
+ // 구형 브라우저 지원 (deprecated)
55
+ mediaQuery.addListener(handleChange);
56
+ }
57
+ // 클린업
58
+ return () => {
59
+ if (mediaQuery.removeEventListener) {
60
+ mediaQuery.removeEventListener('change', handleChange);
61
+ }
62
+ else {
63
+ mediaQuery.removeListener(handleChange);
64
+ }
65
+ };
66
+ }, [query]);
67
+ return matches;
68
+ }
69
+ /**
70
+ * 일반적인 브레이크포인트를 위한 헬퍼 hook들
71
+ */
72
+ /**
73
+ * 모바일 디바이스 체크 (768px 이하)
74
+ * @example
75
+ * const isMobile = useIsMobile();
76
+ * if (isMobile) return <MobileView />;
77
+ */
78
+ export function useIsMobile() {
79
+ return useMediaQuery('(max-width: 768px)');
80
+ }
81
+ /**
82
+ * 태블릿 디바이스 체크 (769px ~ 1024px)
83
+ * @example
84
+ * const isTablet = useIsTablet();
85
+ */
86
+ export function useIsTablet() {
87
+ return useMediaQuery('(min-width: 769px) and (max-width: 1024px)');
88
+ }
89
+ /**
90
+ * 데스크톱 디바이스 체크 (1025px 이상)
91
+ * @example
92
+ * const isDesktop = useIsDesktop();
93
+ */
94
+ export function useIsDesktop() {
95
+ return useMediaQuery('(min-width: 1025px)');
96
+ }
97
+ /**
98
+ * 다크모드 체크
99
+ * @example
100
+ * const isDarkMode = useIsDarkMode();
101
+ */
102
+ export function useIsDarkMode() {
103
+ return useMediaQuery('(prefers-color-scheme: dark)');
104
+ }
@@ -0,0 +1,19 @@
1
+ import { Dispatch, SetStateAction } from 'react';
2
+ /**
3
+ * sessionStorage와 동기화되는 state hook
4
+ *
5
+ * @param key - sessionStorage 키
6
+ * @param initialValue - 초기값
7
+ * @returns [storedValue, setValue, removeValue]
8
+ *
9
+ * @example
10
+ * const [token, setToken, removeToken] = useSessionStorage('auth-token', '');
11
+ *
12
+ * // 값 설정
13
+ * setToken('abc123');
14
+ *
15
+ * // 값 제거
16
+ * removeToken();
17
+ */
18
+ export declare function useSessionStorage<T>(key: string, initialValue: T): [T, Dispatch<SetStateAction<T>>, () => void];
19
+ //# sourceMappingURL=useSessionStorage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSessionStorage.d.ts","sourceRoot":"","sources":["../../src/hooks/useSessionStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEnF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EACjC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,CAAC,GACd,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAoF9C"}
@@ -0,0 +1,85 @@
1
+ import { useState, useEffect, useCallback } from 'react';
2
+ /**
3
+ * sessionStorage와 동기화되는 state hook
4
+ *
5
+ * @param key - sessionStorage 키
6
+ * @param initialValue - 초기값
7
+ * @returns [storedValue, setValue, removeValue]
8
+ *
9
+ * @example
10
+ * const [token, setToken, removeToken] = useSessionStorage('auth-token', '');
11
+ *
12
+ * // 값 설정
13
+ * setToken('abc123');
14
+ *
15
+ * // 값 제거
16
+ * removeToken();
17
+ */
18
+ export function useSessionStorage(key, initialValue) {
19
+ // SSR 안전성 체크
20
+ const isBrowser = typeof window !== 'undefined';
21
+ // 초기값을 sessionStorage에서 가져오기
22
+ const readValue = useCallback(() => {
23
+ if (!isBrowser) {
24
+ return initialValue;
25
+ }
26
+ try {
27
+ const item = window.sessionStorage.getItem(key);
28
+ return item ? JSON.parse(item) : initialValue;
29
+ }
30
+ catch (error) {
31
+ console.warn(`Error reading sessionStorage key "${key}":`, error);
32
+ return initialValue;
33
+ }
34
+ }, [initialValue, key, isBrowser]);
35
+ const [storedValue, setStoredValue] = useState(readValue);
36
+ // 값 설정 함수
37
+ const setValue = useCallback((value) => {
38
+ if (!isBrowser) {
39
+ console.warn(`Tried setting sessionStorage key "${key}" even though environment is not a client`);
40
+ return;
41
+ }
42
+ try {
43
+ // useState와 동일하게 함수형 업데이트 지원
44
+ const newValue = value instanceof Function ? value(storedValue) : value;
45
+ // sessionStorage에 저장
46
+ window.sessionStorage.setItem(key, JSON.stringify(newValue));
47
+ // state 업데이트
48
+ setStoredValue(newValue);
49
+ // storage event 발생
50
+ window.dispatchEvent(new Event('session-storage'));
51
+ }
52
+ catch (error) {
53
+ console.warn(`Error setting sessionStorage key "${key}":`, error);
54
+ }
55
+ }, [key, storedValue, isBrowser]);
56
+ // 값 제거 함수
57
+ const removeValue = useCallback(() => {
58
+ if (!isBrowser) {
59
+ return;
60
+ }
61
+ try {
62
+ window.sessionStorage.removeItem(key);
63
+ setStoredValue(initialValue);
64
+ window.dispatchEvent(new Event('session-storage'));
65
+ }
66
+ catch (error) {
67
+ console.warn(`Error removing sessionStorage key "${key}":`, error);
68
+ }
69
+ }, [key, initialValue, isBrowser]);
70
+ // 같은 페이지 내의 변경사항 감지
71
+ useEffect(() => {
72
+ if (!isBrowser) {
73
+ return;
74
+ }
75
+ const handleStorageChange = () => {
76
+ setStoredValue(readValue());
77
+ };
78
+ // 같은 페이지 내의 변경사항
79
+ window.addEventListener('session-storage', handleStorageChange);
80
+ return () => {
81
+ window.removeEventListener('session-storage', handleStorageChange);
82
+ };
83
+ }, [key, readValue, isBrowser]);
84
+ return [storedValue, setValue, removeValue];
85
+ }
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export * from './date';
2
+ export * from './form';
3
+ export * from './string';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,QAAQ,CAAC;AAGvB,cAAc,QAAQ,CAAC;AAGvB,cAAc,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,12 @@
1
1
  // Date utilities
2
2
  export * from './date';
3
- // String utilities (placeholder for future)
4
- // export * from './string';
3
+ // Form utilities
4
+ export * from './form';
5
+ // String utilities
6
+ export * from './string';
7
+ // React Hooks (import separately: 'goodchuck-utils/hooks')
8
+ // Note: Hooks are not exported from main entry to avoid React dependency for non-React users
9
+ // export * from './hooks';
5
10
  // Array utilities (placeholder for future)
6
11
  // export * from './array';
7
12
  // Object utilities (placeholder for future)
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=case.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"case.test.d.ts","sourceRoot":"","sources":["../../../src/string/__tests__/case.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { capitalize, capitalizeWords, camelCase, pascalCase, snakeCase, kebabCase, constantCase, } from '../case';
3
+ describe('String Case Conversion', () => {
4
+ describe('capitalize', () => {
5
+ it('should capitalize first letter', () => {
6
+ expect(capitalize('hello')).toBe('Hello');
7
+ expect(capitalize('hello world')).toBe('Hello world');
8
+ expect(capitalize('HELLO')).toBe('HELLO');
9
+ });
10
+ it('should handle empty string', () => {
11
+ expect(capitalize('')).toBe('');
12
+ });
13
+ });
14
+ describe('capitalizeWords', () => {
15
+ it('should capitalize each word', () => {
16
+ expect(capitalizeWords('hello world')).toBe('Hello World');
17
+ expect(capitalizeWords('the quick brown fox')).toBe('The Quick Brown Fox');
18
+ });
19
+ });
20
+ describe('camelCase', () => {
21
+ it('should convert to camelCase', () => {
22
+ expect(camelCase('hello world')).toBe('helloWorld');
23
+ expect(camelCase('hello-world')).toBe('helloWorld');
24
+ expect(camelCase('hello_world')).toBe('helloWorld');
25
+ expect(camelCase('Hello World')).toBe('helloWorld');
26
+ });
27
+ it('should handle already camelCase strings', () => {
28
+ expect(camelCase('helloWorld')).toBe('helloworld');
29
+ });
30
+ });
31
+ describe('pascalCase', () => {
32
+ it('should convert to PascalCase', () => {
33
+ expect(pascalCase('hello world')).toBe('HelloWorld');
34
+ expect(pascalCase('hello-world')).toBe('HelloWorld');
35
+ expect(pascalCase('hello_world')).toBe('HelloWorld');
36
+ });
37
+ });
38
+ describe('snakeCase', () => {
39
+ it('should convert to snake_case', () => {
40
+ expect(snakeCase('helloWorld')).toBe('hello_world');
41
+ expect(snakeCase('Hello World')).toBe('hello_world');
42
+ expect(snakeCase('hello-world')).toBe('hello_world');
43
+ expect(snakeCase('HelloWorld')).toBe('hello_world');
44
+ });
45
+ });
46
+ describe('kebabCase', () => {
47
+ it('should convert to kebab-case', () => {
48
+ expect(kebabCase('helloWorld')).toBe('hello-world');
49
+ expect(kebabCase('Hello World')).toBe('hello-world');
50
+ expect(kebabCase('hello_world')).toBe('hello-world');
51
+ expect(kebabCase('HelloWorld')).toBe('hello-world');
52
+ });
53
+ });
54
+ describe('constantCase', () => {
55
+ it('should convert to CONSTANT_CASE', () => {
56
+ expect(constantCase('helloWorld')).toBe('HELLO_WORLD');
57
+ expect(constantCase('Hello World')).toBe('HELLO_WORLD');
58
+ expect(constantCase('hello-world')).toBe('HELLO_WORLD');
59
+ });
60
+ });
61
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=manipulation.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manipulation.test.d.ts","sourceRoot":"","sources":["../../../src/string/__tests__/manipulation.test.ts"],"names":[],"mappings":""}