@sonamu-kit/react-components 0.1.0 → 0.1.2

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 (146) hide show
  1. package/dist/components/index.d.ts +65 -0
  2. package/dist/components/ui/accordion.d.ts +7 -0
  3. package/dist/components/ui/alert-dialog.d.ts +20 -0
  4. package/dist/components/ui/alert.d.ts +8 -0
  5. package/dist/components/ui/aspect-ratio.d.ts +3 -0
  6. package/dist/components/ui/async-select.d.ts +36 -0
  7. package/dist/components/ui/avatar.d.ts +6 -0
  8. package/dist/components/ui/badge.d.ts +9 -0
  9. package/dist/components/ui/breadcrumb.d.ts +19 -0
  10. package/dist/components/ui/button.d.ts +13 -0
  11. package/dist/components/ui/calendar.d.ts +5 -0
  12. package/dist/components/ui/card.d.ts +9 -0
  13. package/dist/components/ui/carousel.d.ts +18 -0
  14. package/dist/components/ui/checkbox.d.ts +8 -0
  15. package/dist/components/ui/collapsible.d.ts +5 -0
  16. package/dist/components/ui/combobox.d.ts +20 -0
  17. package/dist/components/ui/command.d.ts +80 -0
  18. package/dist/components/ui/common-modal.d.ts +28 -0
  19. package/dist/components/ui/context-menu.d.ts +27 -0
  20. package/dist/components/ui/date-input.d.ts +7 -0
  21. package/dist/components/ui/date-picker.d.ts +26 -0
  22. package/dist/components/ui/date-selector-multiple.d.ts +38 -0
  23. package/dist/components/ui/dialog.d.ts +19 -0
  24. package/dist/components/ui/drawer.d.ts +22 -0
  25. package/dist/components/ui/dropdown-menu.d.ts +27 -0
  26. package/dist/components/ui/form.d.ts +23 -0
  27. package/dist/components/ui/hover-card.d.ts +6 -0
  28. package/dist/components/ui/image-uploader.d.ts +14 -0
  29. package/dist/components/ui/input-otp.d.ts +34 -0
  30. package/dist/components/ui/input.d.ts +7 -0
  31. package/dist/components/ui/label.d.ts +5 -0
  32. package/dist/components/ui/menubar.d.ts +28 -0
  33. package/dist/components/ui/month-picker-multiple.d.ts +41 -0
  34. package/dist/components/ui/multi-image-uploader.d.ts +15 -0
  35. package/dist/components/ui/multi-select.d.ts +229 -0
  36. package/dist/components/ui/navigation-menu.d.ts +12 -0
  37. package/dist/components/ui/pagination.d.ts +10 -0
  38. package/dist/components/ui/popover.d.ts +7 -0
  39. package/dist/components/ui/progress.d.ts +4 -0
  40. package/dist/components/ui/radio-group.d.ts +5 -0
  41. package/dist/components/ui/resizable.d.ts +23 -0
  42. package/dist/components/ui/scroll-area.d.ts +5 -0
  43. package/dist/components/ui/select.d.ts +20 -0
  44. package/dist/components/ui/separator.d.ts +4 -0
  45. package/dist/components/ui/sheet.d.ts +25 -0
  46. package/dist/components/ui/sidebar.d.ts +69 -0
  47. package/dist/components/ui/skeleton.d.ts +2 -0
  48. package/dist/components/ui/slider.d.ts +8 -0
  49. package/dist/components/ui/sonner.d.ts +4 -0
  50. package/dist/components/ui/switch.d.ts +8 -0
  51. package/dist/components/ui/table.d.ts +24 -0
  52. package/dist/components/ui/tabs.d.ts +7 -0
  53. package/dist/components/ui/textarea.d.ts +7 -0
  54. package/dist/components/ui/toast.d.ts +15 -0
  55. package/dist/components/ui/toaster.d.ts +1 -0
  56. package/dist/components/ui/toggle-group.d.ts +12 -0
  57. package/dist/components/ui/toggle.d.ts +12 -0
  58. package/dist/components/ui/tooltip.d.ts +7 -0
  59. package/dist/hooks/index.d.ts +1 -0
  60. package/dist/hooks/use-toast.d.ts +44 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/lib/caster.d.ts +3 -0
  63. package/dist/lib/helpers.d.ts +72 -0
  64. package/dist/lib/index.d.ts +6 -0
  65. package/{src/lib/lazy-upload.ts → dist/lib/lazy-upload.d.ts} +1 -12
  66. package/dist/lib/use-mobile.d.ts +1 -0
  67. package/dist/lib/utils.d.ts +2 -0
  68. package/dist/react-components.es.js +28375 -0
  69. package/package.json +105 -76
  70. package/COMPONENTS_LIST.md +0 -106
  71. package/COMPONENTS_STATUS.md +0 -114
  72. package/HELPERS_GUIDE.md +0 -489
  73. package/MIGRATION_PLAN.md +0 -404
  74. package/SETUP_GUIDE.md +0 -125
  75. package/components.json +0 -21
  76. package/postcss.config.js +0 -6
  77. package/src/components/index.ts +0 -315
  78. package/src/components/ui/accordion.tsx +0 -54
  79. package/src/components/ui/alert-dialog.tsx +0 -115
  80. package/src/components/ui/alert.tsx +0 -49
  81. package/src/components/ui/aspect-ratio.tsx +0 -5
  82. package/src/components/ui/async-select.tsx +0 -186
  83. package/src/components/ui/avatar.tsx +0 -45
  84. package/src/components/ui/badge.tsx +0 -38
  85. package/src/components/ui/breadcrumb.tsx +0 -102
  86. package/src/components/ui/button.tsx +0 -54
  87. package/src/components/ui/calendar.tsx +0 -193
  88. package/src/components/ui/card.tsx +0 -65
  89. package/src/components/ui/carousel.tsx +0 -243
  90. package/src/components/ui/checkbox.tsx +0 -67
  91. package/src/components/ui/collapsible.tsx +0 -9
  92. package/src/components/ui/combobox.tsx +0 -135
  93. package/src/components/ui/command.tsx +0 -143
  94. package/src/components/ui/common-modal.tsx +0 -95
  95. package/src/components/ui/context-menu.tsx +0 -189
  96. package/src/components/ui/date-picker.tsx +0 -112
  97. package/src/components/ui/date-selector-multiple.tsx +0 -197
  98. package/src/components/ui/dialog.tsx +0 -104
  99. package/src/components/ui/drawer.tsx +0 -100
  100. package/src/components/ui/dropdown-menu.tsx +0 -189
  101. package/src/components/ui/form.tsx +0 -171
  102. package/src/components/ui/hover-card.tsx +0 -27
  103. package/src/components/ui/image-uploader.tsx +0 -251
  104. package/src/components/ui/input-otp.tsx +0 -69
  105. package/src/components/ui/input.tsx +0 -38
  106. package/src/components/ui/label.tsx +0 -19
  107. package/src/components/ui/menubar.tsx +0 -231
  108. package/src/components/ui/month-picker-multiple.tsx +0 -351
  109. package/src/components/ui/multi-image-uploader.tsx +0 -283
  110. package/src/components/ui/multi-select.tsx +0 -1143
  111. package/src/components/ui/navigation-menu.tsx +0 -120
  112. package/src/components/ui/pagination.tsx +0 -72
  113. package/src/components/ui/popover.tsx +0 -42
  114. package/src/components/ui/progress.tsx +0 -25
  115. package/src/components/ui/radio-group.tsx +0 -38
  116. package/src/components/ui/resizable.tsx +0 -42
  117. package/src/components/ui/scroll-area.tsx +0 -46
  118. package/src/components/ui/select.tsx +0 -235
  119. package/src/components/ui/separator.tsx +0 -24
  120. package/src/components/ui/sheet.tsx +0 -119
  121. package/src/components/ui/sidebar.tsx +0 -683
  122. package/src/components/ui/skeleton.tsx +0 -7
  123. package/src/components/ui/slider.tsx +0 -57
  124. package/src/components/ui/sonner.tsx +0 -39
  125. package/src/components/ui/switch.tsx +0 -63
  126. package/src/components/ui/table.tsx +0 -94
  127. package/src/components/ui/tabs.tsx +0 -53
  128. package/src/components/ui/textarea.tsx +0 -34
  129. package/src/components/ui/toast.tsx +0 -122
  130. package/src/components/ui/toaster.tsx +0 -29
  131. package/src/components/ui/toggle-group.tsx +0 -55
  132. package/src/components/ui/toggle.tsx +0 -41
  133. package/src/components/ui/tooltip.tsx +0 -28
  134. package/src/hooks/index.ts +0 -2
  135. package/src/hooks/use-toast.ts +0 -189
  136. package/src/icons.d.ts +0 -1
  137. package/src/index.ts +0 -4
  138. package/src/lib/caster.ts +0 -66
  139. package/src/lib/helpers.ts +0 -394
  140. package/src/lib/index.ts +0 -31
  141. package/src/lib/use-mobile.ts +0 -19
  142. package/src/lib/utils.ts +0 -6
  143. package/src/styles/globals.css +0 -658
  144. package/tailwind.config.ts +0 -8
  145. package/tsconfig.json +0 -31
  146. package/tsconfig.node.json +0 -11
@@ -1,189 +0,0 @@
1
- "use client";
2
-
3
- // Inspired by react-hot-toast library
4
- import * as React from "react";
5
-
6
- import type { ToastActionElement, ToastProps } from "../components/ui/toast";
7
-
8
- const TOAST_LIMIT = 1;
9
- const TOAST_REMOVE_DELAY = 1000000;
10
-
11
- type ToasterToast = ToastProps & {
12
- id: string;
13
- title?: React.ReactNode;
14
- description?: React.ReactNode;
15
- action?: ToastActionElement;
16
- };
17
-
18
- const actionTypes = {
19
- ADD_TOAST: "ADD_TOAST",
20
- UPDATE_TOAST: "UPDATE_TOAST",
21
- DISMISS_TOAST: "DISMISS_TOAST",
22
- REMOVE_TOAST: "REMOVE_TOAST",
23
- } as const;
24
-
25
- let count = 0;
26
-
27
- function genId() {
28
- count = (count + 1) % Number.MAX_SAFE_INTEGER;
29
- return count.toString();
30
- }
31
-
32
- type ActionType = typeof actionTypes;
33
-
34
- type Action =
35
- | {
36
- type: ActionType["ADD_TOAST"];
37
- toast: ToasterToast;
38
- }
39
- | {
40
- type: ActionType["UPDATE_TOAST"];
41
- toast: Partial<ToasterToast>;
42
- }
43
- | {
44
- type: ActionType["DISMISS_TOAST"];
45
- toastId?: ToasterToast["id"];
46
- }
47
- | {
48
- type: ActionType["REMOVE_TOAST"];
49
- toastId?: ToasterToast["id"];
50
- };
51
-
52
- interface State {
53
- toasts: ToasterToast[];
54
- }
55
-
56
- const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
57
-
58
- const addToRemoveQueue = (toastId: string) => {
59
- if (toastTimeouts.has(toastId)) {
60
- return;
61
- }
62
-
63
- const timeout = setTimeout(() => {
64
- toastTimeouts.delete(toastId);
65
- dispatch({
66
- type: "REMOVE_TOAST",
67
- toastId: toastId,
68
- });
69
- }, TOAST_REMOVE_DELAY);
70
-
71
- toastTimeouts.set(toastId, timeout);
72
- };
73
-
74
- export const reducer = (state: State, action: Action): State => {
75
- switch (action.type) {
76
- case "ADD_TOAST":
77
- return {
78
- ...state,
79
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
80
- };
81
-
82
- case "UPDATE_TOAST":
83
- return {
84
- ...state,
85
- toasts: state.toasts.map((t) => (t.id === action.toast.id ? { ...t, ...action.toast } : t)),
86
- };
87
-
88
- case "DISMISS_TOAST": {
89
- const { toastId } = action;
90
-
91
- // ! Side effects ! - This could be extracted into a dismissToast() action,
92
- // but I'll keep it here for simplicity
93
- if (toastId) {
94
- addToRemoveQueue(toastId);
95
- } else {
96
- state.toasts.forEach((toast) => {
97
- addToRemoveQueue(toast.id);
98
- });
99
- }
100
-
101
- return {
102
- ...state,
103
- toasts: state.toasts.map((t) =>
104
- t.id === toastId || toastId === undefined
105
- ? {
106
- ...t,
107
- open: false,
108
- }
109
- : t,
110
- ),
111
- };
112
- }
113
- case "REMOVE_TOAST":
114
- if (action.toastId === undefined) {
115
- return {
116
- ...state,
117
- toasts: [],
118
- };
119
- }
120
- return {
121
- ...state,
122
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
123
- };
124
- }
125
- };
126
-
127
- const listeners: Array<(state: State) => void> = [];
128
-
129
- let memoryState: State = { toasts: [] };
130
-
131
- function dispatch(action: Action) {
132
- memoryState = reducer(memoryState, action);
133
- listeners.forEach((listener) => {
134
- listener(memoryState);
135
- });
136
- }
137
-
138
- type Toast = Omit<ToasterToast, "id">;
139
-
140
- function toast({ ...props }: Toast) {
141
- const id = genId();
142
-
143
- const update = (props: ToasterToast) =>
144
- dispatch({
145
- type: "UPDATE_TOAST",
146
- toast: { ...props, id },
147
- });
148
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
149
-
150
- dispatch({
151
- type: "ADD_TOAST",
152
- toast: {
153
- ...props,
154
- id,
155
- open: true,
156
- onOpenChange: (open) => {
157
- if (!open) dismiss();
158
- },
159
- },
160
- });
161
-
162
- return {
163
- id: id,
164
- dismiss,
165
- update,
166
- };
167
- }
168
-
169
- function useToast() {
170
- const [state, setState] = React.useState<State>(memoryState);
171
-
172
- React.useEffect(() => {
173
- listeners.push(setState);
174
- return () => {
175
- const index = listeners.indexOf(setState);
176
- if (index > -1) {
177
- listeners.splice(index, 1);
178
- }
179
- };
180
- }, [state]);
181
-
182
- return {
183
- ...state,
184
- toast,
185
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
186
- };
187
- }
188
-
189
- export { useToast, toast };
package/src/icons.d.ts DELETED
@@ -1 +0,0 @@
1
- /// <reference types="unplugin-icons/types/react" />
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- // Main entry point
2
- export * from "./components";
3
- export * from "./hooks";
4
- export * from "./lib";
package/src/lib/caster.ts DELETED
@@ -1,66 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/noExplicitAny: caster 함수에는 any가 필요함 */
2
- import { z } from "zod";
3
-
4
- // optional, nullable 무관하게 ZodNumber 체크
5
- function isZodNumberAnyway(zodType: z.ZodType<any>) {
6
- if (zodType instanceof z.ZodNumber) {
7
- return true;
8
- } else if (zodType instanceof z.ZodNullable && zodType._def.innerType instanceof z.ZodNumber) {
9
- return true;
10
- } else if (zodType instanceof z.ZodOptional && zodType._def.innerType instanceof z.ZodNumber) {
11
- return true;
12
- } else if (
13
- zodType instanceof z.ZodOptional &&
14
- zodType._def.innerType instanceof z.ZodOptional &&
15
- (zodType._def.innerType as any)._def.innerType instanceof z.ZodNumber
16
- ) {
17
- return true;
18
- }
19
-
20
- return false;
21
- }
22
-
23
- // ZodType을 이용해 raw를 Type Coercing
24
- export function caster(zodType: z.ZodType<any>, raw: any): any {
25
- if (isZodNumberAnyway(zodType) && typeof raw === "string") {
26
- // number
27
- return Number(raw);
28
- } else if (zodType instanceof z.ZodUnion) {
29
- const options = zodType.options as z.ZodType<any>[];
30
- if (options.some((opt) => isZodNumberAnyway(opt))) {
31
- // zArrayable Number 케이스 처리
32
- if (Array.isArray(raw)) {
33
- const numType = options.find((opt) => opt instanceof z.ZodNumber);
34
- if (!numType) {
35
- throw new Error("Expected to find a number type in union");
36
- }
37
- return raw.map((elem: any) => caster(numType, elem));
38
- } else {
39
- return Number(raw);
40
- }
41
- }
42
- } else if (zodType instanceof z.ZodBoolean && (raw === "true" || raw === "false")) {
43
- // boolean
44
- return raw === "true";
45
- } else if (zodType instanceof z.ZodArray) {
46
- // array
47
- return raw.map((elem: any) => caster(zodType.element as z.ZodType<any>, elem));
48
- } else if (zodType instanceof z.ZodObject && typeof raw === "object") {
49
- // object
50
- return Object.keys(raw).reduce((r, rawKey) => {
51
- r[rawKey] = caster(zodType.shape[rawKey], raw[rawKey]);
52
- return r;
53
- }, {} as any);
54
- } else if (zodType instanceof z.ZodOptional) {
55
- return caster(zodType._def.innerType as z.ZodType<any>, raw);
56
- }
57
-
58
- // 나머지는 처리 안함
59
- return raw;
60
- }
61
-
62
- export function fastifyCaster(schema: z.ZodObject<any>) {
63
- return z.preprocess((raw: any) => {
64
- return caster(schema, raw);
65
- }, schema);
66
- }
@@ -1,394 +0,0 @@
1
- /** biome-ignore-all lint/suspicious/noExplicitAny: 파싱 결과이므로 any 허용 */
2
-
3
- import { useNavigate, useRouter, useSearch } from "@tanstack/react-router";
4
- import equal from "fast-deep-equal";
5
- import qs from "qs";
6
- import { get, isObject, set, unique } from "radashi";
7
- import React, { type ReactElement, useEffect, useState } from "react";
8
- import { z } from "zod";
9
- import { caster } from "./caster";
10
-
11
- // radashi에 intersection이 없으므로 직접 구현
12
- function intersection<T>(arr1: T[], arr2: T[]): T[] {
13
- return arr1.filter((item) => arr2.includes(item));
14
- }
15
-
16
- // shadcn/ui용 타입 정의 (semantic-ui-react 대체)
17
- export type PaginationProps = {
18
- activePage?: number;
19
- totalPages?: number;
20
- };
21
-
22
- export type TableColumnWidth = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16;
23
-
24
- export function hidden(condition: boolean | undefined): string {
25
- return condition === true ? "hidden" : "";
26
- }
27
-
28
- export function searchParamsToParams<T extends z.ZodType<any>>(
29
- searchParams: URLSearchParams,
30
- paramsSchema: T,
31
- ): z.infer<T> {
32
- const obj = qs.parse(searchParams.toString());
33
- return caster(paramsSchema, obj);
34
- }
35
-
36
- export function paramsToSearchParams<T>(params: T): {
37
- [key in string]: string | string[];
38
- } {
39
- return Object.fromEntries(
40
- // biome-ignore lint/complexity/useFlatMap: 여기는 flatMap 사용하면 깨짐
41
- Object.entries(params as any)
42
- .filter(([, value]) => {
43
- return value !== undefined;
44
- })
45
- .map(([key, value]) => {
46
- if (Array.isArray(value)) {
47
- return [[`${key}[]`, value]];
48
- } else if (isObject(value)) {
49
- return Object.keys(value).map((subKey) => {
50
- return [`${key}[${subKey}]`, String(value[subKey as keyof typeof value])];
51
- });
52
- } else {
53
- return [[key, String(value)]];
54
- }
55
- })
56
- .flat(),
57
- );
58
- }
59
-
60
- export type ErrorObj = {
61
- content: string;
62
- pointing?: "above" | "below" | "left" | "right";
63
- };
64
-
65
- export function useTypeForm<T extends z.ZodObject<any> | z.ZodArray<any>, U extends z.infer<T>>(
66
- zType: T,
67
- defaultValue: U,
68
- ) {
69
- const [form, setForm] = useState<z.infer<T>>(defaultValue);
70
- const [errorObjs, setErrorObjs] = useState<Map<string, ErrorObj>>(new Map());
71
-
72
- function getEmptyStringTo(zType: T, objPath: string): "normal" | "nullable" | "optional" {
73
- const zTypeObjPath = objPath
74
- .replace(/\./g, ".shape.")
75
- .replace(/\[[^\]]+\]/g, ".element")
76
- .replace(/^\.element/, "element");
77
-
78
- let targetZType: unknown;
79
- if (zType instanceof z.ZodObject) {
80
- targetZType = get(zType.shape, zTypeObjPath);
81
- } else if (zType instanceof z.ZodArray) {
82
- targetZType = get(zType, zTypeObjPath);
83
- }
84
-
85
- if (targetZType === undefined) {
86
- return "normal";
87
- } else if (targetZType instanceof z.ZodOptional) {
88
- return "optional";
89
- } else if (targetZType instanceof z.ZodNullable) {
90
- return "nullable";
91
- }
92
- return "normal";
93
- }
94
-
95
- return {
96
- form,
97
- setForm,
98
- register: (objPath: string, _emptyStringTo?: "normal" | "nullable" | "optional"): any => {
99
- const emptyStringTo = _emptyStringTo ?? getEmptyStringTo(zType, objPath);
100
- const srcValue = get(form, objPath) as unknown;
101
-
102
- const error = errorObjs.get(objPath);
103
-
104
- // 공통 업데이트 로직
105
- const updateValue = (newValue: any) => {
106
- if (error !== undefined) {
107
- setErrorObjs((p) => {
108
- const newP = new Map(p);
109
- newP.delete(objPath);
110
- return newP;
111
- });
112
- }
113
-
114
- let processedValue = newValue;
115
- if (emptyStringTo === "nullable") {
116
- processedValue = newValue === "" ? null : newValue;
117
- } else if (emptyStringTo === "optional") {
118
- processedValue = newValue === "" ? undefined : newValue;
119
- }
120
-
121
- setForm(set(form, objPath, processedValue));
122
- };
123
-
124
- const result: Record<string, any> = {
125
- value: srcValue === undefined || srcValue === null ? "" : srcValue,
126
- onValueChange: (value: any) => updateValue(value),
127
- };
128
-
129
- // error가 있으면 추가
130
- if (error) {
131
- result.error = error;
132
- }
133
-
134
- return result;
135
- },
136
- addError: (objPath: string, errorMessage: string | ErrorObj): void => {
137
- setErrorObjs((p) => {
138
- const newP = new Map(p);
139
- newP.set(
140
- objPath,
141
- typeof errorMessage === "string" ? { content: errorMessage } : errorMessage,
142
- );
143
- return newP;
144
- });
145
- },
146
- removeError: (objPath: string): void => {
147
- setErrorObjs((p) => {
148
- const newP = new Map(p);
149
- newP.delete(objPath);
150
- return newP;
151
- });
152
- },
153
- clearError: (): void => {
154
- setErrorObjs(new Map());
155
- },
156
- reset: (): void => {
157
- setForm(defaultValue);
158
- },
159
- };
160
- }
161
-
162
- type ZodKeys<T extends z.ZodType<any>> = keyof z.infer<T>;
163
-
164
- export function useListParams<U extends z.ZodType<any>, T extends Partial<z.infer<U>>>(
165
- zType: U,
166
- defaultValue: T,
167
- options?: {
168
- disableSearchParams?: boolean;
169
- },
170
- ) {
171
- const search = useSearch({ strict: false }) as Record<string, unknown>;
172
- const navigate = useNavigate();
173
-
174
- // URL에서 파라미터 파싱
175
- const listParams = (() => {
176
- if (options?.disableSearchParams) {
177
- return defaultValue;
178
- }
179
-
180
- const parsed = zType.safeParse(search);
181
- if (parsed.success) {
182
- return { ...defaultValue, ...parsed.data };
183
- }
184
- return defaultValue;
185
- })() as T;
186
-
187
- const setListParams = (newParams: T) => {
188
- if (equal(listParams, newParams)) {
189
- return;
190
- }
191
-
192
- navigate({
193
- search: newParams as any,
194
- });
195
- };
196
-
197
- // 함수 오버로드
198
- function register(name: "page"): { value: number; onValueChange: (page: number) => void };
199
- function register(name: Exclude<ZodKeys<U>, "page">): {
200
- value: string;
201
- onValueChange: (value: any) => void;
202
- };
203
- function register(name: ZodKeys<U>) {
204
- if (name === "page") {
205
- return {
206
- value: (listParams as any).page ?? 1,
207
- onValueChange: (page: number) => {
208
- setListParams({ ...listParams, page } as T);
209
- },
210
- };
211
- } else {
212
- const currentValue = (listParams as any)[name];
213
- return {
214
- value: currentValue ?? "",
215
- onValueChange: (value: any) => {
216
- setListParams({
217
- ...listParams,
218
- page: 1,
219
- [name]: value === "" ? undefined : value,
220
- } as T);
221
- },
222
- };
223
- }
224
- }
225
-
226
- return {
227
- listParams,
228
- setListParams,
229
- register,
230
- };
231
- }
232
-
233
- export function useGoBack() {
234
- const router = useRouter();
235
-
236
- const goBack = () => {
237
- router.history.back();
238
- };
239
-
240
- return { goBack };
241
- }
242
-
243
- export function useSelection<T>(allKeys: T[], defaultSelectedKeys: T[] = []) {
244
- const [selection, setSelection] = useState<Map<T, boolean>>(
245
- new Map(allKeys.map((key) => [key, defaultSelectedKeys.includes(key)])),
246
- );
247
- const [lastIndex, setLastIndex] = useState<number>(0);
248
-
249
- // 전체 키가 바뀔 때마다 validation하여 갱신된 전체 키에 포함된 키만 유지
250
- useEffect(() => {
251
- const selectionKeys = Array.from(selection.keys());
252
- if (intersection(allKeys, selectionKeys).length === selectionKeys.length) {
253
- return;
254
- }
255
-
256
- setSelection(new Map(Array.from(selection).filter(([key, _value]) => allKeys.includes(key))));
257
- }, [allKeys, selection]);
258
-
259
- const selectedKeys = Array.from(selection)
260
- .filter(([key, value]) => allKeys.includes(key) && value === true)
261
- .map(([key]) => key);
262
-
263
- return {
264
- getSelected: (key: T) => selection.get(key) ?? false,
265
- toggle: (key: T) => {
266
- setSelection((selection) => {
267
- return new Map([...selection, [key, !(selection.get(key) ?? false)]]);
268
- });
269
- },
270
- selectedKeys,
271
- deselectAll: () => setSelection(new Map(allKeys.map((key) => [key, false]))),
272
- selectAll: () => setSelection(new Map(allKeys.map((key) => [key, true]))),
273
- isAllSelected: selectedKeys.length === allKeys.length,
274
- handleCheckboxClick: (e: React.MouseEvent<HTMLInputElement, MouseEvent>, index: number) => {
275
- const input = e.currentTarget.getElementsByTagName("input");
276
- if (e.shiftKey && input[0]?.checked === false) {
277
- const [begin, end] = (() => {
278
- if (lastIndex < index) {
279
- return [lastIndex, index];
280
- } else {
281
- return [index + 1, lastIndex];
282
- }
283
- })();
284
- setSelection(
285
- new Map(unique([...selectedKeys, ...allKeys.slice(begin, end)]).map((k: T) => [k, true])),
286
- );
287
- } else {
288
- setLastIndex(index);
289
- }
290
- },
291
- };
292
- }
293
-
294
- export function sqlDateToDateString(sqlDateString: string | null) {
295
- if (sqlDateString === null) {
296
- return null;
297
- } else {
298
- return sqlDateString.slice(0, 10);
299
- }
300
- }
301
-
302
- export function numF(num: number | null | undefined): string | number | undefined | null {
303
- return num && new Intl.NumberFormat().format(num);
304
- }
305
-
306
- export function dateF(dateValue: string | Date | null | undefined): string | null {
307
- if (dateValue === null || dateValue === undefined) {
308
- return null;
309
- } else if (dateValue instanceof Date) {
310
- return dateValue.toISOString().slice(0, 10);
311
- } else {
312
- return dateValue.slice(0, 10);
313
- }
314
- }
315
- export function datetimeF(dateValue: string | Date | null | undefined): string | null {
316
- if (dateValue === null || dateValue === undefined) {
317
- return null;
318
- } else if (dateValue instanceof Date) {
319
- return dateValue.toISOString().slice(0, 19).replace("T", " ");
320
- } else {
321
- return dateValue.slice(0, 19);
322
- }
323
- }
324
-
325
- export function arrayableToArray<T extends number | string | boolean>(
326
- val: T | T[] | undefined,
327
- ): T[] {
328
- return val ? (Array.isArray(val) ? val : [val]) : [];
329
- }
330
-
331
- export type ControlledModalProps = {
332
- open: boolean;
333
- close: () => void;
334
- };
335
-
336
- export function useModal<T extends object>(
337
- ModalComponent: (props: T & ControlledModalProps) => React.ReactElement,
338
- defaultProps: T,
339
- ) {
340
- const [modalProps, setModalProps] = useState<T & { open: boolean }>({
341
- ...defaultProps,
342
- open: false,
343
- });
344
-
345
- const close = () => {
346
- setModalProps({
347
- ...modalProps,
348
- open: false,
349
- });
350
- };
351
-
352
- return {
353
- open: (newProps: T) => {
354
- setModalProps({
355
- ...newProps,
356
- open: true,
357
- close,
358
- });
359
- },
360
- modal: React.createElement(ModalComponent, {
361
- ...modalProps,
362
- close,
363
- }),
364
- };
365
- }
366
-
367
- export function caller<T extends Function>() {
368
- let savedFunc: T | null = null;
369
- return {
370
- set: (func: T) => {
371
- savedFunc = func;
372
- },
373
- call: ((...args: unknown[]) => {
374
- if (savedFunc) {
375
- savedFunc.call(args);
376
- }
377
- }) as unknown as T,
378
- };
379
- }
380
-
381
- export type SonamuCol<T> = {
382
- label: string;
383
- th?: ReactElement;
384
- tc: (row: T, index: number) => ReactElement;
385
- className?: string;
386
- collapsing?: boolean;
387
- width?: TableColumnWidth;
388
- hidden?: boolean;
389
- parentLabel?: string;
390
- };
391
-
392
- export type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
393
-
394
- export type Override<T, U> = Omit<T, keyof U> & U;
package/src/lib/index.ts DELETED
@@ -1,31 +0,0 @@
1
- export { Pagination } from "../components/ui/pagination";
2
- export { caster, fastifyCaster } from "./caster";
3
- export {
4
- arrayableToArray,
5
- // Types
6
- type ControlledModalProps,
7
- caller,
8
- type DistributiveOmit,
9
- dateF,
10
- datetimeF,
11
- type ErrorObj,
12
- // Utilities
13
- hidden,
14
- numF,
15
- type Override,
16
- type PaginationProps,
17
- paramsToSearchParams,
18
- type SonamuCol,
19
- searchParamsToParams,
20
- sqlDateToDateString,
21
- type TableColumnWidth,
22
- // Navigation & Selection
23
- useGoBack,
24
- useListParams,
25
- useModal,
26
- useSelection,
27
- useTypeForm,
28
- } from "./helpers";
29
- export { lazyUpload } from "./lazy-upload";
30
- export { useIsMobile } from "./use-mobile";
31
- export { cn } from "./utils";
@@ -1,19 +0,0 @@
1
- import * as React from "react";
2
-
3
- const MOBILE_BREAKPOINT = 768;
4
-
5
- export function useIsMobile() {
6
- const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
7
-
8
- React.useEffect(() => {
9
- const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
10
- const onChange = () => {
11
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
- };
13
- mql.addEventListener("change", onChange);
14
- setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
15
- return () => mql.removeEventListener("change", onChange);
16
- }, []);
17
-
18
- return !!isMobile;
19
- }
package/src/lib/utils.ts DELETED
@@ -1,6 +0,0 @@
1
- import { type ClassValue, clsx } from "clsx";
2
- import { twMerge } from "tailwind-merge";
3
-
4
- export function cn(...inputs: ClassValue[]) {
5
- return twMerge(clsx(inputs));
6
- }