Skip to content

Commit 234bdd3

Browse files
committed
feat: command palette 2
1 parent 5aa1e5f commit 234bdd3

File tree

5 files changed

+101
-3
lines changed

5 files changed

+101
-3
lines changed

src/App.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,28 @@ import AnimatedRoutes from './components/AnimatedRoutes';
55
import { ToastProvider } from './context/ToastContext';
66
import ScrollToTop from './components/ScrollToTop';
77
import ContactModal from './components/ContactModal';
8+
import GenericModal from './components/GenericModal';
89
import { AnimationProvider } from './context/AnimationContext'; // Import AnimationProvider
910

1011
function App() {
1112
const [isModalOpen, setIsModalOpen] = useState(false);
1213
const [isSearchVisible, setIsSearchVisible] = useState(false);
14+
const [isGenericModalOpen, setIsGenericModalOpen] = useState(false);
15+
const [genericModalContent, setGenericModalContent] = useState({ title: '', content: null });
1316

1417
const toggleModal = () => {
1518
setIsModalOpen(!isModalOpen);
1619
};
1720

21+
const openGenericModal = (title, content) => {
22+
setGenericModalContent({ title, content });
23+
setIsGenericModalOpen(true);
24+
};
25+
26+
const closeGenericModal = () => {
27+
setIsGenericModalOpen(false);
28+
};
29+
1830
const toggleSearch = () => {
1931
setIsSearchVisible(!isSearchVisible);
2032
window.scrollTo({ top: 0, behavior: 'smooth' }); // Scroll to top
@@ -31,10 +43,18 @@ function App() {
3143
toggleModal={toggleModal}
3244
isSearchVisible={isSearchVisible}
3345
toggleSearch={toggleSearch}
46+
openGenericModal={openGenericModal}
3447
>
3548
<AnimatedRoutes />
3649
</Layout>
3750
<ContactModal isOpen={isModalOpen} onClose={toggleModal} />
51+
<GenericModal
52+
isOpen={isGenericModalOpen}
53+
onClose={closeGenericModal}
54+
title={genericModalContent.title}
55+
>
56+
{genericModalContent.content}
57+
</GenericModal>
3858
</ToastProvider>
3959
</Router>
4060
</AnimationProvider>

src/components/CommandPalette.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ import useSearchableData from '../hooks/useSearchableData';
55
import { useAnimation } from '../context/AnimationContext';
66
import { useToast } from '../hooks/useToast';
77
import { SIDEBAR_KEYS, remove as removeLocalStorageItem } from '../utils/LocalStorageManager';
8+
import { version } from '../version'; // Import the version
89

9-
const CommandPalette = ({ isOpen, setIsOpen }) => {
10+
const CommandPalette = ({ isOpen, setIsOpen, openGenericModal }) => {
1011
const [searchTerm, setSearchTerm] = useState('');
1112
const [selectedIndex, setSelectedIndex] = useState(0);
1213
const { items, isLoading } = useSearchableData();
@@ -86,6 +87,29 @@ const CommandPalette = ({ isOpen, setIsOpen }) => {
8687
case 'openLinkedIn':
8788
window.open('https://tr.linkedin.com/in/ahmed-samil-bulbul', '_blank', 'noopener,noreferrer');
8889
break;
90+
case 'scrollToTop':
91+
window.scrollTo({ top: 0, behavior: 'smooth' });
92+
break;
93+
case 'scrollToBottom':
94+
window.scrollTo({ top: document.documentElement.scrollHeight, behavior: 'smooth' });
95+
break;
96+
case 'showSiteStats':
97+
const postCount = items.filter(i => i.type === 'post').length;
98+
const projectCount = items.filter(i => i.type === 'project').length;
99+
const logCount = items.filter(i => i.type === 'log').length;
100+
const appCount = items.filter(i => i.type === 'app').length;
101+
openGenericModal('Site Statistics', (
102+
<div>
103+
<p><strong>Posts:</strong> {postCount}</p>
104+
<p><strong>Projects:</strong> {projectCount}</p>
105+
<p><strong>Logs:</strong> {logCount}</p>
106+
<p><strong>Apps:</strong> {appCount}</p>
107+
</div>
108+
));
109+
break;
110+
case 'showVersion':
111+
openGenericModal('Application Version', <p>Version: <strong>v{version}</strong></p>);
112+
break;
89113
default:
90114
break;
91115
}

src/components/GenericModal.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React, { useState, useEffect } from 'react';
2+
import './ContactModal.css'; // Reusing the same CSS for animations
3+
import { XIcon } from '@phosphor-icons/react';
4+
5+
const GenericModal = ({ isOpen, onClose, title, children }) => {
6+
const [isClosing, setIsClosing] = useState(false);
7+
8+
useEffect(() => {
9+
if (!isOpen) {
10+
setIsClosing(false);
11+
}
12+
}, [isOpen]);
13+
14+
const handleClose = () => {
15+
setIsClosing(true);
16+
setTimeout(() => {
17+
onClose();
18+
setIsClosing(false);
19+
}, 300); // Corresponds to animation duration
20+
};
21+
22+
if (!isOpen) {
23+
return null;
24+
}
25+
26+
return (
27+
<div
28+
className={`modal-overlay ${isOpen && !isClosing ? 'fade-in' : 'fade-out'}`}
29+
onClick={handleClose}
30+
>
31+
<div
32+
className={`modal-content ${isOpen && !isClosing ? 'slide-up' : 'slide-down'}`}
33+
onClick={(e) => e.stopPropagation()}
34+
>
35+
<div className="modal-header">
36+
<h2 className="text-xl font-playfairDisplay text-rose-400">{title}</h2>
37+
<button onClick={handleClose} className="close-button">
38+
<XIcon size={24} />
39+
</button>
40+
</div>
41+
<hr className="border-gray-500" />
42+
<div className="modal-body text-gray-300">
43+
{children}
44+
</div>
45+
</div>
46+
</div>
47+
);
48+
};
49+
50+
export default GenericModal;

src/components/Layout.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import CommandPalette from './CommandPalette';
1010

1111
import { DndProvider } from '../context/DndContext';
1212

13-
const Layout = ({ children, toggleModal, isSearchVisible, toggleSearch }) => {
13+
const Layout = ({ children, toggleModal, isSearchVisible, toggleSearch, openGenericModal }) => {
1414
const [isSidebarOpen, setIsSidebarOpen] = useState(window.innerWidth > 768);
1515
const [isPaletteOpen, setIsPaletteOpen] = useState(false);
1616
const location = useLocation();
@@ -56,7 +56,7 @@ const Layout = ({ children, toggleModal, isSearchVisible, toggleSearch }) => {
5656

5757
return (
5858
<>
59-
<CommandPalette isOpen={isPaletteOpen} setIsOpen={setIsPaletteOpen} />
59+
<CommandPalette isOpen={isPaletteOpen} setIsOpen={setIsPaletteOpen} openGenericModal={openGenericModal} />
6060
<div className="bg-gray-950 min-h-screen font-sans flex">
6161
<Sidebar
6262
isOpen={isSidebarOpen}

src/hooks/useSearchableData.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ const useSearchableData = () => {
6969
{ title: 'Open GitHub Profile', type: 'command', commandId: 'openGitHub' },
7070
{ title: 'Open Twitter Profile', type: 'command', commandId: 'openTwitter' },
7171
{ title: 'Open LinkedIn Profile', type: 'command', commandId: 'openLinkedIn' },
72+
{ title: 'Scroll to Top', type: 'command', commandId: 'scrollToTop' },
73+
{ title: 'Scroll to Bottom', type: 'command', commandId: 'scrollToBottom' },
74+
{ title: 'Show Site Stats', type: 'command', commandId: 'showSiteStats' },
75+
{ title: 'Show Version', type: 'command', commandId: 'showVersion' },
7276
];
7377

7478
setItems([...staticRoutes, ...customCommands, ...allPosts, ...allProjects, ...allLogs, ...allApps]);

0 commit comments

Comments
 (0)