@skalfa/skalfa-component 1.0.7 → 1.0.9
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/accordion/Accordion.component.js +2 -3
- package/dist/breadcrumb/Breadcrumb.component.js +2 -3
- package/dist/button/Button.component.d.ts +2 -1
- package/dist/button/Button.component.js +2 -2
- package/dist/carousel/Carousel.component.js +2 -3
- package/dist/chip/Chip.component.js +2 -3
- package/dist/contexts-mock.d.ts +2 -0
- package/dist/contexts-mock.js +10 -0
- package/dist/input/Checkbox.component.js +2 -3
- package/dist/input/Input.component.d.ts +3 -2
- package/dist/input/Input.component.js +3 -3
- package/dist/input/InputCheckbox.component.js +1 -1
- package/dist/input/InputCurrency.component.d.ts +3 -2
- package/dist/input/InputCurrency.component.js +3 -3
- package/dist/input/InputDate.component.js +4 -5
- package/dist/input/InputDatetime.component.d.ts +3 -2
- package/dist/input/InputDatetime.component.js +3 -3
- package/dist/input/InputDocument.component.js +7 -8
- package/dist/input/InputImage.component.js +3 -4
- package/dist/input/InputNumber.component.js +3 -4
- package/dist/input/InputPassword.component.js +3 -3
- package/dist/input/InputRadio.component.js +1 -1
- package/dist/input/InputTime.component.js +3 -3
- package/dist/input/InputValues.component.js +3 -4
- package/dist/input/Select.component.js +6 -7
- package/dist/modal/FloatingPage.component.d.ts +3 -1
- package/dist/modal/FloatingPage.component.js +7 -3
- package/dist/modal/Modal.component.js +1 -2
- package/dist/modal/ModalConfirm.component.d.ts +2 -2
- package/dist/modal/ModalConfirm.component.js +14 -12
- package/dist/modal/Toast.component.js +1 -2
- package/dist/nav/Bottombar.component.d.ts +2 -2
- package/dist/nav/Bottombar.component.js +7 -8
- package/dist/nav/Footer.component.d.ts +2 -2
- package/dist/nav/Footer.component.js +6 -7
- package/dist/nav/Headbar.component.js +2 -3
- package/dist/nav/Navbar.component.d.ts +2 -2
- package/dist/nav/Navbar.component.js +3 -4
- package/dist/nav/Sidebar.component.d.ts +3 -7
- package/dist/nav/Sidebar.component.js +14 -17
- package/dist/supervision/FormSupervision.component.d.ts +4 -29
- package/dist/supervision/FormSupervision.component.js +84 -136
- package/dist/supervision/TableSupervision.component.d.ts +17 -26
- package/dist/supervision/TableSupervision.component.js +207 -193
- package/dist/table/ControlBar.component.js +15 -16
- package/dist/table/FilterComponent.js +4 -6
- package/dist/table/Pagination.component.js +2 -3
- package/dist/table/Table.component.js +3 -4
- package/dist/wrap/Swipe.component.js +2 -2
- package/package.json +18 -10
- package/src/accordion/Accordion.component.tsx +87 -0
- package/src/breadcrumb/Breadcrumb.component.tsx +79 -0
- package/src/button/Button.component.tsx +89 -0
- package/src/card/AlertCard.component.tsx +69 -0
- package/src/card/Card.component.tsx +25 -0
- package/src/card/DashboardCard.component.tsx +44 -0
- package/src/card/GalleryCard.component.tsx +50 -0
- package/src/card/ProductCard.component.tsx +65 -0
- package/src/card/ProfileCard.component.tsx +71 -0
- package/src/carousel/Carousel.component.tsx +111 -0
- package/src/chip/Chip.component.tsx +38 -0
- package/src/contexts-mock.ts +6 -0
- package/src/index.ts +70 -0
- package/src/input/Checkbox.component.tsx +102 -0
- package/src/input/Input.component.tsx +334 -0
- package/src/input/InputCheckbox.component.tsx +174 -0
- package/src/input/InputCurrency.component.tsx +165 -0
- package/src/input/InputDate.component.tsx +355 -0
- package/src/input/InputDatetime.component.tsx +267 -0
- package/src/input/InputDocument.component.tsx +360 -0
- package/src/input/InputImage.component.tsx +534 -0
- package/src/input/InputNumber.component.tsx +193 -0
- package/src/input/InputOtp.component.tsx +169 -0
- package/src/input/InputPassword.component.tsx +245 -0
- package/src/input/InputRadio.component.tsx +174 -0
- package/src/input/InputTime.component.tsx +280 -0
- package/src/input/InputValues.component.tsx +70 -0
- package/src/input/Radio.component.tsx +98 -0
- package/src/input/Select.component.tsx +557 -0
- package/src/modal/BottomSheet.component.tsx +246 -0
- package/src/modal/FloatingPage.component.tsx +133 -0
- package/src/modal/Modal.component.tsx +94 -0
- package/src/modal/ModalConfirm.component.tsx +204 -0
- package/src/modal/Toast.component.tsx +124 -0
- package/src/nav/Bottombar.component.tsx +71 -0
- package/src/nav/Footer.component.tsx +176 -0
- package/src/nav/Headbar.component.tsx +33 -0
- package/src/nav/Navbar.component.tsx +137 -0
- package/src/nav/Sidebar.component.tsx +307 -0
- package/src/nav/Tabbar.component.tsx +61 -0
- package/src/nav/Wizard.component.tsx +80 -0
- package/src/supervision/FormSupervision.component.tsx +404 -0
- package/src/supervision/TableSupervision.component.tsx +653 -0
- package/src/table/ControlBar.component.tsx +501 -0
- package/src/table/FilterComponent.tsx +517 -0
- package/src/table/Pagination.component.tsx +152 -0
- package/src/table/Table.component.tsx +436 -0
- package/src/typography/TypographyArticle.component.tsx +26 -0
- package/src/typography/TypographyColumn.component.tsx +20 -0
- package/src/typography/TypographyContent.component.tsx +20 -0
- package/src/typography/TypographyTips.component.tsx +20 -0
- package/src/wrap/Draggable.component.tsx +303 -0
- package/src/wrap/Image.component.tsx +10 -0
- package/src/wrap/OutsideClick.component.tsx +48 -0
- package/src/wrap/ScrollContainer.component.tsx +107 -0
- package/src/wrap/ShortcutProvider.tsx +57 -0
- package/src/wrap/Swipe.component.tsx +121 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { ReactNode, useEffect, useState } from "react";
|
|
4
|
+
import { cn, pcn } from "@utils";
|
|
5
|
+
import { ButtonComponent } from "../button/Button.component";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
type CT = "base" | "backdrop" | "header" | "footer";
|
|
10
|
+
|
|
11
|
+
export interface ToastProps {
|
|
12
|
+
show: boolean;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
title?: string | ReactNode;
|
|
15
|
+
children?: any;
|
|
16
|
+
footer?: string | ReactNode;
|
|
17
|
+
|
|
18
|
+
/** Use custom class with: "backdrop::", "header::", "footer::". */
|
|
19
|
+
className?: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
export function ToastComponent({
|
|
25
|
+
show,
|
|
26
|
+
onClose,
|
|
27
|
+
title,
|
|
28
|
+
children,
|
|
29
|
+
footer,
|
|
30
|
+
className = "",
|
|
31
|
+
}: ToastProps) {
|
|
32
|
+
const [countdown, setCountdown] = useState(5);
|
|
33
|
+
const [timerId, setTimerId] = useState<NodeJS.Timeout | null>(null);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (show) {
|
|
37
|
+
setCountdown(5); // Reset countdown saat toast muncul
|
|
38
|
+
const id = setInterval(() => {
|
|
39
|
+
setCountdown((prev) => {
|
|
40
|
+
if (prev <= 1) {
|
|
41
|
+
clearInterval(id);
|
|
42
|
+
onClose();
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
return prev - 1;
|
|
46
|
+
});
|
|
47
|
+
}, 1000);
|
|
48
|
+
setTimerId(id);
|
|
49
|
+
}
|
|
50
|
+
return () => {
|
|
51
|
+
if (timerId) {
|
|
52
|
+
clearInterval(timerId);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}, [show]);
|
|
56
|
+
|
|
57
|
+
const handleMouseEnter = () => {
|
|
58
|
+
if (timerId) {
|
|
59
|
+
clearInterval(timerId);
|
|
60
|
+
setTimerId(null);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const handleMouseLeave = () => {
|
|
65
|
+
const id = setInterval(() => {
|
|
66
|
+
setCountdown((prev) => {
|
|
67
|
+
if (prev <= 1) {
|
|
68
|
+
clearInterval(id);
|
|
69
|
+
onClose();
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
return prev - 1;
|
|
73
|
+
});
|
|
74
|
+
}, 1000);
|
|
75
|
+
setTimerId(id);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<>
|
|
80
|
+
<div
|
|
81
|
+
className={cn(
|
|
82
|
+
"toast",
|
|
83
|
+
!show && "translate-y-full opacity-0 scale-y-0",
|
|
84
|
+
pcn<CT>(className, "base"),
|
|
85
|
+
)}
|
|
86
|
+
onMouseEnter={handleMouseEnter}
|
|
87
|
+
onMouseLeave={handleMouseLeave}
|
|
88
|
+
>
|
|
89
|
+
{title && (
|
|
90
|
+
<div
|
|
91
|
+
className={cn(
|
|
92
|
+
"pt-2 px-3 flex justify-between items-center text-foreground",
|
|
93
|
+
pcn<CT>(className, "header"),
|
|
94
|
+
)}
|
|
95
|
+
>
|
|
96
|
+
<h6 className="font-semibold">{title}</h6>
|
|
97
|
+
|
|
98
|
+
<div className="flex gap-2 items-center">
|
|
99
|
+
<span className="text-xs text-foreground/50">{countdown}</span>
|
|
100
|
+
<ButtonComponent
|
|
101
|
+
icon="solid/times"
|
|
102
|
+
variant="simple"
|
|
103
|
+
paint="danger"
|
|
104
|
+
onClick={() => {
|
|
105
|
+
clearInterval(timerId || "");
|
|
106
|
+
onClose();
|
|
107
|
+
}}
|
|
108
|
+
size="sm"
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
)}
|
|
113
|
+
|
|
114
|
+
{show && children}
|
|
115
|
+
|
|
116
|
+
{footer && (
|
|
117
|
+
<div className={cn("modal-footer", pcn<CT>(className, "footer"))}>
|
|
118
|
+
{show && footer}
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { Icon, type IconName } from "@skalfa/skalfa-icon";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { usePathname } from "next/navigation";
|
|
7
|
+
import { cn, pcn, useKeyboardOpen } from "@utils";
|
|
8
|
+
|
|
9
|
+
export interface BottombarItemProps {
|
|
10
|
+
icon : IconName;
|
|
11
|
+
path : string;
|
|
12
|
+
activeKey ?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BottombarProps {
|
|
16
|
+
active ?: string;
|
|
17
|
+
items ?: BottombarItemProps[];
|
|
18
|
+
className ?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const defaultItems: BottombarItemProps[] = [
|
|
22
|
+
{ icon: "solid/home", path: "/_example/bottombar", activeKey: "home" },
|
|
23
|
+
{ icon: "solid/clipboard", path: "/", activeKey: "clipboard" },
|
|
24
|
+
{ icon: "solid/crosshairs", path: "/", activeKey: "crosshairs" },
|
|
25
|
+
{ icon: "solid/sack-dollar", path: "/", activeKey: "sack-dollar" },
|
|
26
|
+
{ icon: "solid/user-circle", path: "/", activeKey: "profile" },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
export function BottombarComponent({
|
|
30
|
+
className = "",
|
|
31
|
+
active,
|
|
32
|
+
items = defaultItems,
|
|
33
|
+
}: BottombarProps) {
|
|
34
|
+
const pathname = usePathname();
|
|
35
|
+
const isKeyboardOpen = useKeyboardOpen();
|
|
36
|
+
|
|
37
|
+
const styles = {
|
|
38
|
+
base: cn(
|
|
39
|
+
"bottombar-base",
|
|
40
|
+
isKeyboardOpen ? "-bottom-60" : "bottom-3",
|
|
41
|
+
pcn(className, "base"),
|
|
42
|
+
),
|
|
43
|
+
item: "bottombar-item",
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
<div className={styles.base}>
|
|
49
|
+
<div className="grid grid-cols-5 gap-2 items-center" style={{ gridTemplateColumns: `repeat(${items.length}, minmax(0, 1fr))` }}>
|
|
50
|
+
{items.map((item, key) => {
|
|
51
|
+
const isActive = pathname === item.path || active === item.activeKey;
|
|
52
|
+
return (
|
|
53
|
+
<Link href={item.path} key={key}>
|
|
54
|
+
<div
|
|
55
|
+
className={cn(
|
|
56
|
+
styles.item,
|
|
57
|
+
isActive && "bottombar-item-active",
|
|
58
|
+
pcn(className, "item"),
|
|
59
|
+
isActive && pcn(className, "active"),
|
|
60
|
+
)}
|
|
61
|
+
>
|
|
62
|
+
<Icon icon={item.icon} />
|
|
63
|
+
</div>
|
|
64
|
+
</Link>
|
|
65
|
+
);
|
|
66
|
+
})}
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { Icon, type IconName } from "@skalfa/skalfa-icon";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
|
|
7
|
+
export interface FooterLinkProps {
|
|
8
|
+
label: string;
|
|
9
|
+
path: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface FooterColumnProps {
|
|
13
|
+
title: string;
|
|
14
|
+
items: FooterLinkProps[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface FooterSocialProps {
|
|
18
|
+
icon: IconName;
|
|
19
|
+
path: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface FooterProps {
|
|
23
|
+
brandTitle ?: string;
|
|
24
|
+
brandSubtitle ?: string;
|
|
25
|
+
address ?: { label: string; path: string };
|
|
26
|
+
phone ?: { label: string; path: string };
|
|
27
|
+
email ?: { label: string; path: string };
|
|
28
|
+
supportHours ?: string;
|
|
29
|
+
socials ?: FooterSocialProps[];
|
|
30
|
+
columns ?: FooterColumnProps[];
|
|
31
|
+
copyrightLink ?: { label: string; path: string };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const defaultSocials: FooterSocialProps[] = [
|
|
35
|
+
{ icon: "brands/facebook" as any, path: "" },
|
|
36
|
+
{ icon: "brands/linkedin" as any, path: "" },
|
|
37
|
+
{ icon: "brands/github" as any, path: "https://github.com/SE-JE" },
|
|
38
|
+
{ icon: "brands/instagram" as any, path: "https://www.instagram.com/seje.digital/" },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const defaultColumns: FooterColumnProps[] = [
|
|
42
|
+
{
|
|
43
|
+
title: "Link Menu",
|
|
44
|
+
items: [
|
|
45
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
46
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
47
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
48
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
title: "Link Menu",
|
|
53
|
+
items: [
|
|
54
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
55
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
56
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
57
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
title: "Link Menu",
|
|
62
|
+
items: [
|
|
63
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
64
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
65
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
66
|
+
{ label: "Klik Link Menu 1", path: "" },
|
|
67
|
+
],
|
|
68
|
+
},
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
export function FooterComponent({
|
|
72
|
+
brandTitle = "Next Light v.3",
|
|
73
|
+
brandSubtitle = "The Magic Starter Template",
|
|
74
|
+
address = { label: "Soekarno Hatta No 27 C, Ponorogo, Jawa Timur, Indonesia", path: "https://maps.app.goo.gl/TY2QDjFPm3RfwjUq6" },
|
|
75
|
+
phone = { label: "+62 888888888888", path: "https://wa.me/6281456140392" },
|
|
76
|
+
email = { label: "example@gmail.com", path: "mailto:sejedigital@gmail.com" },
|
|
77
|
+
supportHours = "24 / 7 Online Suport | Senin - Sabtu ( 09.00 s/d 17.00 )",
|
|
78
|
+
socials = defaultSocials,
|
|
79
|
+
columns = defaultColumns,
|
|
80
|
+
copyrightLink = { label: "sejedigital.com 2020 - 2025", path: "https://sejedigital.com/" },
|
|
81
|
+
}: FooterProps) {
|
|
82
|
+
return (
|
|
83
|
+
<>
|
|
84
|
+
<div className="footer-base">
|
|
85
|
+
<div className="footer-brand-container">
|
|
86
|
+
<h1 className="footer-brand-title">
|
|
87
|
+
{brandTitle}
|
|
88
|
+
</h1>
|
|
89
|
+
{brandSubtitle && (
|
|
90
|
+
<p className="footer-brand-subtitle">
|
|
91
|
+
{brandSubtitle}
|
|
92
|
+
</p>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div className="footer-contact-container">
|
|
97
|
+
{address && (
|
|
98
|
+
<a
|
|
99
|
+
href={address.path}
|
|
100
|
+
className="footer-contact-link"
|
|
101
|
+
target="_blank"
|
|
102
|
+
rel="noopener noreferrer"
|
|
103
|
+
>
|
|
104
|
+
{address.label}
|
|
105
|
+
</a>
|
|
106
|
+
)}
|
|
107
|
+
{phone && (
|
|
108
|
+
<a
|
|
109
|
+
href={phone.path}
|
|
110
|
+
className="footer-contact-link"
|
|
111
|
+
>
|
|
112
|
+
{phone.label}
|
|
113
|
+
</a>
|
|
114
|
+
)}
|
|
115
|
+
{email && (
|
|
116
|
+
<a
|
|
117
|
+
href={email.path}
|
|
118
|
+
className="footer-contact-link"
|
|
119
|
+
>
|
|
120
|
+
{email.label}
|
|
121
|
+
</a>
|
|
122
|
+
)}
|
|
123
|
+
{supportHours && (
|
|
124
|
+
<p className="footer-support-hours">
|
|
125
|
+
{supportHours}
|
|
126
|
+
</p>
|
|
127
|
+
)}
|
|
128
|
+
|
|
129
|
+
{socials && socials.length > 0 && (
|
|
130
|
+
<div className="footer-socials-container">
|
|
131
|
+
{socials.map((social, key) => (
|
|
132
|
+
<a href={social.path} key={key} target="_blank" rel="noopener noreferrer">
|
|
133
|
+
<Icon
|
|
134
|
+
icon={social.icon}
|
|
135
|
+
className="footer-social-icon"
|
|
136
|
+
/>
|
|
137
|
+
</a>
|
|
138
|
+
))}
|
|
139
|
+
</div>
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{columns && columns.length > 0 && (
|
|
144
|
+
<div className="footer-links-container">
|
|
145
|
+
<div className="footer-links-grid">
|
|
146
|
+
{columns.map((column, colKey) => (
|
|
147
|
+
<nav aria-label={`Footer navigation ${column.title}`} key={colKey}>
|
|
148
|
+
<h6 className="footer-menu-title">
|
|
149
|
+
{column.title}
|
|
150
|
+
</h6>
|
|
151
|
+
|
|
152
|
+
<div className="footer-menu-list">
|
|
153
|
+
{column.items.map((item, itemKey) => (
|
|
154
|
+
<Link href={item.path} className="footer-menu-link" key={itemKey}>
|
|
155
|
+
{item.label}
|
|
156
|
+
</Link>
|
|
157
|
+
))}
|
|
158
|
+
</div>
|
|
159
|
+
</nav>
|
|
160
|
+
))}
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{copyrightLink && (
|
|
166
|
+
<p className="footer-copyright">
|
|
167
|
+
Copyright ©
|
|
168
|
+
<a href={copyrightLink.path} className="ml-1" target="_blank" rel="noopener noreferrer">
|
|
169
|
+
{copyrightLink.label}
|
|
170
|
+
</a>
|
|
171
|
+
</p>
|
|
172
|
+
)}
|
|
173
|
+
</div>
|
|
174
|
+
</>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { Icon, type IconName } from "@skalfa/skalfa-icon";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import { useRouter } from "next/navigation";
|
|
6
|
+
import { ReactNode } from "react";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
export interface HeadbarProps {
|
|
11
|
+
title ?: string;
|
|
12
|
+
backHref ?: string | boolean;
|
|
13
|
+
rightContent ?: ReactNode;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
export function HeadbarComponent({ title, backHref, rightContent }: HeadbarProps) {
|
|
18
|
+
const router = useRouter();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex justify-between w-full items-center mt-2 mb-4">
|
|
22
|
+
<div className="flex items-center">
|
|
23
|
+
{backHref && (
|
|
24
|
+
<div className="w-8 aspect-square flex justify-center items-center cursor-pointer" onClick={() => typeof backHref != "boolean" ? router.push(backHref) : router.back()}>
|
|
25
|
+
<Icon icon="solid/chevron-left" className="text-secondary text-xl" />
|
|
26
|
+
</div>
|
|
27
|
+
)}
|
|
28
|
+
<p className="text-lg font-bold px-2">{title}</p>
|
|
29
|
+
</div>
|
|
30
|
+
{rightContent && <div>{rightContent}</div>}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
import { Icon, type IconName } from "@skalfa/skalfa-icon";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import Link from "next/link";
|
|
6
|
+
import { ButtonComponent } from "../button/Button.component";
|
|
7
|
+
import { cn } from "@utils";
|
|
8
|
+
|
|
9
|
+
export interface NavbarItemProps {
|
|
10
|
+
label: string;
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface NavbarProps {
|
|
15
|
+
logoTitle ?: string;
|
|
16
|
+
logoSubtitle ?: string;
|
|
17
|
+
logoPath ?: string;
|
|
18
|
+
specialLink ?: { label: string; path: string; icon?: IconName };
|
|
19
|
+
items ?: NavbarItemProps[];
|
|
20
|
+
isLoggedIn ?: boolean;
|
|
21
|
+
onLogin ?: () => void;
|
|
22
|
+
onRegister ?: () => void;
|
|
23
|
+
onProfile ?: () => void;
|
|
24
|
+
className ?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const defaultItems: NavbarItemProps[] = [
|
|
28
|
+
{ label: "Tentang", path: "" },
|
|
29
|
+
{ label: "Artikel", path: "" },
|
|
30
|
+
{ label: "Bantuan", path: "" },
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const defaultSpecialLink = {
|
|
34
|
+
label: "Special Link",
|
|
35
|
+
path: "",
|
|
36
|
+
icon: "solid/store",
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
export function NavbarComponent({
|
|
40
|
+
logoTitle = "Next Light v.3",
|
|
41
|
+
logoSubtitle = "The Magic Starter Template",
|
|
42
|
+
logoPath = "/",
|
|
43
|
+
specialLink = defaultSpecialLink,
|
|
44
|
+
items = defaultItems,
|
|
45
|
+
isLoggedIn = false,
|
|
46
|
+
onLogin,
|
|
47
|
+
onRegister,
|
|
48
|
+
onProfile,
|
|
49
|
+
className = "",
|
|
50
|
+
}: NavbarProps) {
|
|
51
|
+
return (
|
|
52
|
+
<>
|
|
53
|
+
<div className={cn("navbar-topbar", className)}>
|
|
54
|
+
<div className="navbar-container">
|
|
55
|
+
<h2 className="text-base">
|
|
56
|
+
Ini untuk
|
|
57
|
+
<Link href={specialLink.path} className="navbar-special-link">
|
|
58
|
+
{specialLink.icon && <Icon icon={specialLink.icon} className="mr-1" />}
|
|
59
|
+
{specialLink.label}
|
|
60
|
+
</Link>
|
|
61
|
+
</h2>
|
|
62
|
+
|
|
63
|
+
<div className="navbar-menu">
|
|
64
|
+
{items.map((item, key) => (
|
|
65
|
+
<Link href={item.path} key={key}>
|
|
66
|
+
<div className="navbar-menu-item-sm">{item.label}</div>
|
|
67
|
+
</Link>
|
|
68
|
+
))}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
<div className="navbar-main">
|
|
74
|
+
<div className="navbar-main-container">
|
|
75
|
+
<Link href={logoPath}>
|
|
76
|
+
<div className="w-max">
|
|
77
|
+
<h1 className="navbar-logo-title">
|
|
78
|
+
{logoTitle}
|
|
79
|
+
</h1>
|
|
80
|
+
{logoSubtitle && (
|
|
81
|
+
<p className="navbar-logo-subtitle">
|
|
82
|
+
{logoSubtitle}
|
|
83
|
+
</p>
|
|
84
|
+
)}
|
|
85
|
+
</div>
|
|
86
|
+
</Link>
|
|
87
|
+
|
|
88
|
+
<div className="flex gap-12 items-center">
|
|
89
|
+
<div className="navbar-menu">
|
|
90
|
+
{items.map((item, key) => (
|
|
91
|
+
<Link href={item.path} key={key}>
|
|
92
|
+
<div className="navbar-menu-item-base">{item.label}</div>
|
|
93
|
+
</Link>
|
|
94
|
+
))}
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<div className="w-full flex items-center gap-8">
|
|
98
|
+
{!isLoggedIn ? (
|
|
99
|
+
<div className="navbar-auth-buttons">
|
|
100
|
+
<ButtonComponent
|
|
101
|
+
label={"Masuk"}
|
|
102
|
+
size={"sm"}
|
|
103
|
+
onClick={onLogin}
|
|
104
|
+
/>
|
|
105
|
+
<ButtonComponent
|
|
106
|
+
label={"Daftar"}
|
|
107
|
+
size={"sm"}
|
|
108
|
+
variant="light"
|
|
109
|
+
onClick={onRegister}
|
|
110
|
+
/>
|
|
111
|
+
</div>
|
|
112
|
+
) : (
|
|
113
|
+
<div className="flex gap-2 w-max items-center">
|
|
114
|
+
<div className="p-2 text-light-foreground hover:text-foreground cursor-pointer">
|
|
115
|
+
<Icon icon="solid/history" />
|
|
116
|
+
</div>
|
|
117
|
+
<div className="p-2 text-light-foreground hover:text-foreground cursor-pointer">
|
|
118
|
+
<Icon icon="solid/bell" />
|
|
119
|
+
</div>
|
|
120
|
+
<div className="h-5 w-[1px] bg-foreground mx-2.5"></div>
|
|
121
|
+
<div
|
|
122
|
+
className="flex items-center gap-2.5 px-4 cursor-pointer -ml-2 text-light-foreground hover:text-foreground"
|
|
123
|
+
onClick={onProfile}
|
|
124
|
+
>
|
|
125
|
+
<div className="h-10 bg-background rounded-full aspect-square overflow-hidden flex justify-center items-center">
|
|
126
|
+
<Icon icon="solid/user" />
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
</>
|
|
136
|
+
);
|
|
137
|
+
}
|