Create root-level CommandK component inside Modal
This commit is contained in:
parent
6ea0084e3d
commit
e92ec878dc
@ -13,6 +13,7 @@ import Footer from '@/site/Footer';
|
||||
import { Suspense } from 'react';
|
||||
import FooterClient from '@/site/FooterClient';
|
||||
import NavClient from '@/site/NavClient';
|
||||
import CommandK from '@/components/CommandK';
|
||||
|
||||
import '../site/globals.css';
|
||||
|
||||
@ -97,6 +98,7 @@ export default function RootLayout({
|
||||
<SpeedInsights />
|
||||
<PhotoEscapeHandler />
|
||||
<ToasterWithThemes />
|
||||
<CommandK />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
54
src/components/CommandK.tsx
Normal file
54
src/components/CommandK.tsx
Normal file
@ -0,0 +1,54 @@
|
||||
'use client';
|
||||
|
||||
import { Command } from 'cmdk';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Modal from './Modal';
|
||||
import { clsx } from 'clsx/lite';
|
||||
|
||||
const LISTENER_KEYDOWN = 'keydown';
|
||||
|
||||
export default function CommandK() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const down = (e: KeyboardEvent) => {
|
||||
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
||||
e.preventDefault();
|
||||
setOpen((open) => !open);
|
||||
}
|
||||
};
|
||||
document.addEventListener(LISTENER_KEYDOWN, down);
|
||||
return () => document.removeEventListener(LISTENER_KEYDOWN, down);
|
||||
}, []);
|
||||
|
||||
const renderItem = (item: string) =>
|
||||
<Command.Item className={clsx(
|
||||
'p-1 rounded-md',
|
||||
'data-[selected=true]:bg-blue-50',
|
||||
)}>
|
||||
{item}
|
||||
</Command.Item>;
|
||||
|
||||
return (
|
||||
<Command.Dialog
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
label="Global Command Menu"
|
||||
>
|
||||
<Modal onClose={() => setOpen(false)} fast>
|
||||
<Command.Input className="w-full" />
|
||||
<Command.List>
|
||||
<Command.Empty>No results found.</Command.Empty>
|
||||
<Command.Group heading="Letters">
|
||||
{renderItem('a')}
|
||||
{renderItem('b')}
|
||||
<Command.Separator />
|
||||
{renderItem('c')}
|
||||
</Command.Group>
|
||||
|
||||
{renderItem('Apple')}
|
||||
</Command.List>
|
||||
</Modal>
|
||||
</Command.Dialog>
|
||||
);
|
||||
}
|
||||
@ -11,10 +11,14 @@ import usePrefersReducedMotion from '@/utility/usePrefersReducedMotion';
|
||||
|
||||
export default function Modal({
|
||||
onClosePath,
|
||||
onClose,
|
||||
children,
|
||||
fast,
|
||||
}: {
|
||||
onClosePath?: string
|
||||
onClose?: () => void
|
||||
children: ReactNode
|
||||
fast?: boolean
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
@ -32,10 +36,16 @@ export default function Modal({
|
||||
|
||||
useClickInsideOutside({
|
||||
htmlElements,
|
||||
onClickOutside: () => router.push(
|
||||
onClickOutside: () => {
|
||||
if (onClose) {
|
||||
onClose();
|
||||
} else {
|
||||
router.push(
|
||||
onClosePath ?? PATH_ROOT,
|
||||
{ scroll: false },
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
@ -51,7 +61,7 @@ export default function Modal({
|
||||
transition={{ duration: 0.3, easing: 'easeOut' }}
|
||||
>
|
||||
<AnimateItems
|
||||
duration={0.3}
|
||||
duration={fast ? 0.1 : 0.3}
|
||||
items={[<div
|
||||
key="modalContent"
|
||||
className={clsx(
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
|
||||
import clsx from 'clsx';
|
||||
import { clsx } from 'clsx/lite';
|
||||
import { FiMoreHorizontal } from 'react-icons/fi';
|
||||
import Link from 'next/link';
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user