naria-ui 0.1.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.
@@ -0,0 +1,124 @@
1
+ "use client"
2
+ import {FC, useEffect, useRef, useState} from "react";
3
+ import CloudArrowUp from '@/assets/icons/cloud-arrow-up.svg'
4
+ import LibButton from "@/components/lib/lib-button/LibButton";
5
+ import convertFileSize from "@/utils/convert-file-size";
6
+ import Loading from "@/components/shared/loading/Loading";
7
+
8
+ export interface props {
9
+ isLoading: boolean;
10
+ onFileChange?: any;
11
+ }
12
+
13
+ const LibFileUploader: FC<props> = ({isLoading = true, onFileChange}) => {
14
+ const labelRef = useRef(null);
15
+ const [isDragging, setIsDragging] = useState(false);
16
+ const [selectedFile, setSelectedFile] = useState(null);
17
+
18
+ const handleDragEnter = (e) => {
19
+ e.preventDefault();
20
+ setIsDragging(true);
21
+ };
22
+
23
+ const handleDragLeave = (e) => {
24
+ e.preventDefault();
25
+ setIsDragging(false);
26
+ };
27
+
28
+ const handleDragOver = (e) => {
29
+ e.preventDefault();
30
+ setIsDragging(true);
31
+ };
32
+
33
+ const handleDrop = (e) => {
34
+ e.preventDefault();
35
+ if (isLoading) {
36
+ return;
37
+ }
38
+ setIsDragging(false);
39
+ const file = e?.target?.files ? e?.target?.files[0] : e?.dataTransfer?.files[0];
40
+ if (file) {
41
+ setSelectedFile(file);
42
+ }
43
+ };
44
+ const onCancel = () => {
45
+ setSelectedFile(null)
46
+ }
47
+ const onUpload = () => {
48
+ onFileChange(selectedFile)
49
+ }
50
+ useEffect(() => {
51
+ if(!isLoading) {
52
+ setSelectedFile(null);
53
+ }
54
+ }, [isLoading])
55
+ return (
56
+
57
+ <div className="relative border border-dashed border-secondary-100/40 rounded-2xl overflow-hidden">
58
+ <label
59
+ ref={labelRef}
60
+ className={`py-10 px-6 block ${isDragging ? 'border-secondary-100/100 bg-secondary-100/5' : ''}`}
61
+ onDragEnter={handleDragEnter}
62
+ onDragLeave={handleDragLeave}
63
+ onDragOver={handleDragOver}
64
+ onDrop={handleDrop}
65
+ >
66
+ <div className="flex flex-col md:flex-row justify-center items-center gap-16">
67
+ <div className="flex gap-5 items-center flex-col md:flex-row">
68
+ <CloudArrowUp className="w-10 mt-2.5 shrink-0"/>
69
+ <div>
70
+ <p className="text-base font-bold">جهت انتخاب فایل کلیک کرده یا فایل مورد نظر را اینجا رها
71
+ کنید.</p>
72
+ <p className="text-sm text-secondary-100 mt-2">فایل‌های قابل انتخاب: PNG، JPEG، MP4</p>
73
+ <p className="text-sm text-secondary-100 mt-2">حداکثر حجم مجاز: 2 مگابایت</p>
74
+ </div>
75
+ </div>
76
+
77
+ <input type="file" accept="image/png, image/jpeg, image/jpg, video/mp4"
78
+ value=""
79
+ onChange={handleDrop} className="hidden"/>
80
+ <div className="sm:max-w-[120px] w-full">
81
+ <LibButton theme="btn-info-light" onClick={() => labelRef.current.click()} value="انتخاب فایل"/>
82
+ </div>
83
+
84
+ </div>
85
+
86
+
87
+ </label>
88
+ {
89
+ selectedFile && !isLoading ? (
90
+ <div className="absolute bg-light-100 top-0 right-0 w-full h-full flex items-center">
91
+ <div className="text-sm mx-auto w-full max-w-[250px]">
92
+ <p className="flex gap-1"><span className="text-secondary-100 shrink-0">نام فایل:</span>
93
+ <span
94
+ className="inline-block ltr text-ellipsis overflow-hidden truncate">{selectedFile?.name}</span>
95
+ </p>
96
+ <p><span
97
+ className="text-secondary-100">حجم فایل:</span> {convertFileSize(selectedFile?.size)}
98
+ </p>
99
+ <div className="flex mt-4 gap-2">
100
+
101
+ <div className="grow">
102
+ <LibButton value="انصراف" size="btn-sm" theme="btn-danger-light" onClick={onCancel}/>
103
+ </div>
104
+ <div className="grow">
105
+ <LibButton value="تایید" size="btn-sm" theme="btn-success" onClick={onUpload}/>
106
+ </div>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ ) : undefined
111
+ }
112
+ {
113
+ selectedFile && isLoading ? (
114
+ <div className="absolute bg-light-100 top-0 right-0 w-full h-full flex items-center gap-3 justify-center">
115
+ <Loading size="w-6.5 h-6.5" /> <span>در حال بارگزاری...</span>
116
+ </div>
117
+ ) : undefined
118
+ }
119
+
120
+ </div>
121
+ );
122
+ };
123
+
124
+ export default LibFileUploader;
@@ -0,0 +1,198 @@
1
+ "use client"
2
+ import {FC, useEffect, useState} from "react";
3
+ import ArrowRight from "@/assets/icons/angle-right.svg";
4
+ import ArrowLeft from "@/assets/icons/angle-left.svg";
5
+ import {generateUuid} from '@/utils/generate-uuid';
6
+
7
+ export interface props {
8
+ theme?: "primary" | "secondary" | "warning" | "infoLight" | "dangerLight" | "infoLink" | "outlineSecondary" | "outlinePrimary";
9
+ activeTheme?: "primary" | "secondary" | "warning" | "infoLight" | "dangerLight" | "infoLink" | "outlineSecondary" | "outlinePrimary";
10
+ size?: "md" | "sm";
11
+ totalCount: number;
12
+ pageSize: number;
13
+ onPageChange: any;
14
+ selectedPage?: any;
15
+ }
16
+
17
+ const sizes = {
18
+ sm: "text-xs h-8 min-w-8",
19
+ md: "text-sm h-10 min-w-10"
20
+ }
21
+ const sizesArrow = {
22
+ sm: "w-2.5",
23
+ md: "w8"
24
+ }
25
+
26
+ export const buttonThemes = {
27
+ primary: `border border-transparent bg-primary-100 text-white hover:bg-primary-200 focus:bg-primary-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
28
+ secondary: `border border-transparent bg-secondary-100 text-white hover:bg-secondary-200 focus:bg-secondary-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
29
+ success: `border border-transparent bg-success-100 text-white hover:bg-success-200 focus:bg-success-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
30
+ warning: `border border-transparent bg-warning-100 text-dark-100 hover:bg-warning-200 focus:bg-warning-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
31
+ infoLight: `border border-transparent bg-info-100/10 text-info-100 hover:brightness-80 focus:brightness-80 hover:border-info-100 focus:border-info-100 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
32
+ dangerLight: `border border-transparent bg-danger-300 text-danger-100 hover:bg-danger-400 focus:bg-danger-400 hover:border-danger-100 focus:border-danger-100 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
33
+ infoLink: `text-info-100 hover:text-info-200 focus:text-info-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
34
+ outlinePrimary: `border border-primary-100 text-primary-100 hover:bg-primary-300 focus:bg-primary-300 disabled:text-grey-300 disabled:cursor-not-allowed`,
35
+ outlineSecondary: `border border-secondary-100 text-secondary-100 enabled:hover:bg-grey-200 focus:bg-grey-200 disabled:text-grey-300 disabled:border-grey-200 disabled:fill-grey-200 disabled:cursor-not-allowed`,
36
+ }
37
+ export const activeThemes = {
38
+ primary: `border border-transparent bg-primary-100 text-white hover:bg-primary-200 focus:bg-primary-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
39
+ secondary: `border border-transparent bg-secondary-100 text-white hover:bg-secondary-200 focus:bg-secondary-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
40
+ success: `border border-transparent bg-success-100 text-white hover:bg-success-200 focus:bg-success-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
41
+ warning: `border border-transparent bg-warning-100 !text-dark-100 hover:bg-warning-200 focus:bg-warning-200 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
42
+ infoLight: `border border-transparent bg-info-100/10 text-info-100 hover:brightness-80 focus:brightness-80 hover:border-info-100 focus:border-info-100 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
43
+ dangerLight: `border border-transparent bg-danger-300 text-danger-100 hover:bg-danger-400 focus:bg-danger-400 hover:border-danger-100 focus:border-danger-100 disabled:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
44
+ infoLink: `text-info-100 hover:text-info-200 focus:text-info-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
45
+ outlinePrimary: `transition duration-200 ease border border-primary-100 text-primary-100 hover:bg-primary-300 focus:bg-primary-300 disabled:text-grey-300 disabled:cursor-not-allowed`,
46
+ outlineSecondary: `border border-secondary-100 text-secondary-100 hover:bg-grey-200 focus:bg-grey-200 disabled:text-grey-300 disabled:cursor-not-allowed`,
47
+ }
48
+ export const dotsThemes = {
49
+ outlineSecondary: `text-secondary-100`,
50
+ }
51
+ export const arrowThemes = {
52
+ outlineSecondary: `fill-secondary-100`,
53
+ }
54
+
55
+ const LibPagination: FC<props> = ({
56
+ totalCount,
57
+ pageSize,
58
+ onPageChange,
59
+ selectedPage,
60
+ activeTheme = "primary",
61
+ theme = "primary",
62
+ size = "md"
63
+ }) => {
64
+ const [selected, setSelected] = useState(1);
65
+ const [pagesCount, setPagesCount] = useState(1);
66
+ const [list, setList] = useState([]);
67
+ const onPaginationClicked = (item: number) => {
68
+ setSelected(item);
69
+ onPageChange(+item);
70
+ };
71
+ useEffect(() => {
72
+ onPagesCountHandler();
73
+ }, [totalCount]);
74
+ useEffect(() => {
75
+ setSelected(1);
76
+ onPagesCountHandler();
77
+ }, [pageSize]);
78
+ const onPagesCountHandler = () => {
79
+ const totalPages =
80
+ totalCount < pageSize ? 1 : Math.ceil(totalCount / pageSize);
81
+ setPagesCount(totalPages);
82
+ };
83
+ useEffect(() => {
84
+ generatePages();
85
+ }, [selected, pagesCount, totalCount]);
86
+ useEffect(() => {
87
+ if (selectedPage) {
88
+ setSelected(selectedPage);
89
+ }
90
+ }, [selectedPage]);
91
+
92
+ const generatePages = () => {
93
+ const list: any = [];
94
+ if (totalCount > 0) {
95
+ list.push(
96
+ <button
97
+ className={`rounded-lg flex items-center justify-center ${buttonThemes[theme]} ${sizes[size]} ${arrowThemes[theme]}`}
98
+ disabled={selected === 1}
99
+ onClick={() => onPaginationClicked(selected - 1)}
100
+ key={generateUuid()}>
101
+ <ArrowRight className={`${sizesArrow[size]}`}/>
102
+ </button>
103
+ );
104
+ if (pagesCount <= 5) {
105
+ for (let i = 1; i <= pagesCount; i++) {
106
+ list.push(
107
+ <button
108
+ className={`rounded-lg flex items-center justify-center ${sizes[size]} ${selected === i ? activeThemes[activeTheme] : buttonThemes[theme]}`}
109
+
110
+ onClick={() => onPaginationClicked(i)}
111
+ key={generateUuid()}
112
+ >
113
+ {i}
114
+ </button>
115
+ );
116
+ }
117
+ } else {
118
+ if (selected > pagesCount - 3) {
119
+ list.push(
120
+ <button
121
+ className={`rounded-lg flex items-center justify-center ${sizes[size]} ${selected === 1 ? activeThemes[activeTheme] : buttonThemes[theme]}`}
122
+ onClick={() => onPaginationClicked(1)}
123
+ key={generateUuid()}
124
+ >
125
+ {1}
126
+ </button>
127
+ );
128
+ list.push(
129
+ <div
130
+ className={`rounded-lg flex items-center justify-center ${dotsThemes[theme]} ${sizes[size]}`}
131
+ key={generateUuid()}>
132
+ ...
133
+ </div>
134
+ );
135
+ for (let i = pagesCount - 3; i <= pagesCount; i++) {
136
+ list.push(
137
+ <button
138
+ className={`rounded-lg flex items-center justify-center ${sizes[size]} ${selected === i ? activeThemes[activeTheme] : buttonThemes[theme]}`}
139
+ onClick={() => onPaginationClicked(i)}
140
+ key={generateUuid()}
141
+ >
142
+ {i}
143
+ </button>
144
+ );
145
+ }
146
+ } else {
147
+ const maxPage = selected < 3 ? 4 : selected + 2;
148
+ const fromPage = selected < 3 ? 1 : selected - 1;
149
+ for (let i = fromPage; i <= maxPage; i++) {
150
+ list.push(
151
+ <button
152
+ className={`rounded-lg flex items-center justify-center ${sizes[size]} ${selected === i ? activeThemes[activeTheme] : buttonThemes[theme]}`}
153
+ onClick={() => onPaginationClicked(i)}
154
+ key={generateUuid()}
155
+ >
156
+ {i}
157
+ </button>
158
+ );
159
+ }
160
+ list.push(
161
+ <div
162
+ className={`rounded-lg flex items-center justify-center ${dotsThemes[theme]} ${sizes[size]}`}
163
+ key={generateUuid()}>
164
+ ...
165
+ </div>
166
+ );
167
+ list.push(
168
+ <button
169
+ className={`rounded-lg flex items-center justify-center ${sizes[size]} ${selected === pagesCount ? activeThemes[activeTheme] : buttonThemes[theme]}`}
170
+ onClick={() => onPaginationClicked(pagesCount)}
171
+ key={generateUuid()}
172
+ >
173
+ {pagesCount}
174
+ </button>
175
+ );
176
+ }
177
+ }
178
+ list.push(
179
+ <button
180
+ className={`rounded-lg flex items-center justify-center ${buttonThemes[theme]} ${sizes[size]} ${arrowThemes[theme]}`}
181
+ disabled={selected === pagesCount}
182
+ onClick={() => onPaginationClicked(selected + 1)}
183
+ key={generateUuid()}
184
+ >
185
+ <ArrowLeft className={`${sizesArrow[size]}`}/>
186
+ </button>
187
+ );
188
+ }
189
+ setList(list);
190
+ };
191
+ return (
192
+ <div className="flex items-center justify-center rounded-xl gap-2">
193
+ {list}
194
+ </div>
195
+ );
196
+ };
197
+
198
+ export default LibPagination;
@@ -0,0 +1,88 @@
1
+ "use client"
2
+ import {FC, forwardRef, useEffect, useImperativeHandle, useState} from "react";
3
+ import Close from '@/assets/icons/close.svg'
4
+ import {useWidth} from '@/hooks/use-width';
5
+ import {addNavigation, onHashChanges, removeNavigation} from "@/utils/navigator/navigator-v2";
6
+
7
+ export interface LibModalProps {
8
+ title?: string;
9
+ children?: React.ReactNode;
10
+ size?: string;
11
+ height?: string;
12
+ }
13
+
14
+ export interface LibModalRef {
15
+ open: () => void;
16
+ close: () => void;
17
+ toggle: () => void;
18
+ }
19
+
20
+ const Modal = forwardRef<LibModalRef, LibModalProps>(({title, size, height, children}, ref) => {
21
+ const getDeviceWidth = useWidth();
22
+ const isHashChanged = onHashChanges();
23
+ const [isShow, setIsShow] = useState(false);
24
+
25
+ const open = () => setIsShow(true);
26
+ const close = () => setIsShow(false);
27
+ const toggle = () => setIsShow((v) => !v);
28
+
29
+ useImperativeHandle(ref, () => ({open, close, toggle}), []);
30
+
31
+ const onBackdropClick = (e: React.MouseEvent<HTMLDivElement>) => {
32
+ if (e.currentTarget === e.target) close();
33
+ };
34
+
35
+ useEffect(() => {
36
+ if (isHashChanged) {
37
+ close();
38
+ }
39
+ }, [isHashChanged])
40
+
41
+ useEffect(() => {
42
+ if (getDeviceWidth < 1024) {
43
+ if (isShow) {
44
+ addNavigation()
45
+ } else {
46
+ removeNavigation();
47
+ }
48
+ }
49
+ document.body.style.overflow = isShow ? "hidden" : "auto";
50
+ return () => {
51
+ document.body.style.overflow = "auto";
52
+ };
53
+
54
+ }, [isShow]);
55
+
56
+ return isShow ? (
57
+ <div
58
+ className={`backdrop bg-dark-100/30 fixed items-end justify-center px-4 md:px-10 lg:px-0
59
+ lg:place-items-center flex right-0 top-0 left-0 bottom-0 w-full h-full z-50 duration-200 transition-opacity ${
60
+ isShow ? "opacity-1" : "opacity-0 hidden pointer-events-none"
61
+ }`}
62
+ onClick={onBackdropClick}
63
+ >
64
+ <div
65
+ className={`transform p-3 md:p-5 rounded-t-2xl lg:rounded-2xl bg-grey-100 shadow-xl transition-opacity
66
+ duration-200 lg:h-auto w-full ${
67
+ size || " lg:max-w-2xl"
68
+ } ${isShow ? "opacity-1" : "opacity-0 pointer-events-none"} ${
69
+ getDeviceWidth < 1024 ? `${height ? height : "h-[90vh]"} animate-drawer-bottom-to-top` : `${height ? height : "h-full"} animate-fade-in-translate-y`
70
+ }`}
71
+ >
72
+ <div className="flex items-center justify-between">
73
+ <div className="font-bold text-base lg:text-lg">{title ?? ""}</div>
74
+ <button className="inline-block" onClick={close}>
75
+ <Close className="w-6"/>
76
+ </button>
77
+ </div>
78
+ <div
79
+ className="h-full overflow-y-auto mt-3 md:mt-5 max-h-[calc(100%-40px)] lg:max-h-[calc(100vh-90px)]">
80
+ {children}
81
+ </div>
82
+ </div>
83
+ </div>
84
+ ) : <></>;
85
+ });
86
+
87
+ Modal.displayName = "LibModal";
88
+ export default Modal;
@@ -0,0 +1,210 @@
1
+ "use client"
2
+ import {FC, useEffect, useState} from "react";
3
+ import AngleDown from "@/assets/icons/angle-down.svg";
4
+ import Close from '@/assets/icons/close.svg';
5
+ import Loading from '@/components/shared/loading/Loading';
6
+ import {useWidth} from '@/hooks/use-width';
7
+ import {useRouter} from 'next/navigation';
8
+
9
+ export interface props {
10
+ list?: any[];
11
+ label?: string;
12
+ title: string;
13
+ value?: string;
14
+ api?: any;
15
+ maxHeight?: string;
16
+ theme?: "secondary";
17
+ hasError?: string | null;
18
+ size?: "md" | "sm";
19
+ onSelectChange?: any;
20
+ selected?: any;
21
+ }
22
+
23
+ const themes = {
24
+ secondary: {
25
+ input: `border
26
+ bg-transparent
27
+ placeholder:text-secondary-400
28
+ outline-1
29
+ outline-primary-100
30
+ border-secondary-300
31
+ rounded-md
32
+ hover:not:border-primary-100
33
+ focus:outline
34
+ focus:border-primary-100
35
+
36
+ disabled:text-secondary-100
37
+ disabled:border-secondary-500`,
38
+
39
+ label: `text-dark-100
40
+ peer-focus:text-primary-100
41
+ peer-focus:scale-90
42
+
43
+ peer-disabled:text-secondary-400`
44
+ }
45
+ }
46
+ const sizes = {
47
+ sm: {
48
+ input: "text-xs px-2 py-1",
49
+ label: `px-1 text-xs `,
50
+ },
51
+ md: {
52
+ input: "text-sm px-3 py-2.5",
53
+ label: `px-1 text-xs`,
54
+ },
55
+ }
56
+ const Select: FC<props> = ({
57
+ list, label, hasError,
58
+ title, value, api,
59
+ maxHeight = "max-h-64", theme = "secondary", size = "md",
60
+ onSelectChange,
61
+ selected
62
+ }) => {
63
+ const getDeviceWidth = useWidth();
64
+ const router = useRouter();
65
+ const [isShow, setIsShow] = useState(false);
66
+ const [isLoading, setIsLoading] = useState(true);
67
+ const [localSelected, setLocalSelected] = useState(null);
68
+ const [localList, setLocalList] = useState(null);
69
+ useEffect(() => {
70
+ if (api) {
71
+ setIsLoading(true);
72
+ api().then((res) => {
73
+ setIsLoading(false);
74
+ setLocalList(res?.data?.message ? [] : res.data);
75
+ })
76
+ }
77
+ }, [api]);
78
+ useEffect(() => {
79
+ if (getDeviceWidth < 1024) {
80
+
81
+ if (isShow) {
82
+ router.push('#select');
83
+ document.body.style.overflow = 'hidden';
84
+ } else {
85
+
86
+ if (window.location.hash && !document.referrer.includes('#')) {
87
+ router.back();
88
+ }
89
+ document.body.style.overflow = 'auto';
90
+ }
91
+ }
92
+ }, [isShow]);
93
+
94
+ useEffect(() => {
95
+ const handlePopState = () => {
96
+ setIsShow(false);
97
+ };
98
+ window.addEventListener('popstate', handlePopState);
99
+ return () => {
100
+ window.removeEventListener('popstate', handlePopState);
101
+ };
102
+ }, []);
103
+
104
+ useEffect(() => {
105
+ if (label?.length) {
106
+ setLocalSelected(localList?.find(item => item[label] === selected));
107
+ } else {
108
+ setLocalSelected(selected);
109
+ }
110
+ }, [selected, localList]);
111
+
112
+ const onToggle = () => {
113
+ setIsShow(prevState => !prevState);
114
+ }
115
+ const onToggleEvent = (e) => {
116
+ if (e?.target?.className.toString()?.includes('backdropSelect')) {
117
+ onToggle()
118
+ }
119
+ }
120
+ const onSelect = (item) => {
121
+ setLocalSelected(item);
122
+ onToggle();
123
+ onSelectChange(item);
124
+ }
125
+
126
+ const getActiveClass = (item) => {
127
+ if (!localSelected) {
128
+ return "";
129
+ }
130
+ if (label?.length && item[label] === localSelected[label]) {
131
+ return "bg-grey-100"
132
+ }
133
+ if (item === localSelected) {
134
+ return "bg-grey-100"
135
+ }
136
+ }
137
+ return (
138
+ <div className="relative">
139
+ <label
140
+ className={`cursor-pointer
141
+ ${hasError && "!text-danger-100"}`}>
142
+ <span className={`${themes[theme].label} ${sizes[size].label}`}>{title}</span>
143
+ <button type="button"
144
+ className={`relative z-20 flex items-center mt-1 text-base justify-between gap-2 w-full ${localSelected ? "text-dark-100" : "text-grey-300"} ${themes[theme].input} ${sizes[size].input} ${hasError && "!border-danger-100 focus:border-danger-100 outline-danger-100"}`}
145
+ onClick={onToggle}>
146
+ {
147
+ localSelected ? (
148
+ value?.length ? localSelected[value] : localSelected
149
+ ) : "انتخاب"
150
+ } <AngleDown
151
+ className={`w-4 h-4 transition ease duration-200 ${!isShow ? "rotate-0" : "rotate-180"}`}/>
152
+ </button>
153
+ </label>
154
+
155
+ {
156
+ isShow ? (
157
+ <div
158
+ className={`overflow-hidden ${getDeviceWidth < 1024 ? "fixed top-0 right-0 h-full w-full z-40" : ""}`}>
159
+ <div
160
+ className={`flex flex-col w-full z-40 bg-light-100 w-full shadow-lg border border-secondary-300 ${getDeviceWidth < 1024 ? "relative h-full pt-1 pb-5 px-5" : `animate-fade-in-translate-y mt-1 rounded-lg absolute ${maxHeight}`} overflow-auto`}>
161
+ {
162
+ api && isLoading ? (
163
+ <div className="py-10 flex justify-center">
164
+ <Loading size="w-7 h-7"/>
165
+ </div>
166
+ ) : (
167
+ <>
168
+ {
169
+ getDeviceWidth < 1024 ? (
170
+ <div className="sticky top-0 text-left">
171
+ <button className="p-3" onClick={onToggle}>
172
+ <Close className="w-6"/>
173
+ </button>
174
+ </div>
175
+ ) : undefined
176
+ }
177
+ {
178
+ localList?.map((item, index) => {
179
+ return (
180
+ <>
181
+ <button type="button" onClick={() => onSelect(item)} key={index}
182
+ className={`text-right py-2.5 px-4 text-base hover:bg-grey-100 rounded-lg ${getActiveClass(item)}`}>
183
+ {value?.length ? item[value] : item}
184
+ </button>
185
+ </>
186
+ )
187
+ })
188
+ }
189
+ </>
190
+ )
191
+ }
192
+ </div>
193
+ <div
194
+ className={`backdropSelect fixed top-0 left-0 bottom-0 w-full h-full z-30`}
195
+ onClick={onToggleEvent}>
196
+
197
+ </div>
198
+ </div>
199
+ ) : undefined
200
+ }
201
+ {
202
+ hasError &&
203
+ <p className="text-xs mt-1 text-danger-100">{hasError}</p>
204
+ }
205
+
206
+ </div>
207
+ );
208
+ };
209
+
210
+ export default Select;
@@ -0,0 +1,31 @@
1
+ "use client"
2
+ import {FC, useEffect, useRef, useState} from "react";
3
+
4
+ interface TabType {
5
+ name: string;
6
+ label: string;
7
+ }
8
+
9
+ export interface props {
10
+ tabs: TabType[],
11
+ onTabEmit?: any,
12
+ }
13
+
14
+ const LibTabs: FC<props> = ({tabs, onTabEmit}) => {
15
+ const [isActive, setIsActive] = useState(0);
16
+ const onTabClicked = (item: TabType, index: number) => {
17
+ setIsActive(index)
18
+ onTabEmit(item)
19
+ }
20
+ return (
21
+ <div className="flex border-2 border-primary-100 rounded-lg overflow-hidden divide-x-2 divide-x-reverse divide-primary-100">
22
+ {
23
+ tabs.map((item: TabType, index: number) => {
24
+ return <button className={`transition font-bold grow p-2 ${isActive === index ? "bg-primary-100 text-light-100" : ""}`} key = {index} onClick={() => onTabClicked(item, index)}>{item.label}</button>
25
+ })
26
+ }
27
+ </div>
28
+ );
29
+ };
30
+
31
+ export default LibTabs;