@mbao01/common 0.0.32 → 0.0.34
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/dist/types/components/FileUploader/FileUploader.d.ts +11 -0
- package/dist/types/components/FileUploader/FileUploaderContext.d.ts +3 -0
- package/dist/types/components/FileUploader/index.d.ts +1 -0
- package/dist/types/components/FileUploader/types.d.ts +20 -0
- package/dist/types/components/FileUploader/useFileUpload.d.ts +1 -0
- package/dist/types/components/Form/OtpInput/OtpInput.d.ts +2 -0
- package/dist/types/components/Form/OtpInput/index.d.ts +1 -0
- package/dist/types/components/Form/OtpInput/types.d.ts +6 -0
- package/dist/types/components/Form/index.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/utilities/theme.d.ts +1 -0
- package/package.json +5 -2
- package/src/components/FileUploader/FileUploader.tsx +309 -0
- package/src/components/FileUploader/FileUploaderContext.tsx +5 -0
- package/src/components/FileUploader/index.ts +1 -0
- package/src/components/FileUploader/types.ts +23 -0
- package/src/components/FileUploader/useFileUpload.ts +10 -0
- package/src/components/Form/OtpInput/OtpInput.tsx +34 -0
- package/src/components/Form/OtpInput/index.ts +1 -0
- package/src/components/Form/OtpInput/types.ts +7 -0
- package/src/components/Form/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/utilities/theme.ts +10 -8
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { FileUploaderProps } from "./types";
|
|
3
|
+
export declare const FileUploader: {
|
|
4
|
+
({ className, dropzoneOptions, value, onValueChange, reSelect, orientation, children, dir, ...props }: FileUploaderProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
displayName: string;
|
|
6
|
+
Content: import("react").ForwardRefExoticComponent<import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
7
|
+
Input: import("react").ForwardRefExoticComponent<import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
8
|
+
Item: import("react").ForwardRefExoticComponent<{
|
|
9
|
+
index: number;
|
|
10
|
+
} & import("react").HTMLAttributes<HTMLDivElement> & import("react").RefAttributes<HTMLDivElement>>;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FileUploader } from "./FileUploader";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Dispatch, SetStateAction } from "react";
|
|
2
|
+
import type { DropzoneState, DropzoneOptions } from "react-dropzone";
|
|
3
|
+
export type FileUploaderProps = {
|
|
4
|
+
value: File[] | null;
|
|
5
|
+
reSelect?: boolean;
|
|
6
|
+
onValueChange: (value: File[] | null) => void;
|
|
7
|
+
dropzoneOptions: DropzoneOptions;
|
|
8
|
+
orientation?: "horizontal" | "vertical";
|
|
9
|
+
} & React.HTMLAttributes<HTMLDivElement>;
|
|
10
|
+
export type DirectionOptions = "rtl" | "ltr" | undefined;
|
|
11
|
+
export type FileUploaderContextType = {
|
|
12
|
+
dropzoneState: DropzoneState;
|
|
13
|
+
isLOF: boolean;
|
|
14
|
+
isFileTooBig: boolean;
|
|
15
|
+
removeFileFromSet: (index: number) => void;
|
|
16
|
+
activeIndex: number;
|
|
17
|
+
setActiveIndex: Dispatch<SetStateAction<number>>;
|
|
18
|
+
orientation: "horizontal" | "vertical";
|
|
19
|
+
direction: DirectionOptions;
|
|
20
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const useFileUpload: () => import("./types").FileUploaderContextType;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OtpInput } from "./OtpInput";
|
package/dist/types/index.d.ts
CHANGED
|
@@ -24,6 +24,7 @@ export * from "./components/Table";
|
|
|
24
24
|
export * from "./components/Tabs";
|
|
25
25
|
export * from "./components/Text";
|
|
26
26
|
/** data input */
|
|
27
|
+
export * from "./components/FileUploader";
|
|
27
28
|
export * from "./components/Form";
|
|
28
29
|
export * from "./components/Combobox";
|
|
29
30
|
export * from "./components/DatePicker";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mbao01/common",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Ayomide Bakare",
|
|
7
7
|
"license": "MIT",
|
|
@@ -91,10 +91,13 @@
|
|
|
91
91
|
"date-fns": "^3.6.0",
|
|
92
92
|
"embla-carousel-react": "^8.0.2",
|
|
93
93
|
"react-day-picker": "^8.10.0",
|
|
94
|
+
"react-dropzone": "^14.2.3",
|
|
94
95
|
"react-international-phone": "^4.2.6",
|
|
96
|
+
"react-otp-input": "^3.1.1",
|
|
95
97
|
"sonner": "^1.4.41",
|
|
96
98
|
"tailwind-merge": "^2.2.1",
|
|
97
99
|
"tailwindcss-animate": "^1.0.7",
|
|
100
|
+
"universal-cookie": "^7.1.4",
|
|
98
101
|
"vaul": "^0.9.0"
|
|
99
102
|
},
|
|
100
103
|
"devDependencies": {
|
|
@@ -142,5 +145,5 @@
|
|
|
142
145
|
"react-dom": "^18.2.0",
|
|
143
146
|
"typescript": "^5.2.2"
|
|
144
147
|
},
|
|
145
|
-
"gitHead": "
|
|
148
|
+
"gitHead": "cc3e285416d8c9184e0bf597400550d08847001e"
|
|
146
149
|
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { forwardRef, useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useDropzone, FileRejection } from "react-dropzone";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
import { TrashIcon } from "@radix-ui/react-icons";
|
|
7
|
+
import type { DirectionOptions, FileUploaderProps } from "./types";
|
|
8
|
+
import { cn } from "../../utilities";
|
|
9
|
+
import { FileUploaderContext } from "./FileUploaderContext";
|
|
10
|
+
import { useFileUpload } from "./useFileUpload";
|
|
11
|
+
|
|
12
|
+
export const FileUploader = ({
|
|
13
|
+
className,
|
|
14
|
+
dropzoneOptions,
|
|
15
|
+
value,
|
|
16
|
+
onValueChange,
|
|
17
|
+
reSelect,
|
|
18
|
+
orientation = "vertical",
|
|
19
|
+
children,
|
|
20
|
+
dir,
|
|
21
|
+
...props
|
|
22
|
+
}: FileUploaderProps) => {
|
|
23
|
+
const [isFileTooBig, setIsFileTooBig] = useState(false);
|
|
24
|
+
const [isLOF, setIsLOF] = useState(false);
|
|
25
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
26
|
+
const {
|
|
27
|
+
accept = {
|
|
28
|
+
"image/*": [".jpg", ".jpeg", ".png", ".gif"],
|
|
29
|
+
},
|
|
30
|
+
maxFiles = 1,
|
|
31
|
+
maxSize = 4 * 1024 * 1024,
|
|
32
|
+
multiple = true,
|
|
33
|
+
} = dropzoneOptions;
|
|
34
|
+
|
|
35
|
+
const reSelectAll = maxFiles === 1 ? true : reSelect;
|
|
36
|
+
const direction: DirectionOptions = dir === "rtl" ? "rtl" : "ltr";
|
|
37
|
+
|
|
38
|
+
const removeFileFromSet = useCallback(
|
|
39
|
+
(i: number) => {
|
|
40
|
+
if (!value) return;
|
|
41
|
+
const newFiles = value.filter((_, index) => index !== i);
|
|
42
|
+
onValueChange(newFiles);
|
|
43
|
+
},
|
|
44
|
+
[value, onValueChange]
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const handleKeyDown = useCallback(
|
|
48
|
+
(e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
e.stopPropagation();
|
|
51
|
+
|
|
52
|
+
if (!value) return;
|
|
53
|
+
|
|
54
|
+
const moveNext = () => {
|
|
55
|
+
const nextIndex = activeIndex + 1;
|
|
56
|
+
setActiveIndex(nextIndex > value.length - 1 ? 0 : nextIndex);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const movePrev = () => {
|
|
60
|
+
const nextIndex = activeIndex - 1;
|
|
61
|
+
setActiveIndex(nextIndex < 0 ? value.length - 1 : nextIndex);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const prevKey =
|
|
65
|
+
orientation === "horizontal"
|
|
66
|
+
? direction === "ltr"
|
|
67
|
+
? "ArrowLeft"
|
|
68
|
+
: "ArrowRight"
|
|
69
|
+
: "ArrowUp";
|
|
70
|
+
|
|
71
|
+
const nextKey =
|
|
72
|
+
orientation === "horizontal"
|
|
73
|
+
? direction === "ltr"
|
|
74
|
+
? "ArrowRight"
|
|
75
|
+
: "ArrowLeft"
|
|
76
|
+
: "ArrowDown";
|
|
77
|
+
|
|
78
|
+
if (e.key === nextKey) {
|
|
79
|
+
moveNext();
|
|
80
|
+
} else if (e.key === prevKey) {
|
|
81
|
+
movePrev();
|
|
82
|
+
} else if (e.key === "Enter" || e.key === "Space") {
|
|
83
|
+
if (activeIndex === -1) {
|
|
84
|
+
dropzoneState.inputRef.current?.click();
|
|
85
|
+
}
|
|
86
|
+
} else if (e.key === "Delete" || e.key === "Backspace") {
|
|
87
|
+
if (activeIndex !== -1) {
|
|
88
|
+
removeFileFromSet(activeIndex);
|
|
89
|
+
if (value.length - 1 === 0) {
|
|
90
|
+
setActiveIndex(-1);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
movePrev();
|
|
94
|
+
}
|
|
95
|
+
} else if (e.key === "Escape") {
|
|
96
|
+
setActiveIndex(-1);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
100
|
+
[value, activeIndex, removeFileFromSet]
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const onDrop = useCallback(
|
|
104
|
+
(acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
|
|
105
|
+
const files = acceptedFiles;
|
|
106
|
+
|
|
107
|
+
if (!files) {
|
|
108
|
+
toast.error("file error , probably too big");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const newValues: File[] = value ? [...value] : [];
|
|
113
|
+
|
|
114
|
+
if (reSelectAll) {
|
|
115
|
+
newValues.splice(0, newValues.length);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
files.forEach((file) => {
|
|
119
|
+
if (newValues.length < maxFiles) {
|
|
120
|
+
newValues.push(file);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
onValueChange(newValues);
|
|
125
|
+
|
|
126
|
+
if (rejectedFiles.length > 0) {
|
|
127
|
+
for (const rejectedFile of rejectedFiles) {
|
|
128
|
+
if (rejectedFile.errors[0]?.code === "file-too-large") {
|
|
129
|
+
toast.error(
|
|
130
|
+
`File is too large. Max size is ${maxSize / 1024 / 1024}MB`
|
|
131
|
+
);
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
if (rejectedFile.errors[0]?.message) {
|
|
135
|
+
toast.error(rejectedFile.errors[0].message);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
142
|
+
[reSelectAll, value]
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (!value) return;
|
|
147
|
+
if (value.length === maxFiles) {
|
|
148
|
+
setIsLOF(true);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
setIsLOF(false);
|
|
152
|
+
}, [value, maxFiles]);
|
|
153
|
+
|
|
154
|
+
const opts = dropzoneOptions
|
|
155
|
+
? dropzoneOptions
|
|
156
|
+
: { accept, maxFiles, maxSize, multiple };
|
|
157
|
+
|
|
158
|
+
const dropzoneState = useDropzone({
|
|
159
|
+
...opts,
|
|
160
|
+
onDrop,
|
|
161
|
+
onDropRejected: () => setIsFileTooBig(true),
|
|
162
|
+
onDropAccepted: () => setIsFileTooBig(false),
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<FileUploaderContext.Provider
|
|
167
|
+
value={{
|
|
168
|
+
dropzoneState,
|
|
169
|
+
isLOF,
|
|
170
|
+
isFileTooBig,
|
|
171
|
+
removeFileFromSet,
|
|
172
|
+
activeIndex,
|
|
173
|
+
setActiveIndex,
|
|
174
|
+
orientation,
|
|
175
|
+
direction,
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
<div
|
|
179
|
+
tabIndex={0}
|
|
180
|
+
onKeyDownCapture={handleKeyDown}
|
|
181
|
+
className={cn(
|
|
182
|
+
"grid w-full focus:outline-none overflow-hidden ",
|
|
183
|
+
className,
|
|
184
|
+
{
|
|
185
|
+
"gap-2": value && value.length > 0,
|
|
186
|
+
}
|
|
187
|
+
)}
|
|
188
|
+
dir={dir}
|
|
189
|
+
{...props}
|
|
190
|
+
>
|
|
191
|
+
{children}
|
|
192
|
+
</div>
|
|
193
|
+
</FileUploaderContext.Provider>
|
|
194
|
+
);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
FileUploader.displayName = "FileUploader";
|
|
198
|
+
|
|
199
|
+
const FileUploaderContent = forwardRef<
|
|
200
|
+
HTMLDivElement,
|
|
201
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
202
|
+
>(({ children, className, ...props }, ref) => {
|
|
203
|
+
const { orientation } = useFileUpload();
|
|
204
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<div
|
|
208
|
+
className={cn("w-full px-1")}
|
|
209
|
+
ref={containerRef}
|
|
210
|
+
aria-description="content file holder"
|
|
211
|
+
>
|
|
212
|
+
<div
|
|
213
|
+
{...props}
|
|
214
|
+
ref={ref}
|
|
215
|
+
className={cn(
|
|
216
|
+
"flex rounded-xl gap-1",
|
|
217
|
+
orientation === "horizontal" ? "flex-raw flex-wrap" : "flex-col",
|
|
218
|
+
className
|
|
219
|
+
)}
|
|
220
|
+
>
|
|
221
|
+
{children}
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
FileUploaderContent.displayName = "FileUploaderContent";
|
|
228
|
+
|
|
229
|
+
const FileUploaderItem = forwardRef<
|
|
230
|
+
HTMLDivElement,
|
|
231
|
+
{ index: number } & React.HTMLAttributes<HTMLDivElement>
|
|
232
|
+
>(({ className, index, children, ...props }, ref) => {
|
|
233
|
+
const { removeFileFromSet, activeIndex, direction } = useFileUpload();
|
|
234
|
+
const isSelected = index === activeIndex;
|
|
235
|
+
return (
|
|
236
|
+
<div
|
|
237
|
+
ref={ref}
|
|
238
|
+
className={cn(
|
|
239
|
+
"h-6 p-1 justify-between cursor-pointer relative",
|
|
240
|
+
className,
|
|
241
|
+
isSelected ? "bg-muted" : ""
|
|
242
|
+
)}
|
|
243
|
+
{...props}
|
|
244
|
+
>
|
|
245
|
+
<div className="text-sm leading-none tracking-tight flex items-center gap-1.5 h-full w-full">
|
|
246
|
+
{children}
|
|
247
|
+
</div>
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
className={cn(
|
|
251
|
+
"absolute",
|
|
252
|
+
direction === "rtl" ? "top-1 left-1" : "top-1 right-1"
|
|
253
|
+
)}
|
|
254
|
+
onClick={() => removeFileFromSet(index)}
|
|
255
|
+
>
|
|
256
|
+
<span className="sr-only">remove item {index}</span>
|
|
257
|
+
<TrashIcon className="w-4 h-4 hover:stroke-destructive duration-200 ease-in-out" />
|
|
258
|
+
</button>
|
|
259
|
+
</div>
|
|
260
|
+
);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
FileUploaderItem.displayName = "FileUploaderItem";
|
|
264
|
+
|
|
265
|
+
const FileUploaderInput = forwardRef<
|
|
266
|
+
HTMLDivElement,
|
|
267
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
268
|
+
>(({ className, children, ...props }, ref) => {
|
|
269
|
+
const { dropzoneState, isFileTooBig, isLOF } = useFileUpload();
|
|
270
|
+
const rootProps = isLOF ? {} : dropzoneState.getRootProps();
|
|
271
|
+
return (
|
|
272
|
+
<div
|
|
273
|
+
ref={ref}
|
|
274
|
+
{...props}
|
|
275
|
+
className={`relative w-full ${
|
|
276
|
+
isLOF ? "opacity-50 cursor-not-allowed " : "cursor-pointer "
|
|
277
|
+
}`}
|
|
278
|
+
>
|
|
279
|
+
<div
|
|
280
|
+
className={cn(
|
|
281
|
+
`w-full rounded-lg duration-300 ease-in-out
|
|
282
|
+
${
|
|
283
|
+
dropzoneState.isDragAccept
|
|
284
|
+
? "border-green-500"
|
|
285
|
+
: dropzoneState.isDragReject || isFileTooBig
|
|
286
|
+
? "border-red-500"
|
|
287
|
+
: "border-gray-300"
|
|
288
|
+
}`,
|
|
289
|
+
className
|
|
290
|
+
)}
|
|
291
|
+
{...rootProps}
|
|
292
|
+
>
|
|
293
|
+
{children}
|
|
294
|
+
</div>
|
|
295
|
+
<input
|
|
296
|
+
ref={dropzoneState.inputRef}
|
|
297
|
+
disabled={isLOF}
|
|
298
|
+
{...dropzoneState.getInputProps()}
|
|
299
|
+
className={`${isLOF ? "cursor-not-allowed" : ""}`}
|
|
300
|
+
/>
|
|
301
|
+
</div>
|
|
302
|
+
);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
FileUploaderInput.displayName = "FileInput";
|
|
306
|
+
|
|
307
|
+
FileUploader.Content = FileUploaderContent;
|
|
308
|
+
FileUploader.Input = FileUploaderInput;
|
|
309
|
+
FileUploader.Item = FileUploaderItem;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FileUploader } from "./FileUploader";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Dispatch, SetStateAction } from "react";
|
|
2
|
+
import type { DropzoneState, DropzoneOptions } from "react-dropzone";
|
|
3
|
+
|
|
4
|
+
export type FileUploaderProps = {
|
|
5
|
+
value: File[] | null;
|
|
6
|
+
reSelect?: boolean;
|
|
7
|
+
onValueChange: (value: File[] | null) => void;
|
|
8
|
+
dropzoneOptions: DropzoneOptions;
|
|
9
|
+
orientation?: "horizontal" | "vertical";
|
|
10
|
+
} & React.HTMLAttributes<HTMLDivElement>;
|
|
11
|
+
|
|
12
|
+
export type DirectionOptions = "rtl" | "ltr" | undefined;
|
|
13
|
+
|
|
14
|
+
export type FileUploaderContextType = {
|
|
15
|
+
dropzoneState: DropzoneState;
|
|
16
|
+
isLOF: boolean;
|
|
17
|
+
isFileTooBig: boolean;
|
|
18
|
+
removeFileFromSet: (index: number) => void;
|
|
19
|
+
activeIndex: number;
|
|
20
|
+
setActiveIndex: Dispatch<SetStateAction<number>>;
|
|
21
|
+
orientation: "horizontal" | "vertical";
|
|
22
|
+
direction: DirectionOptions;
|
|
23
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { useContext } from "react";
|
|
2
|
+
import { FileUploaderContext } from "./FileUploaderContext";
|
|
3
|
+
|
|
4
|
+
export const useFileUpload = () => {
|
|
5
|
+
const context = useContext(FileUploaderContext);
|
|
6
|
+
if (!context) {
|
|
7
|
+
throw new Error("useFileUpload must be used within a FileUploaderProvider");
|
|
8
|
+
}
|
|
9
|
+
return context;
|
|
10
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import OTPInput from "react-otp-input";
|
|
2
|
+
import { type OtpInputProps } from "./types";
|
|
3
|
+
import { Input } from "../Input";
|
|
4
|
+
import { cn } from "../../../utilities";
|
|
5
|
+
import { useState } from "react";
|
|
6
|
+
|
|
7
|
+
export const OtpInput = ({
|
|
8
|
+
className,
|
|
9
|
+
inputProps,
|
|
10
|
+
...props
|
|
11
|
+
}: OtpInputProps) => {
|
|
12
|
+
const [otp, setOtp] = useState("");
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<OTPInput
|
|
16
|
+
{...props}
|
|
17
|
+
value={otp}
|
|
18
|
+
onChange={setOtp}
|
|
19
|
+
renderInput={(renderProps) => (
|
|
20
|
+
<Input
|
|
21
|
+
{...inputProps}
|
|
22
|
+
{...renderProps}
|
|
23
|
+
className={cn(
|
|
24
|
+
"!w-12 !appearance-none selection:bg-base text-base-content",
|
|
25
|
+
className
|
|
26
|
+
)}
|
|
27
|
+
/>
|
|
28
|
+
)}
|
|
29
|
+
containerStyle={`flex justify-center items-center flex-wrap text-2xl font-bold ${
|
|
30
|
+
props.renderSeparator ? "gap-1" : "gap-x-3 gap-y-2"
|
|
31
|
+
}`}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OtpInput } from "./OtpInput";
|
package/src/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export * from "./components/Tabs";
|
|
|
26
26
|
export * from "./components/Text";
|
|
27
27
|
|
|
28
28
|
/** data input */
|
|
29
|
+
export * from "./components/FileUploader";
|
|
29
30
|
export * from "./components/Form";
|
|
30
31
|
export * from "./components/Combobox";
|
|
31
32
|
export * from "./components/DatePicker";
|
package/src/utilities/theme.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
+
import Cookies from "universal-cookie";
|
|
2
|
+
|
|
1
3
|
export type Theme = "dark" | "light";
|
|
2
4
|
|
|
5
|
+
export const THEME_COOKIE_NAME = "data-theme";
|
|
6
|
+
|
|
3
7
|
export const getTheme = () => {
|
|
4
8
|
if (typeof window === "undefined") return null;
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
? "dark"
|
|
10
|
-
: "light";
|
|
11
|
-
return t;
|
|
10
|
+
const cookies = new Cookies();
|
|
11
|
+
const theme = cookies.get(THEME_COOKIE_NAME) as Theme;
|
|
12
|
+
return theme;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export const saveTheme = (theme: Theme) => {
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const cookies = new Cookies();
|
|
17
|
+
cookies.set(THEME_COOKIE_NAME, theme, { secure: true });
|
|
18
|
+
document.body.setAttribute(THEME_COOKIE_NAME, theme);
|
|
17
19
|
};
|