Skip to content

Commit

Permalink
Merge pull request #6 from pheralb/next
Browse files Browse the repository at this point in the history
✨ Add search + improve light/dark mode
  • Loading branch information
pheralb authored Aug 18, 2024
2 parents de362be + 53addeb commit d10da4d
Show file tree
Hide file tree
Showing 10 changed files with 800 additions and 3,769 deletions.
2 changes: 2 additions & 0 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
"@astrojs/mdx": "3.1.3",
"@astrojs/react": "3.6.1",
"@astrojs/tailwind": "5.1.0",
"@radix-ui/react-dialog": "1.1.1",
"@radix-ui/react-dropdown-menu": "2.1.1",
"@radix-ui/react-icons": "1.3.0",
"@radix-ui/react-slot": "1.1.0",
"astro": "4.13.0",
"class-variance-authority": "0.7.0",
"clsx": "2.1.1",
"cmdk": "1.0.0",
"fast-npm-meta": "0.0.1",
"js-confetti": "0.12.0",
"lucide-react": "0.399.0",
Expand Down
5 changes: 4 additions & 1 deletion docs/src/components/header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { cn } from '@/utils';
import { SocialLinks } from '@/docs.config';
import { buttonVariants } from '@/ui/button';
import { Logo } from '@/components/icons';
import MobileMenu from '@/components/mobileMenu';
import { ModeToggle } from '@/components/theme/modeToggle';
import SearchModal from '@/components/search/searchModal';
---

<nav
Expand Down Expand Up @@ -48,7 +50,8 @@ import { ModeToggle } from '@/components/theme/modeToggle';
)),
)
}
<ModeToggle client:load />
<SearchModal client:only="react" />
<ModeToggle client:only="react" />
</div>
</div>
</nav>
77 changes: 77 additions & 0 deletions docs/src/components/search/searchModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { allRoutes } from '@/docs.config';
import { Button } from '@/ui/button';

import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/ui/command';
import { FileIcon, SearchIcon } from 'lucide-react';
import { useEffect, useState } from 'react';

const SearchModal = () => {
const [open, setOpen] = useState<boolean>(false);

useEffect(() => {
const down = (e: KeyboardEvent) => {
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
e.preventDefault();
setOpen((open) => !open);
}
};
document.addEventListener('keydown', down);
return () => document.removeEventListener('keydown', down);
}, []);

return (
<>
<Button
variant="ghost"
size="icon"
title="Search"
onClick={() => setOpen(true)}
>
<SearchIcon size={20} strokeWidth={1.5} />
<span className="sr-only">Toggle theme</span>
</Button>
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
{allRoutes.map((doc) => (
<>
<CommandGroup
key={doc.category}
heading={doc.category}
title={doc.category}
>
{doc.routes.map((route) => (
<CommandItem
key={route.path}
title={route.title}
onSelect={() => {
setOpen(false);
window.location.href = route.path;
}}
>
{route.icon ? (
<route.icon width={14} height={14} strokeWidth={1.5} />
) : (
<FileIcon size={14} strokeWidth={1.5} />
)}
<span>{route.title}</span>
</CommandItem>
))}
</CommandGroup>
</>
))}
</CommandList>
</CommandDialog>
</>
);
};

export default SearchModal;
1 change: 1 addition & 0 deletions docs/src/components/theme/modeToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export function ModeToggle() {
const applyTheme = (theme: 'light' | 'dark') => {
setToastTheme(theme);
document.documentElement.classList.toggle('dark', theme === 'dark');
document.documentElement.style.colorScheme = theme;
localStorage.setItem('theme', theme);
};

Expand Down
14 changes: 9 additions & 5 deletions docs/src/content/config.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { z, defineCollection } from 'astro:content';

const docsSchema = z.object({
title: z.string(),
description: z.string(),
category: z.string(),
});

export type Docs = z.infer<typeof docsSchema>;

const docsCollection = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
category: z.string(),
}),
schema: docsSchema,
});

export const collections = {
Expand Down
22 changes: 4 additions & 18 deletions docs/src/docs.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { SVGProps } from 'react';
import { X, Github, Nextjs, Remix, Astro } from './components/icons';

interface iDocsRoutes {
export interface iDocsRoutes {
category: string;
routes: {
title: string;
Expand Down Expand Up @@ -61,23 +61,7 @@ export const SidebarRoutes: iDocsRoutes[] = [
},
],
},
{
category: 'Framework Guides',
routes: [
{
title: 'Astro',
path: '/astro',
},
{
title: 'Next.js',
path: '/nextjs',
},
{
title: 'Remix',
path: '/remix',
},
],
},
...FrameworkGuides,
{
category: 'API',
routes: [
Expand All @@ -101,3 +85,5 @@ export const SidebarRoutes: iDocsRoutes[] = [
],
},
];

export const allRoutes = [...SidebarRoutes, ...SocialLinks];
154 changes: 154 additions & 0 deletions docs/src/ui/command.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import * as React from 'react';
import { type DialogProps } from '@radix-ui/react-dialog';
import { Command as CommandPrimitive } from 'cmdk';

import { cn } from '@/utils';
import { Dialog, DialogContent } from '@/ui/dialog';

const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive>
>(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
'flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-neutral-950 dark:bg-neutral-900 dark:text-neutral-50',
className,
)}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;

interface CommandDialogProps extends DialogProps {}

const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>
<DialogContent className="overflow-hidden p-0">
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-neutral-500 dark:[&_[cmdk-group-heading]]:text-neutral-400 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-[18px] [&_[cmdk-item]_svg]:w-[18px]">
{children}
</Command>
</DialogContent>
</Dialog>
);
};

const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
>(({ className, ...props }, ref) => (
<div className="flex items-center px-3" cmdk-input-wrapper="">
<CommandPrimitive.Input
ref={ref}
className={cn(
'flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-neutral-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-neutral-400',
className,
)}
{...props}
/>
</div>
));

CommandInput.displayName = CommandPrimitive.Input.displayName;

const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.List>
>(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', className)}
{...props}
/>
));

CommandList.displayName = CommandPrimitive.List.displayName;

const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Empty>
>((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
));

CommandEmpty.displayName = CommandPrimitive.Empty.displayName;

const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Group>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
'overflow-hidden p-1 text-neutral-950 dark:text-neutral-50 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-neutral-500 dark:[&_[cmdk-group-heading]]:text-neutral-400',
className,
)}
{...props}
/>
));

CommandGroup.displayName = CommandPrimitive.Group.displayName;

const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Separator>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn(
'-mx-1 my-2 h-px bg-neutral-200 dark:bg-neutral-800',
className,
)}
{...props}
/>
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;

const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
React.ComponentPropsWithoutRef<typeof CommandPrimitive.Item>
>(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
'flex cursor-default items-center gap-2 rounded-md px-2 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-neutral-100 data-[selected=true]:text-neutral-900 data-[disabled=true]:opacity-50 dark:data-[selected=true]:bg-neutral-800 dark:data-[selected=true]:text-neutral-50',
className,
)}
{...props}
/>
));

CommandItem.displayName = CommandPrimitive.Item.displayName;

const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes<HTMLSpanElement>) => {
return (
<span
className={cn(
'ml-auto text-xs tracking-widest text-neutral-500 dark:text-neutral-400',
className,
)}
{...props}
/>
);
};
CommandShortcut.displayName = 'CommandShortcut';

export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
};
Loading

0 comments on commit d10da4d

Please sign in to comment.