├── .firebaserc ├── .github └── workflows │ ├── firebase-hosting-merge.yml │ └── firebase-hosting-pull-request.yml ├── .gitignore ├── README.md ├── firebase.json ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.cjs ├── public └── vite.svg ├── src ├── App.tsx ├── assets │ └── react.svg ├── components │ ├── command-bar │ │ ├── actions │ │ │ ├── actions.ts │ │ │ └── index.ts │ │ ├── icons │ │ │ ├── home.tsx │ │ │ └── index.ts │ │ ├── index.ts │ │ ├── results │ │ │ ├── index.ts │ │ │ ├── result-item.tsx │ │ │ └── results-renderer.tsx │ │ └── wrapper │ │ │ ├── index.ts │ │ │ ├── wrapper.interface.ts │ │ │ └── wrapper.tsx │ ├── layout │ │ ├── Container.tsx │ │ ├── Navbar.tsx │ │ ├── index.ts │ │ └── navigation-links.tsx │ ├── ui-elements │ │ ├── CompanyLogo │ │ │ ├── CompanyLogo.tsx │ │ │ └── index.ts │ │ ├── HamburgerButton │ │ │ ├── HamburgerButton.tsx │ │ │ └── index.ts │ │ ├── KbarInput │ │ │ ├── KbarInput.tsx │ │ │ └── index.ts │ │ ├── MenuLinks │ │ │ ├── LinkSubItems.tsx │ │ │ ├── MenuLinks.tsx │ │ │ ├── index.ts │ │ │ └── interfaces.ts │ │ ├── MobileMenu │ │ │ ├── LinkSubItems.tsx │ │ │ ├── MobileMenu.tsx │ │ │ ├── index.ts │ │ │ └── interfaces.ts │ │ ├── User │ │ │ ├── User.tsx │ │ │ └── index.ts │ │ └── index.ts │ └── views │ │ ├── Changelog.tsx │ │ ├── Contact.tsx │ │ ├── External1.tsx │ │ ├── External2.tsx │ │ ├── Home.tsx │ │ ├── NotFound.tsx │ │ └── index.ts ├── index.css ├── main.tsx ├── navigation │ ├── index.ts │ └── navigation.tsx └── vite-env.d.ts ├── tailwind.config.cjs ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /.firebaserc: -------------------------------------------------------------------------------- 1 | { 2 | "projects": { 3 | "default": "awesome-react-navbar" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-merge.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on merge 5 | 'on': 6 | push: 7 | branches: 8 | - master 9 | jobs: 10 | build_and_deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - run: npm ci && npm run build 15 | - uses: FirebaseExtended/action-hosting-deploy@v0 16 | with: 17 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 18 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_AWESOME_REACT_NAVBAR }}' 19 | channelId: live 20 | projectId: awesome-react-navbar 21 | -------------------------------------------------------------------------------- /.github/workflows/firebase-hosting-pull-request.yml: -------------------------------------------------------------------------------- 1 | # This file was auto-generated by the Firebase CLI 2 | # https://github.com/firebase/firebase-tools 3 | 4 | name: Deploy to Firebase Hosting on PR 5 | 'on': pull_request 6 | jobs: 7 | build_and_preview: 8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: npm ci && npm run build 13 | - uses: FirebaseExtended/action-hosting-deploy@v0 14 | with: 15 | repoToken: '${{ secrets.GITHUB_TOKEN }}' 16 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_AWESOME_REACT_NAVBAR }}' 17 | projectId: awesome-react-navbar 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome React Navigation Bar 2 | 3 | A modern application's navigation bar. 4 | 5 | ## Purpose 6 | 7 | To learn to build great navigation bars using tailwind with modern design patterns. 8 | 9 | Used patterns: 10 | 11 | - Light / dark theme 12 | - Mega menu 13 | - K Bar 14 | 15 | ## Demo 16 | 17 | A live demo is available at [https://awesome-react-navbar.web.app/](https://awesome-react-navbar.web.app/) 18 | 19 | Screenshots: 20 | 21 | - Light theme: 22 | light 23 | 24 | - Dark theme: 25 | dark 26 | 27 | 28 | ## Used technologies 29 | 30 | - 🎁 **Repository:** regular Git repository 31 | - 🧰 **Frameworks:** React, Typescript 32 | - 🎨 **Styling:** tailwind 33 | 34 | ## How to run 35 | 36 | 1. Install [Node.js](https://nodejs.org/en/download/). 37 | 2. Install [Yarn](https://classic.yarnpkg.com/en/docs/install/). 38 | 3. Clone the repository. 39 | 4. Type and run: `yarn` in the root directory. 40 | 5. Type and run: `yarn dev` in the root directory. 41 | 6. Open `http://localhost:5173` in your browser to see the app. 42 | -------------------------------------------------------------------------------- /firebase.json: -------------------------------------------------------------------------------- 1 | { 2 | "hosting": { 3 | "public": "dist", 4 | "ignore": ["firebase.json", "**/.*", "**/node_modules/**"], 5 | "rewrites": [ 6 | { 7 | "source": "**", 8 | "destination": "/index.html" 9 | } 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Awesome React Navbar 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-react-navbar", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "preview": "vite preview", 10 | "generate-changelog": "changelog generate" 11 | }, 12 | "dependencies": { 13 | "@heroicons/react": "^2.0.16", 14 | "firebase": "^9.22.2", 15 | "kbar": "^0.1.0-beta.40", 16 | "react": "^18.2.0", 17 | "react-dom": "^18.2.0", 18 | "react-markdown": "^8.0.5", 19 | "react-router-dom": "^6.8.1", 20 | "@vercel/analytics": "^1.0.0" 21 | }, 22 | "devDependencies": { 23 | "@tailwindcss/typography": "^0.5.9", 24 | "@types/react": "^18.0.27", 25 | "@types/react-dom": "^18.0.10", 26 | "@vitejs/plugin-react-swc": "^3.0.0", 27 | "autoprefixer": "^10.4.13", 28 | "generate-changelog": "^1.8.0", 29 | "postcss": "^8.4.21", 30 | "tailwindcss": "^3.2.7", 31 | "typescript": "^4.9.3", 32 | "vite": "^4.1.0", 33 | "vite-plugin-pwa": "^0.14.4" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { Navigation } from "./navigation"; 2 | import { inject } from '@vercel/analytics'; 3 | 4 | export const App = () => { 5 | inject(); 6 | return ; 7 | }; 8 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/command-bar/actions/actions.ts: -------------------------------------------------------------------------------- 1 | import { createAction } from "kbar"; 2 | import { 3 | ClockIcon, 4 | PaperAirplaneIcon, 5 | ReceiptRefundIcon, 6 | HomeIcon, 7 | } from "@heroicons/react/24/outline"; 8 | import { useLocation } from "react-router-dom"; 9 | 10 | export const initialActions = [ 11 | // -------------------basic--------------------- 12 | { 13 | id: "homeAction", 14 | name: "Home", 15 | shortcut: ["h"], 16 | keywords: "back home main dashboard", 17 | section: "Basic", 18 | perform: () => (window.location.href = "/"), 19 | //icon: // ClockIcon, 20 | subtitle: "Subtitles can help add more context.", 21 | }, 22 | 23 | // -------------------add------------------- 24 | { 25 | id: "workTimeAction", 26 | name: "Work time", 27 | shortcut: ["a", "w"], 28 | keywords: "work time log add", 29 | section: "Add", 30 | subtitle: "Log and manage your work time.", 31 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 32 | }, 33 | { 34 | id: "vacationAction", 35 | name: "Vacation", 36 | shortcut: ["a", "v"], 37 | keywords: "vacation add", 38 | section: "Add", 39 | subtitle: "Log and manage your days off.", 40 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 41 | }, 42 | { 43 | id: "invoiceAction", 44 | name: "Invoice", 45 | shortcut: ["a", "i"], 46 | keywords: "invoice add", 47 | section: "Add", 48 | subtitle: "Log and manage your invoices.", 49 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 50 | }, 51 | { 52 | id: "documentsAction", 53 | name: "Document", 54 | shortcut: ["a", "d"], 55 | keywords: "document add", 56 | section: "Add", 57 | subtitle: "Log and manage your documents.", 58 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 59 | }, 60 | // -------------------project--------------------- 61 | { 62 | id: "projectCreateAction", 63 | name: "Create project", 64 | shortcut: ["p", "c"], 65 | keywords: "project create", 66 | section: "Project", 67 | subtitle: "Create new project.", 68 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 69 | }, 70 | { 71 | id: "projectManageAction", 72 | name: "Manage project", 73 | shortcut: ["p", "m"], 74 | keywords: "project manage", 75 | section: "Project", 76 | subtitle: "Manage project.", 77 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 78 | }, 79 | { 80 | id: "projectArchiveAction", 81 | name: "Archive project", 82 | shortcut: ["p", "a"], 83 | keywords: "project archive", 84 | section: "Project", 85 | subtitle: "Archive project.", 86 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 87 | }, 88 | // -----------------client----------------------- 89 | { 90 | id: "clientCreateAction", 91 | name: "Create client", 92 | shortcut: ["l", "c"], 93 | keywords: "project create", 94 | section: "Client", 95 | subtitle: "Create new project.", 96 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 97 | }, 98 | { 99 | id: "clientManageAction", 100 | name: "Manage client", 101 | shortcut: ["l", "m"], 102 | keywords: "client manage", 103 | section: "Client", 104 | subtitle: "Manage client.", 105 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 106 | }, 107 | { 108 | id: "clientArchiveAction", 109 | name: "Archive client", 110 | shortcut: ["l", "a"], 111 | keywords: "client archive", 112 | section: "Client", 113 | subtitle: "Archive client.", 114 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 115 | }, 116 | // ---------------employee------------------------- 117 | { 118 | id: "employeeCreateAction", 119 | name: "Create employee", 120 | shortcut: ["e", "c"], 121 | keywords: "employee create", 122 | section: "Employee", 123 | subtitle: "Create new employee.", 124 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 125 | }, 126 | { 127 | id: "employeeManageAction", 128 | name: "Manage employee", 129 | shortcut: ["e", "m"], 130 | keywords: "employee manage", 131 | section: "Employee", 132 | subtitle: "Manage employee.", 133 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 134 | }, 135 | { 136 | id: "employeeArchiveAction", 137 | name: "Archive employee", 138 | shortcut: ["e", "a"], 139 | keywords: "employee archive", 140 | section: "Employee", 141 | subtitle: "Archive employee.", 142 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 143 | }, 144 | // ---------------employee------------------------- 145 | { 146 | id: "employeeCreateAction", 147 | name: "Create employee", 148 | shortcut: ["e", "c"], 149 | keywords: "employee create", 150 | section: "Employee", 151 | subtitle: "Create new employee.", 152 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 153 | }, 154 | { 155 | id: "employeeManageAction", 156 | name: "Manage employee", 157 | shortcut: ["e", "m"], 158 | keywords: "employee manage", 159 | section: "Employee", 160 | subtitle: "Manage employee.", 161 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 162 | }, 163 | { 164 | id: "employeeArchiveAction", 165 | name: "Archive employee", 166 | shortcut: ["e", "a"], 167 | keywords: "employee archive", 168 | section: "Employee", 169 | subtitle: "Archive employee.", 170 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 171 | }, 172 | // ---------------employee------------------------- 173 | { 174 | id: "employeeCreateAction", 175 | name: "Create employee", 176 | shortcut: ["e", "c"], 177 | keywords: "employee create", 178 | section: "Employee", 179 | subtitle: "Create new employee.", 180 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 181 | }, 182 | { 183 | id: "employeeManageAction", 184 | name: "Manage employee", 185 | shortcut: ["e", "m"], 186 | keywords: "employee manage", 187 | section: "Employee", 188 | subtitle: "Manage employee.", 189 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 190 | }, 191 | { 192 | id: "employeeArchiveAction", 193 | name: "Archive employee", 194 | shortcut: ["e", "a"], 195 | keywords: "employee archive", 196 | section: "Employee", 197 | subtitle: "Archive employee.", 198 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 199 | }, 200 | // ---------------company------------------------- 201 | { 202 | id: "paperworkAction", 203 | name: "Paperwork", 204 | shortcut: ["c", "p"], 205 | keywords: "paperwork", 206 | section: "Company", 207 | subtitle: "Paperwork.", 208 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 209 | }, 210 | { 211 | id: "licensesAction", 212 | name: "Licenses", 213 | shortcut: ["c", "l"], 214 | keywords: "licenses", 215 | section: "Company", 216 | subtitle: "Licenses.", 217 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 218 | }, 219 | { 220 | id: "jiraAction", 221 | name: "Jira tickets", 222 | shortcut: ["c", "j"], 223 | keywords: "jira tickets", 224 | section: "Company", 225 | subtitle: "Jira tickets.", 226 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 227 | }, 228 | { 229 | id: "perksBenefitsAction", 230 | name: "Perks and Benefits", 231 | shortcut: ["c", "b"], 232 | keywords: "perks benefits", 233 | section: "Company", 234 | subtitle: "Perks and Benefits.", 235 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 236 | }, 237 | { 238 | id: "referralsRewardsAction", 239 | name: "Referrals and Rewards", 240 | shortcut: ["c", "r"], 241 | keywords: "referrals rewards", 242 | section: "Company", 243 | subtitle: "Referrals and Rewards.", 244 | perform: () => window.open("https://timc1.github.io/kbar/", "_blank"), 245 | }, 246 | 247 | createAction({ 248 | name: "This repo on GitHub", 249 | shortcut: ["g", "h"], 250 | keywords: "github repo source code", 251 | section: "Links", 252 | perform: () => window.open("https://github.com/timc1/kbar", "_blank"), 253 | }), 254 | ]; 255 | -------------------------------------------------------------------------------- /src/components/command-bar/actions/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions"; 2 | -------------------------------------------------------------------------------- /src/components/command-bar/icons/home.tsx: -------------------------------------------------------------------------------- 1 | export const HomeIcon = () => ( 2 | 3 | 7 | 8 | ); 9 | -------------------------------------------------------------------------------- /src/components/command-bar/icons/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./home"; 2 | -------------------------------------------------------------------------------- /src/components/command-bar/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./actions"; 2 | export * from "./wrapper"; 3 | export * from "./icons"; 4 | -------------------------------------------------------------------------------- /src/components/command-bar/results/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./result-item"; 2 | export * from "./results-renderer"; 3 | -------------------------------------------------------------------------------- /src/components/command-bar/results/result-item.tsx: -------------------------------------------------------------------------------- 1 | import { ActionId, ActionImpl } from "kbar"; 2 | import React from "react"; 3 | 4 | export const ResultItem = React.forwardRef( 5 | ( 6 | { 7 | action, 8 | active, 9 | currentRootActionId, 10 | }: { 11 | action: ActionImpl; 12 | active: boolean; 13 | currentRootActionId: ActionId; 14 | }, 15 | ref: React.Ref 16 | ) => { 17 | const ancestors = React.useMemo(() => { 18 | if (!currentRootActionId) return action.ancestors; 19 | const index = action.ancestors.findIndex( 20 | (ancestor) => ancestor.id === currentRootActionId 21 | ); 22 | return action.ancestors.slice(index + 1); 23 | }, [action.ancestors, currentRootActionId]); 24 | 25 | return ( 26 |
34 |
35 | {action.icon} 36 |
37 |
38 | {ancestors.length > 0 && 39 | ancestors.map((ancestor) => ( 40 | 41 | 42 | {ancestor.name} 43 | 44 | 45 | 46 | ))} 47 | 48 | {action.name} 49 | 50 |
51 | {action.subtitle && ( 52 | 53 | {action.subtitle} 54 | 55 | )} 56 |
57 |
58 | {action.shortcut?.length ? ( 59 |
60 | {action.shortcut.map((sc) => ( 61 | 69 | {sc} 70 | 71 | ))} 72 |
73 | ) : null} 74 |
75 | ); 76 | } 77 | ); 78 | -------------------------------------------------------------------------------- /src/components/command-bar/results/results-renderer.tsx: -------------------------------------------------------------------------------- 1 | import { KBarResults, useMatches } from "kbar"; 2 | import { ResultItem } from "./result-item"; 3 | 4 | export const RenderResults = () => { 5 | const { results, rootActionId } = useMatches(); 6 | 7 | return ( 8 | 11 | typeof item === "string" ? ( 12 |
{item}
13 | ) : ( 14 | 19 | ) 20 | } 21 | /> 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/components/command-bar/wrapper/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./wrapper"; 2 | export * from "./wrapper.interface"; 3 | -------------------------------------------------------------------------------- /src/components/command-bar/wrapper/wrapper.interface.ts: -------------------------------------------------------------------------------- 1 | export interface KBarWrapperProps { 2 | children: React.ReactNode; 3 | } 4 | -------------------------------------------------------------------------------- /src/components/command-bar/wrapper/wrapper.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | KBarProvider, 3 | KBarPortal, 4 | KBarPositioner, 5 | KBarAnimator, 6 | KBarSearch, 7 | } from "kbar"; 8 | import { initialActions } from "../actions"; 9 | 10 | import { RenderResults } from "../results"; 11 | import { KBarWrapperProps } from "./wrapper.interface"; 12 | 13 | export const KBarWrapper = (props: KBarWrapperProps) => { 14 | const { children } = props; 15 | return ( 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {children} 26 | 27 | ); 28 | }; 29 | -------------------------------------------------------------------------------- /src/components/layout/Container.tsx: -------------------------------------------------------------------------------- 1 | import { Navbar } from "./Navbar"; 2 | import { Outlet } from "react-router-dom"; 3 | 4 | export const Container = () => { 5 | return ( 6 | <> 7 | 8 | 9 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/layout/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import { useLocation } from "react-router-dom"; 3 | import { 4 | CompanyLogo, 5 | HamburgerButton, 6 | KbarInput, 7 | MenuLinks, 8 | MobileMenu, 9 | } from "../ui-elements"; 10 | import User from "../ui-elements/User/User"; 11 | import { navigationLinks } from "./navigation-links"; 12 | 13 | export const Navbar = () => { 14 | const location = useLocation(); 15 | 16 | const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); 17 | 18 | useEffect(() => { 19 | setIsMobileMenuOpen(false); 20 | }, [location]); 21 | 22 | return ( 23 | <> 24 | 54 | 55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Container"; 2 | export * from "./Navbar"; 3 | -------------------------------------------------------------------------------- /src/components/layout/navigation-links.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ClockIcon, 3 | PaperAirplaneIcon, 4 | ReceiptRefundIcon, 5 | DocumentIcon, 6 | PlusCircleIcon, 7 | ListBulletIcon, 8 | DocumentTextIcon, 9 | CommandLineIcon, 10 | TicketIcon, 11 | HeartIcon, 12 | GiftIcon, 13 | } from "@heroicons/react/24/outline"; 14 | 15 | export const navigationLinks = [ 16 | { 17 | name: "Add", 18 | subLinks: [ 19 | { 20 | name: "Work time", 21 | description: "Log and manage your work time", 22 | link: "/work-time", 23 | color: "bg-teal-300 dark:bg-teal-700", 24 | icon: , 25 | }, 26 | { 27 | name: "Document", 28 | description: "Add and manage your documents", 29 | link: "/other-documents", 30 | color: "bg-indigo-300 dark:bg-indigo-700", 31 | icon: , 32 | }, 33 | { 34 | name: "Vacation", 35 | description: "Add and manage your vacation", 36 | link: "/vacation", 37 | color: "bg-amber-300 dark:bg-amber-700", 38 | icon: , 39 | }, 40 | { 41 | name: "Invoice", 42 | description: "Add and manage your invoices", 43 | link: "/invoices", 44 | color: "bg-rose-300 dark:bg-rose-700", 45 | icon: , 46 | }, 47 | ], 48 | }, 49 | { 50 | name: "Projects", 51 | subLinks: [ 52 | { 53 | name: "Create", 54 | description: "Create a new project", 55 | link: "/create-project", 56 | color: "bg-green-300 dark:bg-green-700", 57 | icon: , 58 | }, 59 | { 60 | name: "Manage", 61 | description: "Manage projects", 62 | link: "/manage-projects", 63 | color: "bg-blue-300 dark:bg-blue-700", 64 | icon: , 65 | }, 66 | { 67 | name: "Archive", 68 | description: "Manage all archived projects", 69 | link: "/archive-projects", 70 | color: "bg-red-300 dark:bg-red-700", 71 | icon: , 72 | }, 73 | ], 74 | }, 75 | { 76 | name: "Clients", 77 | subLinks: [ 78 | { 79 | name: "Create", 80 | description: "Create a new client", 81 | link: "/create-client", 82 | color: "bg-green-300 dark:bg-green-700", 83 | icon: , 84 | }, 85 | { 86 | name: "Manage", 87 | description: "Manage clients", 88 | link: "/manage-clients", 89 | color: "bg-blue-300 dark:bg-blue-700", 90 | icon: , 91 | }, 92 | { 93 | name: "Archive", 94 | description: "Manage all archived clients", 95 | link: "/archive-clients", 96 | color: "bg-red-300 dark:bg-red-700", 97 | icon: , 98 | }, 99 | ], 100 | }, 101 | { 102 | name: "Employees", 103 | subLinks: [ 104 | { 105 | name: "Create", 106 | description: "Create a new employee", 107 | link: "/create-employee", 108 | color: "bg-green-300 dark:bg-green-700", 109 | icon: , 110 | }, 111 | { 112 | name: "Manage", 113 | description: "Manage employees", 114 | link: "/manage-employees", 115 | color: "bg-blue-300 dark:bg-blue-700", 116 | icon: , 117 | }, 118 | { 119 | name: "Archive", 120 | description: "Manage all archived employees", 121 | link: "/archive-employees", 122 | color: "bg-red-300 dark:bg-red-700", 123 | icon: , 124 | }, 125 | ], 126 | }, 127 | { 128 | name: "Company", 129 | subLinks: [ 130 | { 131 | name: "Paperwork", 132 | description: "Manage documents and consents", 133 | link: "/company-paperwork", 134 | color: "bg-teal-300 dark:bg-teal-700", 135 | icon: , 136 | }, 137 | { 138 | name: "Licenses", 139 | description: "Manage your software licenses and subscriptions", 140 | link: "/company-licenses", 141 | color: "bg-pink-300 dark:bg-pink-700", 142 | icon: , 143 | }, 144 | { 145 | name: "Jira tickets", 146 | description: "Create and edit your Jira tickets", 147 | link: "/company-jira-tickets", 148 | color: "bg-sky-300 dark:bg-sky-700", 149 | icon: , 150 | }, 151 | { 152 | name: "Perks and benefits", 153 | description: "Manage your company perks and benefits", 154 | link: "/company-perks-and-benefits", 155 | color: "bg-green-300 dark:bg-green-700", 156 | icon: , 157 | }, 158 | { 159 | name: "Referrals and rewards", 160 | description: "Manage your company referrals and rewards", 161 | link: "/company-referrals-and-rewards", 162 | color: "bg-red-300 dark:bg-red-700", 163 | icon: , 164 | }, 165 | ], 166 | extraLinks: [ 167 | { 168 | name: "Company details", 169 | link: "/company-details", 170 | }, 171 | { 172 | name: "Company structure", 173 | link: "/company-structure", 174 | }, 175 | { 176 | name: "Company blog", 177 | link: "/company-blog", 178 | }, 179 | { 180 | name: "Company social media", 181 | link: "/company-social-media", 182 | }, 183 | ], 184 | }, 185 | ]; 186 | -------------------------------------------------------------------------------- /src/components/ui-elements/CompanyLogo/CompanyLogo.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | export const CompanyLogo = () => { 4 | return ( 5 | 6 | 7 | My Company 8 | 9 | 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/ui-elements/CompanyLogo/index.ts: -------------------------------------------------------------------------------- 1 | export * from './CompanyLogo' -------------------------------------------------------------------------------- /src/components/ui-elements/HamburgerButton/HamburgerButton.tsx: -------------------------------------------------------------------------------- 1 | import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline"; 2 | import { useState } from "react"; 3 | 4 | export interface HamburgerButtonProps { 5 | isOpen: boolean; 6 | onClick: () => void; 7 | } 8 | 9 | export const HamburgerButton = ({ isOpen, onClick }: HamburgerButtonProps) => { 10 | return ( 11 | 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/ui-elements/HamburgerButton/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./HamburgerButton"; 2 | -------------------------------------------------------------------------------- /src/components/ui-elements/KbarInput/KbarInput.tsx: -------------------------------------------------------------------------------- 1 | import { useKBar } from "kbar"; 2 | import { MagnifyingGlassIcon } from "@heroicons/react/24/outline"; 3 | 4 | export const KbarInput = () => { 5 | const { query } = useKBar(); 6 | 7 | const onClick = () => { 8 | query.toggle(); 9 | }; 10 | 11 | return ( 12 | 24 | ); 25 | }; 26 | -------------------------------------------------------------------------------- /src/components/ui-elements/KbarInput/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./KbarInput"; 2 | -------------------------------------------------------------------------------- /src/components/ui-elements/MenuLinks/LinkSubItems.tsx: -------------------------------------------------------------------------------- 1 | import { SubLinksBoxProps } from "./interfaces"; 2 | import { Link } from "react-router-dom"; 3 | 4 | export const SubLinksBox = (props: SubLinksBoxProps) => { 5 | const { subLinks, extraLinks } = props; 6 | return ( 7 |
8 |
    9 | {subLinks.map((subLink) => ( 10 |
  • 14 | 15 |
    18 |
    19 | {subLink.icon} 20 |
    21 |
    22 |
    23 |

    24 | {subLink.name} 25 |

    26 |

    27 | {subLink.description} 28 |

    29 |
    30 | 31 |
  • 32 | ))} 33 |
34 | {extraLinks && ( 35 |
36 |
37 |
    38 | {extraLinks?.map((extraLink) => ( 39 | 40 |
  • 44 | {extraLink.name} 45 |
  • 46 | 47 | ))} 48 |
49 |
50 | )} 51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/components/ui-elements/MenuLinks/MenuLinks.tsx: -------------------------------------------------------------------------------- 1 | import { MenuLinksProps } from "./interfaces"; 2 | import { SubLinksBox } from "./LinkSubItems"; 3 | 4 | export const MenuLinks = ({ menuLinks }: MenuLinksProps) => { 5 | return ( 6 |
    7 | {menuLinks.map((link) => ( 8 |
    9 |
  • 10 | {link.name} 11 |
  • 12 |
    13 | 17 |
    18 |
    19 | ))} 20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/ui-elements/MenuLinks/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MenuLinks"; 2 | -------------------------------------------------------------------------------- /src/components/ui-elements/MenuLinks/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface MenuLinksProps { 2 | menuLinks: MenuLinks[]; 3 | } 4 | 5 | export interface MenuLinks { 6 | name: string; 7 | subLinks: SubLink[]; 8 | extraLinks?: ExtraLink[]; 9 | } 10 | 11 | export interface SubLinksBoxProps { 12 | subLinks: SubLink[]; 13 | extraLinks?: ExtraLink[]; 14 | } 15 | 16 | export interface SubLink { 17 | name: string; 18 | description: string; 19 | link: string; 20 | color?: string; 21 | icon: JSX.Element; 22 | } 23 | 24 | export interface ExtraLink { 25 | name: string; 26 | link: string; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/ui-elements/MobileMenu/LinkSubItems.tsx: -------------------------------------------------------------------------------- 1 | import { SubLinksBoxProps } from "./interfaces"; 2 | 3 | export const SubLinksBox = (props: SubLinksBoxProps) => { 4 | const { subLinks, extraLinks } = props; 5 | return ( 6 |
7 |
    8 | {subLinks.map((subLink) => ( 9 |
  • 13 |
    16 |
    17 | {subLink.icon} 18 |
    19 |
    20 |
    21 |

    {subLink.name}

    22 |

    23 | {subLink.description} 24 |

    25 |
    26 |
  • 27 | ))} 28 |
29 | {extraLinks && ( 30 |
31 |
32 |
    33 | {extraLinks?.map((extraLink) => ( 34 |
  • 38 | {extraLink.name} 39 |
  • 40 | ))} 41 |
42 |
43 | )} 44 |
45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/components/ui-elements/MobileMenu/MobileMenu.tsx: -------------------------------------------------------------------------------- 1 | import { MenuLinks } from "./interfaces"; 2 | import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/outline"; 3 | import { useState } from "react"; 4 | import { Link } from "react-router-dom"; 5 | 6 | interface MobileMenuProps { 7 | menuLinks: MenuLinks[]; 8 | } 9 | 10 | export const MobileMenu = ({ menuLinks }: MobileMenuProps) => { 11 | const [expandedLinkId, setExpandedLinkId] = useState(null); 12 | 13 | const onMenuItemClick = (index: number) => { 14 | if (expandedLinkId === index) { 15 | setExpandedLinkId(null); 16 | } else { 17 | setExpandedLinkId(index); 18 | } 19 | }; 20 | 21 | return ( 22 |
23 |
24 |
    25 | {menuLinks.map((link, index) => ( 26 |
    27 |
  • onMenuItemClick(index)} 30 | > 31 |
    32 |

    {link.name}

    33 |
    34 | {expandedLinkId === index ? ( 35 | 36 | ) : ( 37 | 38 | )} 39 |
    40 |
    41 | {expandedLinkId === index && ( 42 |
    43 |
      44 | {link.subLinks.map((subLink) => ( 45 |
    • 49 | 50 |
      {subLink.icon}
      51 |
      52 |

      {subLink.name}

      53 |

      54 | {subLink.description} 55 |

      56 |
      57 | 58 |
    • 59 | ))} 60 |
    61 |
    62 | )} 63 |
  • 64 |
    65 | ))} 66 |
67 |
68 |
69 | ); 70 | }; 71 | -------------------------------------------------------------------------------- /src/components/ui-elements/MobileMenu/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./MobileMenu"; 2 | -------------------------------------------------------------------------------- /src/components/ui-elements/MobileMenu/interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface MenuLinksProps { 2 | menuLinks: MenuLinks[]; 3 | } 4 | 5 | export interface MenuLinks { 6 | name: string; 7 | subLinks: SubLink[]; 8 | extraLinks?: ExtraLink[]; 9 | } 10 | 11 | export interface SubLinksBoxProps { 12 | subLinks: SubLink[]; 13 | extraLinks?: ExtraLink[]; 14 | } 15 | 16 | export interface SubLink { 17 | name: string; 18 | description: string; 19 | link: string; 20 | color?: string; 21 | icon: JSX.Element; 22 | } 23 | 24 | export interface ExtraLink { 25 | name: string; 26 | link: string; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/ui-elements/User/User.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | IdentificationIcon, 3 | SunIcon, 4 | MoonIcon, 5 | AdjustmentsVerticalIcon, 6 | ExclamationCircleIcon, 7 | } from "@heroicons/react/24/outline"; 8 | import { useState } from "react"; 9 | 10 | const User = () => { 11 | const [theme, setTheme] = useState(localStorage.getItem("theme")); 12 | 13 | const ms = new Date().getUTCMilliseconds(); 14 | 15 | const items = [ 16 | { 17 | title: "Profile", 18 | icon: , 19 | color: "bg-indigo-300 dark:bg-indigo-800", 20 | onclick: () => {}, 21 | }, 22 | { 23 | title: theme === "light" ? "Dark theme" : "Light theme", 24 | icon: theme === "light" ? : , 25 | color: "bg-teal-300 dark:bg-teal-800", 26 | onclick: () => onChangeThemeClick(), 27 | }, 28 | { 29 | title: "Settings", 30 | icon: , 31 | color: "bg-fuchsia-300 dark:bg-fuchsia-800", 32 | onclick: () => {}, 33 | }, 34 | { 35 | title: "Logout", 36 | icon: , 37 | color: "bg-red-300 dark:bg-red-800", 38 | onclick: () => {}, 39 | }, 40 | ]; 41 | 42 | const onChangeThemeClick = () => { 43 | const newTheme = theme === "light" ? "dark" : "light"; 44 | setTheme(newTheme); 45 | 46 | if (newTheme === "dark") { 47 | document.documentElement.classList.remove("light"); 48 | document.documentElement.classList.add("dark"); 49 | } else { 50 | document.documentElement.classList.remove("dark"); 51 | document.documentElement.classList.add("light"); 52 | } 53 | }; 54 | 55 | return ( 56 |
57 |
58 | 62 |

Steve

63 |
64 |
    65 | {items.map((item) => ( 66 |
  • 71 |
    74 |
    75 | {item.icon} 76 |
    77 |
    78 |

    79 | {item.title} 80 |

    81 |
  • 82 | ))} 83 |
84 |
85 | ); 86 | }; 87 | 88 | export default User; 89 | -------------------------------------------------------------------------------- /src/components/ui-elements/User/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./User"; 2 | -------------------------------------------------------------------------------- /src/components/ui-elements/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./User"; 2 | export * from "./KbarInput"; 3 | export * from "./MenuLinks"; 4 | export * from "./CompanyLogo"; 5 | export * from "./HamburgerButton"; 6 | export * from "./MobileMenu"; 7 | -------------------------------------------------------------------------------- /src/components/views/Changelog.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import ReactMarkdown from "react-markdown"; 3 | 4 | export const Changelog = () => { 5 | const [content, setContent] = useState(""); 6 | 7 | fetch( 8 | "https://raw.githubusercontent.com/wojciechmarek/AwesomeReactNavbar/master/CHANGELOG.md" 9 | ) 10 | .then((res) => res.text()) 11 | .then((text) => setContent(text)); 12 | 13 | return ( 14 |
15 |
16 | 17 | {content} 18 | 19 |
20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/views/Contact.tsx: -------------------------------------------------------------------------------- 1 | export const Contact = () => { 2 | return ( 3 |
4 |

5 | Contact 6 |

7 |
8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/views/External1.tsx: -------------------------------------------------------------------------------- 1 | export const External1 = () => { 2 | return ( 3 |
4 | 8 |
9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/views/External2.tsx: -------------------------------------------------------------------------------- 1 | export const External2 = () => { 2 | return ( 3 |
4 | 5 |
6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /src/components/views/Home.tsx: -------------------------------------------------------------------------------- 1 | export const Home = () => { 2 | return ( 3 |
4 |

5 | Home 6 |

7 |
8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /src/components/views/NotFound.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "react-router-dom"; 2 | 3 | export const NotFound = () => { 4 | return ( 5 |
6 |

7 | 404 8 |

9 |

10 | 11 | The page you are looking for does not exist. 12 | 13 |

14 | 18 | Go to Home 19 | 20 |
21 | ); 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/views/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./Home"; 2 | export * from "./Changelog"; 3 | export * from "./Contact"; 4 | export * from "./External1"; 5 | export * from "./External2"; 6 | export * from "./NotFound"; 7 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | :root { 6 | color-scheme: light dark; 7 | 8 | font-synthesis: none; 9 | text-rendering: optimizeLegibility; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | -webkit-text-size-adjust: 100%; 13 | } 14 | 15 | body { 16 | margin: 0; 17 | padding: 0; 18 | } 19 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import { App } from "./App"; 4 | import "./index.css"; 5 | import { KBarWrapper } from "./components/command-bar"; 6 | 7 | // Import the functions you need from the SDKs you need 8 | import { initializeApp } from "firebase/app"; 9 | import { getAnalytics } from "firebase/analytics"; 10 | // TODO: Add SDKs for Firebase products that you want to use 11 | // https://firebase.google.com/docs/web/setup#available-libraries 12 | 13 | // Your web app's Firebase configuration 14 | // For Firebase JS SDK v7.20.0 and later, measurementId is optional 15 | const firebaseConfig = { 16 | apiKey: "AIzaSyCa07zE09ONmDK-UtdFJeug2oPoWy5Lcbk", 17 | authDomain: "awesome-react-navbar.firebaseapp.com", 18 | projectId: "awesome-react-navbar", 19 | storageBucket: "awesome-react-navbar.appspot.com", 20 | messagingSenderId: "71219047347", 21 | appId: "1:71219047347:web:82950dedc2ae77b85879c6", 22 | measurementId: "G-EYP11S18BM" 23 | }; 24 | 25 | // Initialize Firebase 26 | const app = initializeApp(firebaseConfig); 27 | const analytics = getAnalytics(app); 28 | 29 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 30 | 31 | 32 | 33 | 34 | 35 | ); 36 | -------------------------------------------------------------------------------- /src/navigation/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./navigation"; 2 | -------------------------------------------------------------------------------- /src/navigation/navigation.tsx: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 2 | import { Container } from "../components/layout"; 3 | import { 4 | Changelog, 5 | Contact, 6 | External1, 7 | External2, 8 | NotFound, 9 | Home, 10 | } from "../components/views"; 11 | 12 | export const Navigation = () => ( 13 | 14 | 15 | }> 16 | } /> 17 | } /> 18 | } /> 19 | } /> 20 | } /> 21 | } /> 22 | 23 | 24 | 25 | ); 26 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /tailwind.config.cjs: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | module.exports = { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | darkMode: "class", 5 | theme: { 6 | extend: {}, 7 | }, 8 | plugins: [require("@tailwindcss/typography")], 9 | }; 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react-swc"; 3 | import { VitePWA } from "vite-plugin-pwa"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | react(), 9 | VitePWA({ 10 | registerType: "autoUpdate", 11 | devOptions: { 12 | enabled: true, 13 | }, 14 | }), 15 | ], 16 | }); 17 | --------------------------------------------------------------------------------