feat(theme-toggle): implement triple tap gesture for theme switching and enhance sidebar visibility logic
This commit is contained in:
@@ -19,6 +19,7 @@ export function ThemeToggleSwitch() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [isTransitioning, setIsTransitioning] = useState(false);
|
||||
const isDarkTheme = theme === "dark";
|
||||
const tripleTapMaxDelay = 500;
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
@@ -34,6 +35,45 @@ export function ThemeToggleSwitch() {
|
||||
return () => window.clearTimeout(transitionTimer);
|
||||
}, [isTransitioning]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!mounted) return;
|
||||
|
||||
let tapCount = 0;
|
||||
let lastTapAt = 0;
|
||||
|
||||
const isTypingInInput = (target: EventTarget | null) => {
|
||||
if (!(target instanceof HTMLElement)) return false;
|
||||
|
||||
const tagName = target.tagName.toLowerCase();
|
||||
if (tagName === "input" || tagName === "textarea" || tagName === "select") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return target.isContentEditable;
|
||||
};
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
if (event.repeat) return;
|
||||
if (event.key.toLowerCase() !== "t") return;
|
||||
if (event.ctrlKey || event.metaKey || event.altKey) return;
|
||||
if (isTypingInInput(event.target)) return;
|
||||
|
||||
const now = Date.now();
|
||||
tapCount = now - lastTapAt <= tripleTapMaxDelay ? tapCount + 1 : 1;
|
||||
lastTapAt = now;
|
||||
|
||||
if (tapCount === 3) {
|
||||
setIsTransitioning(true);
|
||||
setTheme(theme === "dark" ? "light" : "dark");
|
||||
tapCount = 0;
|
||||
lastTapAt = 0;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [mounted, setTheme, theme]);
|
||||
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,14 @@ export const NAV_DATA: NavData = [
|
||||
url: "/",
|
||||
icon: Icons.HomeIcon,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: "Admins",
|
||||
url: "/admins",
|
||||
icon: Icons.User,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: "Manage Companies",
|
||||
@@ -46,12 +48,14 @@ export const NAV_DATA: NavData = [
|
||||
url: "/companies",
|
||||
title: "Companies",
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
icon: Icons.Building,
|
||||
url: "/company-categories",
|
||||
title: "Company Categories",
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -60,18 +64,21 @@ export const NAV_DATA: NavData = [
|
||||
url: "/customers",
|
||||
icon: Icons.User,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: "Tenders",
|
||||
url: "/tenders",
|
||||
icon: Icons.Table,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: "Notifications",
|
||||
url: "/notification-history",
|
||||
icon: Icons.NotificationIcon,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
title: "Contact Us",
|
||||
@@ -85,13 +92,14 @@ export const NAV_DATA: NavData = [
|
||||
url: "/marketing",
|
||||
icon: GlobeSearch,
|
||||
items: [],
|
||||
visible:false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
title: "Inquiries",
|
||||
url: "/inquiries",
|
||||
icon: Icons.BriefcaseTimer,
|
||||
items: [],
|
||||
visible: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
const { setIsOpen, isOpen, isMobile, toggleSidebar } = useSidebarContext();
|
||||
const [expandedItems, setExpandedItems] = useState<string[]>([]);
|
||||
const isVisible = (item: { visible?: boolean }) => item.visible !== false;
|
||||
|
||||
const toggleExpanded = (title: string) => {
|
||||
setExpandedItems((prev) =>
|
||||
@@ -26,9 +27,9 @@ export function Sidebar() {
|
||||
const newExpandedItems: string[] = [];
|
||||
|
||||
NAV_DATA.forEach((section) => {
|
||||
section.items.forEach((item) => {
|
||||
section.items.filter(isVisible).forEach((item) => {
|
||||
if (item.items.length > 0) {
|
||||
const hasActiveSubItem = item.items.some(
|
||||
const hasActiveSubItem = item.items.filter(isVisible).some(
|
||||
(subItem: NavSubItem) =>
|
||||
subItem.url === pathname ||
|
||||
(pathname.startsWith(subItem.url) && subItem.url !== "/"),
|
||||
@@ -115,7 +116,14 @@ export function Sidebar() {
|
||||
isCollapsed ? "w-full px-1" : "pr-3",
|
||||
)}
|
||||
>
|
||||
{NAV_DATA.map((section) => (
|
||||
{NAV_DATA.map((section) => {
|
||||
const visibleItems = section.items.filter(isVisible);
|
||||
|
||||
if (!visibleItems.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={section.label} className="mb-6">
|
||||
{!isCollapsed && (
|
||||
<h2 className="mb-4 px-2 text-[11px] font-semibold uppercase tracking-[0.12em] text-dark-5 dark:text-dark-6">
|
||||
@@ -125,18 +133,21 @@ export function Sidebar() {
|
||||
|
||||
<nav role="navigation" aria-label={section.label}>
|
||||
<ul className={cn("space-y-2", isCollapsed && "space-y-1")}>
|
||||
{section.items.map((item) => (
|
||||
{visibleItems.map((item) => {
|
||||
const visibleSubItems = item.items.filter(isVisible);
|
||||
|
||||
return (
|
||||
<li key={item.title}>
|
||||
{item.items.length ? (
|
||||
{visibleSubItems.length ? (
|
||||
<div>
|
||||
<MenuItem
|
||||
isActive={
|
||||
item.items.some(
|
||||
visibleSubItems.some(
|
||||
({ url }: { url: string }) =>
|
||||
url === pathname,
|
||||
) ||
|
||||
(item.items.length > 0 &&
|
||||
item.items.some(
|
||||
(visibleSubItems.length > 0 &&
|
||||
visibleSubItems.some(
|
||||
({ url }: { url: string }) =>
|
||||
pathname.startsWith(url) && url !== "/",
|
||||
))
|
||||
@@ -178,7 +189,7 @@ export function Sidebar() {
|
||||
className="ml-9 mr-0 space-y-1.5 pb-[15px] pr-0 pt-2"
|
||||
role="menu"
|
||||
>
|
||||
{item.items.map((subItem: NavSubItem) => (
|
||||
{visibleSubItems.map((subItem: NavSubItem) => (
|
||||
<li key={subItem.title} role="none">
|
||||
<MenuItem
|
||||
as="link"
|
||||
@@ -232,11 +243,13 @@ export function Sidebar() {
|
||||
})()
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
))}
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Toggle button at bottom for collapsed state */}
|
||||
|
||||
Reference in New Issue
Block a user