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 CHANGED
@@ -1,4 +1,4 @@
1
- # GBS Building Blocks 2.0 (v0.0.58)
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.58)
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
@@ -25,6 +25,7 @@ const CONFIG = {
25
25
  "MaterialInput",
26
26
  "ContextMenu",
27
27
  "Skeleton",
28
+ "Navbar",
28
29
  ],
29
30
  // Define component dependencies
30
31
  dependencies: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gbs-add-block",
3
- "version": "0.0.58",
3
+ "version": "0.0.60",
4
4
  "description": "React Component Library",
5
5
  "files": [
6
6
  "index.js",
@@ -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
- };