diff --git a/app/docs/components/context-menu/page.tsx b/app/docs/components/context-menu/page.tsx new file mode 100644 index 00000000..18313984 --- /dev/null +++ b/app/docs/components/context-menu/page.tsx @@ -0,0 +1,91 @@ +import { Metadata } from "next" + +import { contextMenuMetaData } from "@/lib/metadata" +import { Button } from "@/components/ui/8bit/button" +import { + ContextMenu, + ContextMenuContent, + ContextMenuItem, + ContextMenuTrigger, +} from "@/components/ui/8bit/context-menu" +import { Separator } from "@/components/ui/separator" + +import CodeSnippet from "../code-snippet" +import CopyCommandButton from "../copy-command-button" +import InstallationCommands from "../installation-commands" +import { OpenInV0Button } from "../open-in-v0-button" + +export const metadata: Metadata = { + title: "8-bit Context Menu", + description: "Displays an 8-bit context menu component.", + openGraph: { + images: contextMenuMetaData, + }, +} + +export default function ButtonPage() { + return ( +
+
+

Context Menu

+ +
+ +

+ Displays an 8-bit context menu component. +

+ +
+
+

+ 8-bit context menu component +

+ +
+ +
+
+
+ + + + + + Profile + Billing + Team + Subscription + + +
+
+ +

Installation

+ + + + + +

Usage

+ + + + {`import { ContextMenu } from "@/components/ui/8bit/context-menu"`} + + {` + Right click + + Profile + Billing + Team + Subscription + +`} +
+ ) +} diff --git a/components/ui/8bit/context-menu.tsx b/components/ui/8bit/context-menu.tsx new file mode 100644 index 00000000..47c39651 --- /dev/null +++ b/components/ui/8bit/context-menu.tsx @@ -0,0 +1,288 @@ +"use client" + +import * as React from "react" +import { Press_Start_2P } from "next/font/google" +import * as ContextMenuPrimitive from "@radix-ui/react-context-menu" +import { ChevronRight, Circle } from "lucide-react" + +import { cn } from "@/lib/utils" + +const pressStart = Press_Start_2P({ + weight: ["400"], + subsets: ["latin"], +}) + +const ContextMenu = ContextMenuPrimitive.Root + +const ContextMenuTrigger = ContextMenuPrimitive.Trigger + +const ContextMenuGroup = ContextMenuPrimitive.Group + +const ContextMenuPortal = ContextMenuPrimitive.Portal + +const ContextMenuSub = ContextMenuPrimitive.Sub + +const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup + +const ContextMenuSubTrigger = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName + +const ContextMenuSubContent = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName + +const ContextMenuContent = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +
+ + + +)) +ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName + +const ContextMenuItem = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName + +const ContextMenuCheckboxItem = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + + + + + + + + + + {children} + +)) +ContextMenuCheckboxItem.displayName = + ContextMenuPrimitive.CheckboxItem.displayName + +const ContextMenuRadioItem = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName + +const ContextMenuLabel = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName + +const ContextMenuSeparator = React.forwardRef< + React.ComponentRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName + +const ContextMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +ContextMenuShortcut.displayName = "ContextMenuShortcut" + +export { + ContextMenu, + ContextMenuTrigger, + ContextMenuContent, + ContextMenuItem, + ContextMenuCheckboxItem, + ContextMenuRadioItem, + ContextMenuLabel, + ContextMenuSeparator, + ContextMenuShortcut, + ContextMenuGroup, + ContextMenuPortal, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, + ContextMenuRadioGroup, +} diff --git a/config/nav-items.ts b/config/nav-items.ts index 5364e7e7..589ac24c 100644 --- a/config/nav-items.ts +++ b/config/nav-items.ts @@ -75,6 +75,11 @@ export const navItems = { title: "Command", url: "/docs/components/command", }, + { + title: "Context Menu", + url: "/docs/components/context-menu", + new: true, + }, { title: "Collapsible", url: "/docs/components/collapsible", diff --git a/lib/metadata.ts b/lib/metadata.ts index 7e66c815..f5106a52 100644 --- a/lib/metadata.ts +++ b/lib/metadata.ts @@ -49,3 +49,4 @@ export const toggleGroupMetaData = "/assets/8bitcn-toggle-group-light.png" export const accordionMetaData = "/assets/8bitcn-accordion-light.png" export const sliderMetaData = "/assets/8bitcn-slider-light.png" export const datePickerMetaData = "/assets/8bitcn-date-picker-light.png" +export const contextMenuMetaData = "/assets/8bitcn-context-menu-light.png" diff --git a/package.json b/package.json index 66534cb7..52fca727 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-avatar": "^1.1.9", "@radix-ui/react-checkbox": "^1.3.1", "@radix-ui/react-collapsible": "^1.1.10", + "@radix-ui/react-context-menu": "^2.2.14", "@radix-ui/react-dialog": "^1.1.13", "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-hover-card": "^1.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 995b606d..1e25d716 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: '@radix-ui/react-collapsible': specifier: ^1.1.10 version: 1.1.10(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-context-menu': + specifier: ^2.2.14 + version: 2.2.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@radix-ui/react-dialog': specifier: ^1.1.13 version: 1.1.13(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -770,6 +773,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-context-menu@2.2.14': + resolution: {integrity: sha512-RUHvrJE2qKAd9pQ50HZZsePio4SMWEh8v6FWQwg/4t6K1fuxfb4Ec40VEVvni6V7nFxmj9srU4UZc7aYp8x0LQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-context@1.1.2': resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} peerDependencies: @@ -4392,6 +4408,20 @@ snapshots: optionalDependencies: '@types/react': 19.1.4 + '@radix-ui/react-context-menu@2.2.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-menu': 2.1.14(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.2(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.4)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.4)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.1.4 + '@types/react-dom': 19.1.5(@types/react@19.1.4) + '@radix-ui/react-context@1.1.2(@types/react@19.1.4)(react@19.1.0)': dependencies: react: 19.1.0 diff --git a/public/assets/8bitcn-context-menu-light.png b/public/assets/8bitcn-context-menu-light.png new file mode 100644 index 00000000..ab7c5b4a Binary files /dev/null and b/public/assets/8bitcn-context-menu-light.png differ diff --git a/public/r/8bit-context-menu.json b/public/r/8bit-context-menu.json new file mode 100644 index 00000000..55cf3843 --- /dev/null +++ b/public/r/8bit-context-menu.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://ui.shadcn.com/schema/registry-item.json", + "name": "8bit-context-menu", + "type": "registry:component", + "title": "8-bit Context Menu", + "description": "A simple 8-bit context menu component", + "registryDependencies": [], + "files": [ + { + "path": "components/ui/8bit/context-menu.tsx", + "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Press_Start_2P } from \"next/font/google\"\nimport * as ContextMenuPrimitive from \"@radix-ui/react-context-menu\"\nimport { ChevronRight, Circle } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nconst pressStart = Press_Start_2P({\n weight: [\"400\"],\n subsets: [\"latin\"],\n})\n\nconst ContextMenu = ContextMenuPrimitive.Root\n\nconst ContextMenuTrigger = ContextMenuPrimitive.Trigger\n\nconst ContextMenuGroup = ContextMenuPrimitive.Group\n\nconst ContextMenuPortal = ContextMenuPrimitive.Portal\n\nconst ContextMenuSub = ContextMenuPrimitive.Sub\n\nconst ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup\n\nconst ContextMenuSubTrigger = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, children, ...props }, ref) => (\n \n {children}\n \n \n))\nContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName\n\nconst ContextMenuSubContent = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName\n\nconst ContextMenuContent = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n
\n \n \n
\n
\n))\nContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName\n\nconst ContextMenuItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n \n))\nContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName\n\nconst ContextMenuCheckboxItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, checked, ...props }, ref) => (\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n {children}\n \n))\nContextMenuCheckboxItem.displayName =\n ContextMenuPrimitive.CheckboxItem.displayName\n\nconst ContextMenuRadioItem = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, children, ...props }, ref) => (\n \n \n \n \n \n \n {children}\n \n))\nContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName\n\nconst ContextMenuLabel = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n \n))\nContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName\n\nconst ContextMenuSeparator = React.forwardRef<\n React.ComponentRef,\n React.ComponentPropsWithoutRef\n>(({ className, ...props }, ref) => (\n \n))\nContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName\n\nconst ContextMenuShortcut = ({\n className,\n ...props\n}: React.HTMLAttributes) => {\n return (\n \n )\n}\nContextMenuShortcut.displayName = \"ContextMenuShortcut\"\n\nexport {\n ContextMenu,\n ContextMenuTrigger,\n ContextMenuContent,\n ContextMenuItem,\n ContextMenuCheckboxItem,\n ContextMenuRadioItem,\n ContextMenuLabel,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuGroup,\n ContextMenuPortal,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuRadioGroup,\n}\n", + "type": "registry:component", + "target": "components/ui/8bit/context-menu.tsx" + } + ] +} \ No newline at end of file diff --git a/registry.json b/registry.json index 16a723ed..14f43b35 100644 --- a/registry.json +++ b/registry.json @@ -409,6 +409,20 @@ } ] }, + { + "name": "8bit-context-menu", + "type": "registry:component", + "title": "8-bit Context Menu", + "description": "A simple 8-bit context menu component", + "registryDependencies": [], + "files": [ + { + "path": "components/ui/8bit/context-menu.tsx", + "type": "registry:component", + "target": "components/ui/8bit/context-menu.tsx" + } + ] + }, { "name": "8bit-toggle", "type": "registry:component",