gbs-add-block 0.0.58 → 0.0.60
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/README.md +4 -3
- package/index.js +1 -0
- package/package.json +1 -1
- package/source/components/grid/Grid.tsx +5 -0
- package/source/components/navbar/AuthSection.tsx +98 -0
- package/source/components/navbar/MobileToggle.tsx +31 -0
- package/source/components/navbar/NavItemComponent.tsx +119 -0
- package/source/components/navbar/SearchBar.tsx +55 -0
- package/source/components/navbar/helperFunctions.ts +27 -0
- package/source/components/navbar/index.tsx +156 -0
- package/source/components/navbar/types.ts +69 -0
- package/source/components/skeleton/index.tsx +7 -0
- package/source/components/grid/RenderCell.tsx +0 -32
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# GBS Building Blocks 2.0 (v0.0.
|
|
1
|
+
# GBS Building Blocks 2.0 (v0.0.60)
|
|
2
2
|
|
|
3
3
|
Latest and upgraded version of GBS building blocks with headless UI and removed dependencies.
|
|
4
4
|
|
|
@@ -6,10 +6,11 @@ Latest and upgraded version of GBS building blocks with headless UI and removed
|
|
|
6
6
|
|
|
7
7
|
For detailed documentation on usage and props, Please visit: [Building Block Documentation v2.0](https://blackmax-designs.gitbook.io/building-block-v2.0)
|
|
8
8
|
|
|
9
|
-
## What's New 🎉 (Ver 0.0.
|
|
9
|
+
## What's New 🎉 (Ver 0.0.60)
|
|
10
10
|
|
|
11
11
|
- Updated for bug fixes.
|
|
12
|
-
- New component "Skeleton" Added.
|
|
12
|
+
- New component "Skeleton" & "Navbar" Added.
|
|
13
|
+
|
|
13
14
|
|
|
14
15
|
## Authors
|
|
15
16
|
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -421,6 +421,11 @@ export const GridMemoised = forwardRef((props: GridProps, ref) => {
|
|
|
421
421
|
onChange={handleSearchInput}
|
|
422
422
|
placeholder="search"
|
|
423
423
|
className="outline-none p-2 text-sm font-normal bg-gray-50 rounded-lg max-sm:hidden dark:bg-black dark:border dark:text-white"
|
|
424
|
+
onKeyDown={(event: React.KeyboardEvent<HTMLInputElement>) => {
|
|
425
|
+
if (event.key === "Enter") {
|
|
426
|
+
handleSearch(searchParam);
|
|
427
|
+
}
|
|
428
|
+
}}
|
|
424
429
|
/>
|
|
425
430
|
<button
|
|
426
431
|
className="bg-white border rounded-lg text-black w-10 flex items-center justify-center dark:bg-black dark:text-white"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { AuthSectionProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const AuthSection: React.FC<AuthSectionProps> = ({
|
|
5
|
+
showAuthButtons,
|
|
6
|
+
authState,
|
|
7
|
+
userProfile,
|
|
8
|
+
variant,
|
|
9
|
+
onLogin,
|
|
10
|
+
onSignup,
|
|
11
|
+
onLogout,
|
|
12
|
+
onProfileClick,
|
|
13
|
+
}) => {
|
|
14
|
+
if (!showAuthButtons) return null;
|
|
15
|
+
|
|
16
|
+
if (authState === "loggedIn" && userProfile) {
|
|
17
|
+
return (
|
|
18
|
+
<div className="ml-4 relative">
|
|
19
|
+
<button
|
|
20
|
+
onClick={onProfileClick}
|
|
21
|
+
className="flex items-center text-sm focus:outline-none"
|
|
22
|
+
>
|
|
23
|
+
{userProfile.avatar ? (
|
|
24
|
+
<img
|
|
25
|
+
className="h-8 w-8 rounded-full"
|
|
26
|
+
src={userProfile.avatar}
|
|
27
|
+
alt={userProfile.name}
|
|
28
|
+
/>
|
|
29
|
+
) : (
|
|
30
|
+
<div className="h-8 w-8 rounded-full bg-blue-500 flex items-center justify-center text-white">
|
|
31
|
+
{userProfile.name.charAt(0).toUpperCase()}
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
<span className="ml-2 hidden md:block">{userProfile.name}</span>
|
|
35
|
+
<svg
|
|
36
|
+
className="ml-1 h-4 w-4"
|
|
37
|
+
fill="none"
|
|
38
|
+
stroke="currentColor"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
>
|
|
41
|
+
<path
|
|
42
|
+
strokeLinecap="round"
|
|
43
|
+
strokeLinejoin="round"
|
|
44
|
+
strokeWidth={2}
|
|
45
|
+
d="M19 9l-7 7-7-7"
|
|
46
|
+
/>
|
|
47
|
+
</svg>
|
|
48
|
+
</button>
|
|
49
|
+
<div className="origin-top-right absolute right-0 mt-2 w-48 rounded-md shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 hidden">
|
|
50
|
+
<a
|
|
51
|
+
href="/profile"
|
|
52
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
|
53
|
+
>
|
|
54
|
+
Your Profile
|
|
55
|
+
</a>
|
|
56
|
+
<a
|
|
57
|
+
href="/settings"
|
|
58
|
+
className="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
|
59
|
+
>
|
|
60
|
+
Settings
|
|
61
|
+
</a>
|
|
62
|
+
<button
|
|
63
|
+
onClick={onLogout}
|
|
64
|
+
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
|
|
65
|
+
>
|
|
66
|
+
Sign out
|
|
67
|
+
</button>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className="md:ml-4 mt-4 md:mt-0 flex flex-col md:flex-row space-y-2 md:space-y-0 md:space-x-2">
|
|
75
|
+
<button
|
|
76
|
+
onClick={onLogin}
|
|
77
|
+
className={`
|
|
78
|
+
px-4 py-2 text-sm font-medium rounded-md
|
|
79
|
+
${
|
|
80
|
+
variant === "dark"
|
|
81
|
+
? "text-white hover:bg-gray-700"
|
|
82
|
+
: "text-blue-600 hover:bg-blue-50"
|
|
83
|
+
}
|
|
84
|
+
`}
|
|
85
|
+
>
|
|
86
|
+
Log in
|
|
87
|
+
</button>
|
|
88
|
+
<button
|
|
89
|
+
onClick={onSignup}
|
|
90
|
+
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700"
|
|
91
|
+
>
|
|
92
|
+
Sign up
|
|
93
|
+
</button>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export default AuthSection;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { MobileToggleProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const MobileToggle: React.FC<MobileToggleProps> = ({ isOpen, toggleMenu }) => {
|
|
5
|
+
return (
|
|
6
|
+
<div className="block md:hidden">
|
|
7
|
+
<button
|
|
8
|
+
className="navbar-toggle flex items-center px-3 py-2 border rounded"
|
|
9
|
+
onClick={toggleMenu}
|
|
10
|
+
>
|
|
11
|
+
<svg className="h-4 w-4 fill-current" viewBox="0 0 20 20">
|
|
12
|
+
{isOpen ? (
|
|
13
|
+
<path
|
|
14
|
+
fillRule="evenodd"
|
|
15
|
+
d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
|
|
16
|
+
clipRule="evenodd"
|
|
17
|
+
/>
|
|
18
|
+
) : (
|
|
19
|
+
<path
|
|
20
|
+
fillRule="evenodd"
|
|
21
|
+
d="M3 5a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 10a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1zM3 15a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"
|
|
22
|
+
clipRule="evenodd"
|
|
23
|
+
/>
|
|
24
|
+
)}
|
|
25
|
+
</svg>
|
|
26
|
+
</button>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default MobileToggle;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { NavItem, NavItemComponentProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const NavItemComponent: React.FC<NavItemComponentProps> = ({
|
|
5
|
+
items,
|
|
6
|
+
isMobile,
|
|
7
|
+
activeSubmenu,
|
|
8
|
+
toggleSubmenu,
|
|
9
|
+
variant,
|
|
10
|
+
}) => {
|
|
11
|
+
const renderNavItems = (navItems: NavItem[], isSubmenu: boolean = false) => {
|
|
12
|
+
return navItems.map((item) => {
|
|
13
|
+
const hasChildren = item.children && item.children.length > 0;
|
|
14
|
+
|
|
15
|
+
if (item.customComponent) {
|
|
16
|
+
return (
|
|
17
|
+
<li
|
|
18
|
+
key={item.id}
|
|
19
|
+
className={`${isMobile ? "block w-full" : "relative"} ${
|
|
20
|
+
isSubmenu ? "w-full" : ""
|
|
21
|
+
}`}
|
|
22
|
+
>
|
|
23
|
+
{item.customComponent}
|
|
24
|
+
</li>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<li
|
|
30
|
+
key={item.id}
|
|
31
|
+
className={`${isMobile ? "block w-full" : "relative"} ${
|
|
32
|
+
isSubmenu ? "w-full" : ""
|
|
33
|
+
}`}
|
|
34
|
+
>
|
|
35
|
+
{hasChildren ? (
|
|
36
|
+
<>
|
|
37
|
+
<button
|
|
38
|
+
onClick={(e) => toggleSubmenu(item.id, e)}
|
|
39
|
+
className={`
|
|
40
|
+
flex items-center justify-between px-3 py-2 rounded-md
|
|
41
|
+
${item.highlight ? "font-medium text-blue-600" : ""}
|
|
42
|
+
${
|
|
43
|
+
isMobile
|
|
44
|
+
? "w-full text-left"
|
|
45
|
+
: "hover:bg-opacity-10 hover:bg-gray-200"
|
|
46
|
+
}
|
|
47
|
+
`}
|
|
48
|
+
>
|
|
49
|
+
<span className="flex items-center">
|
|
50
|
+
{item.icon && <span className="mr-2">{item.icon}</span>}
|
|
51
|
+
{item.label}
|
|
52
|
+
</span>
|
|
53
|
+
<svg
|
|
54
|
+
className={`ml-1 h-4 w-4 transition-transform ${
|
|
55
|
+
activeSubmenu === item.id ? "rotate-180" : ""
|
|
56
|
+
}`}
|
|
57
|
+
fill="none"
|
|
58
|
+
stroke="currentColor"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
>
|
|
61
|
+
<path
|
|
62
|
+
strokeLinecap="round"
|
|
63
|
+
strokeLinejoin="round"
|
|
64
|
+
strokeWidth={2}
|
|
65
|
+
d="M19 9l-7 7-7-7"
|
|
66
|
+
/>
|
|
67
|
+
</svg>
|
|
68
|
+
</button>
|
|
69
|
+
<div
|
|
70
|
+
className={`
|
|
71
|
+
${
|
|
72
|
+
isMobile
|
|
73
|
+
? "pl-4 border-l border-gray-200"
|
|
74
|
+
: "absolute left-0 min-w-max shadow-lg rounded-md py-2 bg-white z-50"
|
|
75
|
+
}
|
|
76
|
+
${
|
|
77
|
+
variant === "dark" && !isMobile
|
|
78
|
+
? "bg-gray-800 text-white"
|
|
79
|
+
: ""
|
|
80
|
+
}
|
|
81
|
+
${activeSubmenu === item.id ? "block" : "hidden"}
|
|
82
|
+
${!isMobile ? "mt-1" : ""}
|
|
83
|
+
`}
|
|
84
|
+
>
|
|
85
|
+
<ul className="w-full">
|
|
86
|
+
{renderNavItems(item.children || [], true)}
|
|
87
|
+
</ul>
|
|
88
|
+
</div>
|
|
89
|
+
</>
|
|
90
|
+
) : (
|
|
91
|
+
<a
|
|
92
|
+
href={item.href}
|
|
93
|
+
className={`
|
|
94
|
+
flex items-center px-3 py-2 rounded-md
|
|
95
|
+
${item.highlight ? "font-medium text-blue-600" : ""}
|
|
96
|
+
${
|
|
97
|
+
isMobile
|
|
98
|
+
? "block w-full"
|
|
99
|
+
: "hover:bg-opacity-10 hover:bg-gray-200"
|
|
100
|
+
}
|
|
101
|
+
`}
|
|
102
|
+
>
|
|
103
|
+
{item.icon && <span className="mr-2">{item.icon}</span>}
|
|
104
|
+
{item.label}
|
|
105
|
+
</a>
|
|
106
|
+
)}
|
|
107
|
+
</li>
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<ul className="md:flex md:space-x-1 flex-col md:flex-row">
|
|
114
|
+
{renderNavItems(items)}
|
|
115
|
+
</ul>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default NavItemComponent;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
|
+
import { SearchBarProps } from "./types";
|
|
3
|
+
|
|
4
|
+
const SearchBar: React.FC<SearchBarProps> = ({
|
|
5
|
+
variant,
|
|
6
|
+
searchQuery,
|
|
7
|
+
setSearchQuery,
|
|
8
|
+
onSearch,
|
|
9
|
+
}) => {
|
|
10
|
+
const handleSearch = useCallback(
|
|
11
|
+
(e: React.FormEvent) => {
|
|
12
|
+
e.preventDefault();
|
|
13
|
+
if (onSearch) {
|
|
14
|
+
onSearch(searchQuery);
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
[searchQuery, onSearch]
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<form onSubmit={handleSearch} className="relative md:ml-4 mt-3 md:mt-0">
|
|
22
|
+
<input
|
|
23
|
+
type="search"
|
|
24
|
+
placeholder="Search..."
|
|
25
|
+
value={searchQuery}
|
|
26
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
27
|
+
className={`
|
|
28
|
+
w-full md:w-auto pl-10 pr-4 py-2 rounded-md text-sm focus:outline-none
|
|
29
|
+
${
|
|
30
|
+
variant === "dark"
|
|
31
|
+
? "bg-gray-800 text-white"
|
|
32
|
+
: "bg-gray-100 text-gray-800"
|
|
33
|
+
}
|
|
34
|
+
`}
|
|
35
|
+
/>
|
|
36
|
+
<div className="absolute left-3 top-1/2 transform -translate-y-1/2">
|
|
37
|
+
<svg
|
|
38
|
+
className="h-4 w-4 text-gray-400"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
viewBox="0 0 24 24"
|
|
42
|
+
>
|
|
43
|
+
<path
|
|
44
|
+
strokeLinecap="round"
|
|
45
|
+
strokeLinejoin="round"
|
|
46
|
+
strokeWidth={2}
|
|
47
|
+
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
|
48
|
+
/>
|
|
49
|
+
</svg>
|
|
50
|
+
</div>
|
|
51
|
+
</form>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export default SearchBar;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const getThemeClasses = (variant: string) => {
|
|
2
|
+
switch (variant) {
|
|
3
|
+
case "dark":
|
|
4
|
+
return "bg-gray-900 text-white shadow-md";
|
|
5
|
+
case "colored":
|
|
6
|
+
return "bg-blue-600 text-white shadow-md";
|
|
7
|
+
default:
|
|
8
|
+
return "bg-white text-gray-800 shadow-md";
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const getPositionClasses = (position: string) => {
|
|
13
|
+
switch (position) {
|
|
14
|
+
case "fixed":
|
|
15
|
+
return "fixed top-0 left-0 right-0 z-50";
|
|
16
|
+
case "sticky":
|
|
17
|
+
return "sticky top-0 z-40";
|
|
18
|
+
default:
|
|
19
|
+
return "";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const getContainerClasses = (containerType: string) => {
|
|
24
|
+
return containerType === "contained" ? "max-w-6xl mx-auto px-4" : "px-4";
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { getContainerClasses, getPositionClasses, getThemeClasses };
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useEffect, useCallback, memo } from "react";
|
|
9
|
+
import NavItemComponent from "./NavItemComponent";
|
|
10
|
+
import SearchBar from "./SearchBar";
|
|
11
|
+
import AuthSection from "./AuthSection";
|
|
12
|
+
import MobileToggle from "./MobileToggle";
|
|
13
|
+
import { NavbarProps } from "./types";
|
|
14
|
+
import {
|
|
15
|
+
getContainerClasses,
|
|
16
|
+
getPositionClasses,
|
|
17
|
+
getThemeClasses,
|
|
18
|
+
} from "./helperFunctions";
|
|
19
|
+
|
|
20
|
+
const Navbar: React.FC<NavbarProps> = memo(
|
|
21
|
+
({
|
|
22
|
+
brand,
|
|
23
|
+
items,
|
|
24
|
+
variant = "light",
|
|
25
|
+
position = "sticky",
|
|
26
|
+
containerType = "contained",
|
|
27
|
+
showSearchBar = false,
|
|
28
|
+
showAuthButtons = false,
|
|
29
|
+
authState = "loggedOut",
|
|
30
|
+
userProfile,
|
|
31
|
+
rightSection,
|
|
32
|
+
leftSection,
|
|
33
|
+
centerSection,
|
|
34
|
+
onSearch,
|
|
35
|
+
onLogin,
|
|
36
|
+
onSignup,
|
|
37
|
+
onLogout,
|
|
38
|
+
onProfileClick,
|
|
39
|
+
}) => {
|
|
40
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
41
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
42
|
+
const [scrolled, setScrolled] = useState(false);
|
|
43
|
+
const [activeSubmenu, setActiveSubmenu] = useState<string | null>(null);
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
if (position !== "static") {
|
|
47
|
+
const handleScroll = () => {
|
|
48
|
+
setScrolled(window.scrollY > 20);
|
|
49
|
+
};
|
|
50
|
+
window.addEventListener("scroll", handleScroll);
|
|
51
|
+
return () => window.removeEventListener("scroll", handleScroll);
|
|
52
|
+
}
|
|
53
|
+
}, [position]);
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (isOpen) {
|
|
57
|
+
const handleClickOutside = (e: MouseEvent) => {
|
|
58
|
+
const target = e.target as HTMLElement;
|
|
59
|
+
if (
|
|
60
|
+
!target.closest(".navbar-menu") &&
|
|
61
|
+
!target.closest(".navbar-toggle")
|
|
62
|
+
) {
|
|
63
|
+
setIsOpen(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
67
|
+
return () =>
|
|
68
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
69
|
+
}
|
|
70
|
+
}, [isOpen]);
|
|
71
|
+
|
|
72
|
+
const toggleMenu = useCallback(() => {
|
|
73
|
+
setIsOpen((prev) => !prev);
|
|
74
|
+
}, []);
|
|
75
|
+
|
|
76
|
+
const toggleSubmenu = useCallback((id: string, e: React.MouseEvent) => {
|
|
77
|
+
e.preventDefault();
|
|
78
|
+
setActiveSubmenu((prev) => (prev === id ? null : id));
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<nav
|
|
83
|
+
className={`${getThemeClasses(variant)} ${getPositionClasses(
|
|
84
|
+
position
|
|
85
|
+
)} ${scrolled ? "shadow-md" : ""} transition-shadow duration-300`}
|
|
86
|
+
>
|
|
87
|
+
<div
|
|
88
|
+
className={`${getContainerClasses(
|
|
89
|
+
containerType
|
|
90
|
+
)} flex flex-wrap items-center justify-between py-3`}
|
|
91
|
+
>
|
|
92
|
+
<div className="flex items-center flex-shrink-0 mr-6">
|
|
93
|
+
{leftSection || (
|
|
94
|
+
<a href={brand.href} className="flex items-center">
|
|
95
|
+
{brand.logo && <div className="mr-2">{brand.logo}</div>}
|
|
96
|
+
<span className="text-lg font-semibold">{brand.name}</span>
|
|
97
|
+
</a>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<MobileToggle isOpen={isOpen} toggleMenu={toggleMenu} />
|
|
102
|
+
|
|
103
|
+
{centerSection && (
|
|
104
|
+
<div className="hidden md:block flex-grow-0 order-3 md:order-2">
|
|
105
|
+
{centerSection}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
|
|
109
|
+
<div
|
|
110
|
+
className={`navbar-menu w-full md:flex md:items-center md:w-auto order-last md:order-3 ${
|
|
111
|
+
isOpen ? "block" : "hidden"
|
|
112
|
+
} md:block`}
|
|
113
|
+
>
|
|
114
|
+
<div className="text-sm md:flex-grow mt-4 md:mt-0">
|
|
115
|
+
<NavItemComponent
|
|
116
|
+
items={items}
|
|
117
|
+
isMobile={!isOpen ? false : true}
|
|
118
|
+
activeSubmenu={activeSubmenu}
|
|
119
|
+
toggleSubmenu={toggleSubmenu}
|
|
120
|
+
variant={variant}
|
|
121
|
+
/>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div className="flex flex-col md:flex-row md:items-center">
|
|
125
|
+
{rightSection || (
|
|
126
|
+
<>
|
|
127
|
+
{showSearchBar && (
|
|
128
|
+
<SearchBar
|
|
129
|
+
variant={variant}
|
|
130
|
+
searchQuery={searchQuery}
|
|
131
|
+
setSearchQuery={setSearchQuery}
|
|
132
|
+
onSearch={onSearch}
|
|
133
|
+
/>
|
|
134
|
+
)}
|
|
135
|
+
<AuthSection
|
|
136
|
+
showAuthButtons={showAuthButtons}
|
|
137
|
+
authState={authState}
|
|
138
|
+
userProfile={userProfile}
|
|
139
|
+
variant={variant}
|
|
140
|
+
onLogin={onLogin}
|
|
141
|
+
onSignup={onSignup}
|
|
142
|
+
onLogout={onLogout}
|
|
143
|
+
onProfileClick={onProfileClick}
|
|
144
|
+
/>
|
|
145
|
+
</>
|
|
146
|
+
)}
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
</nav>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
Navbar.displayName = "Navbar";
|
|
156
|
+
export default Navbar;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
interface AuthSectionProps {
|
|
2
|
+
showAuthButtons: boolean;
|
|
3
|
+
authState: "loggedIn" | "loggedOut";
|
|
4
|
+
userProfile?: { name: string; avatar?: string };
|
|
5
|
+
variant: "light" | "dark" | "colored";
|
|
6
|
+
onLogin?: () => void;
|
|
7
|
+
onSignup?: () => void;
|
|
8
|
+
onLogout?: () => void;
|
|
9
|
+
onProfileClick?: () => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface MobileToggleProps {
|
|
13
|
+
isOpen: boolean;
|
|
14
|
+
toggleMenu: () => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface NavItem {
|
|
18
|
+
id: string;
|
|
19
|
+
label: string;
|
|
20
|
+
href: string;
|
|
21
|
+
icon?: React.ReactNode;
|
|
22
|
+
children?: NavItem[];
|
|
23
|
+
highlight?: boolean;
|
|
24
|
+
customComponent?: React.ReactNode;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface NavbarProps {
|
|
28
|
+
brand: { name: string; logo?: React.ReactNode; href: string };
|
|
29
|
+
items: NavItem[];
|
|
30
|
+
variant?: "light" | "dark" | "colored";
|
|
31
|
+
position?: "fixed" | "sticky" | "static";
|
|
32
|
+
containerType?: "full" | "contained";
|
|
33
|
+
showSearchBar?: boolean;
|
|
34
|
+
showAuthButtons?: boolean;
|
|
35
|
+
authState?: "loggedIn" | "loggedOut";
|
|
36
|
+
userProfile?: { name: string; avatar?: string };
|
|
37
|
+
rightSection?: React.ReactNode;
|
|
38
|
+
leftSection?: React.ReactNode;
|
|
39
|
+
centerSection?: React.ReactNode;
|
|
40
|
+
onSearch?: (query: string) => void;
|
|
41
|
+
onLogin?: () => void;
|
|
42
|
+
onSignup?: () => void;
|
|
43
|
+
onLogout?: () => void;
|
|
44
|
+
onProfileClick?: () => void;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface NavItemComponentProps {
|
|
48
|
+
items: NavItem[];
|
|
49
|
+
isMobile: boolean;
|
|
50
|
+
activeSubmenu: string | null;
|
|
51
|
+
toggleSubmenu: (id: string, e: React.MouseEvent) => void;
|
|
52
|
+
variant: "light" | "dark" | "colored";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
interface SearchBarProps {
|
|
56
|
+
variant: "light" | "dark" | "colored";
|
|
57
|
+
searchQuery: string;
|
|
58
|
+
setSearchQuery: (query: string) => void;
|
|
59
|
+
onSearch?: (query: string) => void;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export type {
|
|
63
|
+
SearchBarProps,
|
|
64
|
+
NavItemComponentProps,
|
|
65
|
+
NavItem,
|
|
66
|
+
MobileToggleProps,
|
|
67
|
+
AuthSectionProps,
|
|
68
|
+
NavbarProps,
|
|
69
|
+
};
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Grampro Business Services and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
import React, { useMemo, memo } from "react";
|
|
2
9
|
|
|
3
10
|
type SkeletonType = "text" | "circle" | "rectangle";
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const renderCell = (
|
|
2
|
-
rowData: any,
|
|
3
|
-
column: any,
|
|
4
|
-
rowIndex: number,
|
|
5
|
-
currentPage: number,
|
|
6
|
-
pageSettings: any,
|
|
7
|
-
rowChange?: any
|
|
8
|
-
) => {
|
|
9
|
-
const cellValue = rowData[column.field];
|
|
10
|
-
|
|
11
|
-
if (column.template) {
|
|
12
|
-
return (
|
|
13
|
-
<column.template
|
|
14
|
-
rowData={rowData}
|
|
15
|
-
rowIndex={rowIndex + currentPage * pageSettings.pageNumber}
|
|
16
|
-
rowChange={(changes: any) => {
|
|
17
|
-
if (rowChange) rowChange(changes);
|
|
18
|
-
}}
|
|
19
|
-
/>
|
|
20
|
-
);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (column.tooltip) {
|
|
24
|
-
return (
|
|
25
|
-
<span title={cellValue?.toString()}>
|
|
26
|
-
{column.tooltip ? cellValue.slice(0, 10) : cellValue}
|
|
27
|
-
</span>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return cellValue;
|
|
32
|
-
};
|