Skip to content

Commit

Permalink
feat(www): add folder icons component
Browse files Browse the repository at this point in the history
  • Loading branch information
fellipeutaka committed Aug 16, 2024
1 parent f21e3ee commit 3364fb2
Show file tree
Hide file tree
Showing 7 changed files with 565 additions and 208 deletions.
49 changes: 9 additions & 40 deletions apps/www/src/components/mdx/files.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
"use client";

import { Collapsible } from "@kanpeki/ui/collapsible";
import { Icons } from "@kanpeki/ui/icons";
import { cn } from "@kanpeki/utils/cn";
import { useState } from "react";
import { tv } from "tailwind-variants";
import { LanguageIcon } from "../language-icon";

type FilesProps = React.ComponentProps<"div">;

const itemStyles =
"flex flex-row items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground";

export function Files({ children, className, ...props }: FilesProps) {
return (
<div className={cn("rounded-md border bg-card p-2", className)} {...props}>
<div {...props} className={cn("rounded-md border bg-card p-2", className)}>
{children}
</div>
);
}

export const FileStyles = tv({
base: [
"flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-accent hover:text-accent-foreground",
],
});

interface FileProps extends React.ComponentProps<"div"> {
name: string;
icon?: React.ReactNode;
}

export function File({ name, className, ...props }: FileProps) {
return (
<div className={cn(itemStyles, className)} {...props}>
<div {...props} className={FileStyles({ className })}>
<LanguageIcon
title={name}
language={name.split(".").pop() || ""}
Expand All @@ -36,33 +35,3 @@ export function File({ name, className, ...props }: FileProps) {
</div>
);
}

interface FolderProps extends React.ComponentProps<"div"> {
name: string;
defaultOpen?: boolean;
}

export function Folder({
children,
name,
defaultOpen = false,
...rest
}: FolderProps) {
const [open, setOpen] = useState(defaultOpen);

return (
<Collapsible.Root open={open} onOpenChange={setOpen} {...rest}>
<Collapsible.Trigger className={cn(itemStyles, "w-full")}>
{open ? (
<Icons.FolderOpen className="size-4" />
) : (
<Icons.Folder className="size-4" />
)}
{name}
</Collapsible.Trigger>
<Collapsible.Content>
<div className="ml-2 flex flex-col border-l pl-2">{children}</div>
</Collapsible.Content>
</Collapsible.Root>
);
}
43 changes: 43 additions & 0 deletions apps/www/src/components/mdx/folder-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { type IconProps, Icons } from "@kanpeki/ui/icons";
import { cn } from "@kanpeki/utils/cn";

const folderNameMap = new Map([
["src", [Icons.SrcFolder, Icons.SrcFolderOpen]],
["app", [Icons.AppFolder, Icons.AppFolderOpen]],
["components", [Icons.ComponentsFolder, Icons.ComponentsFolderOpen]],
["config", [Icons.ConfigFolder, Icons.ConfigFolderOpen]],
["hooks", [Icons.HookFolder, Icons.HookFolderOpen]],
["styles", [Icons.CssFolder, Icons.CssFolderOpen]],
["utils", [Icons.UtilsFolder, Icons.UtilsFolderOpen]],
["ui", [Icons.ThemeFolder, Icons.ThemeFolderOpen]],
]);

interface FolderIconProps extends IconProps {
title: string;
isOpen: boolean;
}

export function FolderIcon({
title,
isOpen,
className,
...props
}: FolderIconProps) {
const Icon = folderNameMap.get(title);

if (!Icon) {
return isOpen ? (
<Icons.FolderOpen className={cn("size-4", className)} {...props} />
) : (
<Icons.Folder className={cn("size-4", className)} {...props} />
);
}

const [FolderIcon, FolderIconOpen] = Icon;

return isOpen ? (
<FolderIconOpen className={cn("size-4", className)} {...props} />
) : (
<FolderIcon className={cn("size-4", className)} {...props} />
);
}
33 changes: 33 additions & 0 deletions apps/www/src/components/mdx/folder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"use client";

import { Collapsible } from "@kanpeki/ui/collapsible";
import { useState } from "react";
import { FileStyles } from "./files";
import { FolderIcon } from "./folder-icon";

interface FolderProps extends React.ComponentProps<"div"> {
name: string;
defaultOpen?: boolean;
}

export function Folder({
children,
name,
defaultOpen = false,
className,
...rest
}: FolderProps) {
const [isOpen, setIsOpen] = useState(defaultOpen);

return (
<Collapsible.Root open={isOpen} onOpenChange={setIsOpen} {...rest}>
<Collapsible.Trigger className={FileStyles({ className })}>
<FolderIcon title={name} isOpen={isOpen} />
{name}
</Collapsible.Trigger>
<Collapsible.Content>
<div className="ml-2 flex flex-col border-l pl-2">{children}</div>
</Collapsible.Content>
</Collapsible.Root>
);
}
3 changes: 2 additions & 1 deletion apps/www/src/components/mdx/mdx-components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { LinkStyles } from "~/styles/link";
import { ComponentPreview } from "./component-preview";
import { ComponentSource } from "./component-source";
import { Figcaption } from "./figcaption";
import { File, Files, Folder } from "./files";
import { File, Files } from "./files";
import { Folder } from "./folder";
import { Heading } from "./heading";
import { Pre } from "./pre";
import { Step, Steps } from "./steps";
Expand Down
8 changes: 5 additions & 3 deletions apps/www/src/content/docs/installation/next.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,11 @@ Here's how I structure my Next.js apps. You can use this as a reference:
<File name="page.tsx" />
</Folder>
<Folder name="components">
<File name="button.tsx" />
<File name="tabs.tsx" />
<File name="dialog.tsx" />
<Folder name="ui">
<File name="button.tsx" />
<File name="tabs.tsx" />
<File name="dialog.tsx" />
</Folder>
</Folder>
<Folder name="config">
<File name="fonts.ts" />
Expand Down
Loading

0 comments on commit 3364fb2

Please sign in to comment.