Skip to content

Commit 36100cc

Browse files
authored
feat: Item Component (#431)
* feat: Item Component * refactor: fix issues for item component * refactor: refactor item props * refactor: remove sperator from codebase * chore: resolved merge conflits feat: Add Empty Component (#422) * feat: Add Empty Component * fix: remove unncessary code * refactor: Refactor issue in Empty * refactor: font props * chore: Empty Registry * refactor: change != to !== * feat: Item Component * chore: resolved merge confilts
1 parent 09ee2aa commit 36100cc

File tree

10 files changed

+447
-2
lines changed

10 files changed

+447
-2
lines changed

app/docs/components/item/page.tsx

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import { Fragment } from "react";
2+
3+
import { Metadata } from "next";
4+
5+
import { itemMetaData } from "@/lib/metadata";
6+
7+
import { Button } from "@/components/ui/8bit/button";
8+
import {
9+
Item,
10+
ItemActions,
11+
ItemContent,
12+
ItemDescription,
13+
ItemGroup,
14+
ItemSeparator,
15+
ItemTitle,
16+
} from "@/components/ui/8bit/item";
17+
import { Separator } from "@/components/ui/separator";
18+
19+
import CodeSnippet from "@/app/docs/components/code-snippet";
20+
import CopyCommandButton from "@/app/docs/components/copy-command-button";
21+
import InstallationCommands from "@/app/docs/components/installation-commands";
22+
import { OpenInV0Button } from "@/app/docs/components/open-in-v0-button";
23+
24+
export const metadata: Metadata = {
25+
title: "8-bit Item",
26+
description: "Displays an 8-bit item component.",
27+
openGraph: {
28+
images: itemMetaData,
29+
},
30+
};
31+
32+
const items = [
33+
{
34+
title: "Sword",
35+
price: 300,
36+
description: "The demonic sword",
37+
},
38+
{
39+
title: "Shield",
40+
price: 250,
41+
description: "The divine shield",
42+
},
43+
{
44+
title: "Bow",
45+
price: 280,
46+
description: "The bow of precision",
47+
},
48+
];
49+
50+
export default function ItemPage() {
51+
return (
52+
<div className="flex flex-col gap-4">
53+
<div className="flex flex-col md:flex-row items-center justify-between gap-2">
54+
<h1 className="text-3xl font-bold">Item</h1>
55+
<CopyCommandButton
56+
copyCommand="pnpm dlx shadcn@latest add @8bitcn/item"
57+
command="pnpm dlx shadcn@latest add @8bitcn/item"
58+
/>
59+
</div>
60+
<p className="text-muted-foreground">Displays an 8-bit item component.</p>
61+
<div className="flex flex-col gap-4 border rounded-lg p-4 min-h-[450px]">
62+
<div className="flex items-center justify-between">
63+
<h2 className="text-sm text-muted-foreground sm:pl-3">
64+
8-bit item component
65+
</h2>
66+
67+
<div className="flex items-center gap-2">
68+
<<<<<<< HEAD
69+
<<<<<<< HEAD
70+
<OpenInV0Button name="item" className="w-fit" />
71+
=======
72+
<OpenInV0Button name="8bit-empty" className="w-fit" />
73+
>>>>>>> f7d626c (feat: Item Component)
74+
=======
75+
<OpenInV0Button name="8bit-item" className="w-fit" />
76+
>>>>>>> a36509e (chore: resolved merge confilts)
77+
</div>
78+
</div>
79+
<div className="flex items-center justify-center min-h-[400px] relative">
80+
<ItemGroup>
81+
{items.map((item, index) => (
82+
<Fragment key={index}>
83+
<Item variant="outline">
84+
<ItemContent>
85+
<ItemTitle>
86+
{item.title} &bull; {item.price}$
87+
</ItemTitle>
88+
<ItemDescription>{item.description}</ItemDescription>
89+
</ItemContent>
90+
<ItemActions>
91+
<Button variant="outline" size="sm">
92+
Buy
93+
</Button>
94+
</ItemActions>
95+
</Item>
96+
{index !== items.length - 1 && <ItemSeparator />}
97+
</Fragment>
98+
))}
99+
</ItemGroup>
100+
</div>
101+
</div>
102+
<h3 className="text-lg font-bold">Installation</h3>
103+
104+
<Separator />
105+
106+
<InstallationCommands packageName="item" />
107+
108+
<h3 className="text-lg font-bold mt-10">Usage</h3>
109+
110+
<Separator />
111+
112+
<CodeSnippet>{`import { Button } from "@/components/ui/8bit/button";
113+
import {
114+
Item,
115+
ItemActions,
116+
ItemContent,
117+
ItemDescription,
118+
ItemGroup,
119+
ItemSeparator,
120+
ItemTitle,
121+
} from "@/components/ui/8bit/item";
122+
<<<<<<< HEAD
123+
=======
124+
import { Separator } from "@/components/ui/separator";
125+
>>>>>>> f7d626c (feat: Item Component)
126+
127+
const items = [
128+
{
129+
title: "Sword",
130+
price: 300,
131+
description: "The demonic sword"
132+
},
133+
{
134+
title: "Shield",
135+
price: 250,
136+
description: "The divine shield"
137+
},
138+
{
139+
title: "Bow",
140+
price: 280,
141+
description: "The bow of precision"
142+
}
143+
];
144+
`}</CodeSnippet>
145+
<CodeSnippet>{`<ItemGroup>
146+
{items.map((item, index) => (
147+
<Fragment key={index}>
148+
<Item variant="outline">
149+
<ItemContent>
150+
<ItemTitle>
151+
{item.title} &bull; {item.price}$
152+
</ItemTitle>
153+
<ItemDescription>{item.description}</ItemDescription>
154+
</ItemContent>
155+
<ItemActions>
156+
<Button variant="outline" size="sm">
157+
Buy
158+
</Button>
159+
</ItemActions>
160+
</Item>
161+
{index !== items.length - 1 && <ItemSeparator />}
162+
</Fragment>
163+
))}
164+
</ItemGroup>`}</CodeSnippet>
165+
</div>
166+
);
167+
}

components/ui/8bit/item.tsx

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import * as React from "react";
2+
3+
import { Slot } from "@radix-ui/react-slot";
4+
import { type VariantProps, cva } from "class-variance-authority";
5+
6+
import { cn } from "@/lib/utils";
7+
8+
<<<<<<< HEAD
9+
<<<<<<< HEAD
10+
import { Separator } from "./separator";
11+
=======
12+
import { Separator } from "./separator.tsx";
13+
>>>>>>> f7d626c (feat: Item Component)
14+
=======
15+
import { Separator } from "./separator";
16+
>>>>>>> a36509e (chore: resolved merge confilts)
17+
import "./styles/retro.css";
18+
19+
function ItemGroup({ className, ...props }: React.ComponentProps<"div">) {
20+
return (
21+
<div
22+
role="list"
23+
data-slot="item-group"
24+
className={cn("group/item-group flex flex-col", className)}
25+
{...props}
26+
/>
27+
);
28+
}
29+
30+
function ItemSeparator({
31+
className,
32+
...props
33+
}: React.ComponentProps<typeof Separator>) {
34+
return (
35+
<Separator
36+
data-slot="item-separator"
37+
orientation="horizontal"
38+
className={cn("my-0", className)}
39+
{...props}
40+
/>
41+
);
42+
}
43+
44+
const itemVariants = cva(
45+
"group/item flex items-center border border-transparent text-sm rounded-md transition-colors [a]:hover:bg-accent/50 [a]:transition-colors duration-100 flex-wrap outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
46+
{
47+
variants: {
48+
variant: {
49+
default: "bg-transparent",
50+
outline: "border-border",
51+
muted: "bg-muted/50",
52+
},
53+
size: {
54+
default: "p-4 gap-4 ",
55+
sm: "py-3 px-4 gap-2.5",
56+
},
57+
font: {
58+
normal: "",
59+
retro: "retro",
60+
},
61+
},
62+
defaultVariants: {
63+
variant: "default",
64+
size: "default",
65+
font: "retro",
66+
},
67+
}
68+
);
69+
70+
function Item({
71+
className,
72+
variant = "default",
73+
size = "default",
74+
<<<<<<< HEAD
75+
font = "retro",
76+
=======
77+
>>>>>>> f7d626c (feat: Item Component)
78+
asChild = false,
79+
...props
80+
}: React.ComponentProps<"div"> &
81+
VariantProps<typeof itemVariants> & { asChild?: boolean }) {
82+
const Comp = asChild ? Slot : "div";
83+
return (
84+
<Comp
85+
data-slot="item"
86+
data-variant={variant}
87+
data-size={size}
88+
<<<<<<< HEAD
89+
data-font={font}
90+
className={cn(itemVariants({ variant, size, font, className }))}
91+
=======
92+
className={cn(itemVariants({ variant, size, className }))}
93+
>>>>>>> f7d626c (feat: Item Component)
94+
{...props}
95+
/>
96+
);
97+
}
98+
99+
const itemMediaVariants = cva(
100+
"flex shrink-0 items-center justify-center gap-2 group-has-[[data-slot=item-description]]/item:self-start [&_svg]:pointer-events-none group-has-[[data-slot=item-description]]/item:translate-y-0.5",
101+
{
102+
variants: {
103+
variant: {
104+
default: "bg-transparent",
105+
icon: "size-8 border rounded-sm bg-muted [&_svg:not([class*='size-'])]:size-4",
106+
image:
107+
"size-10 rounded-sm overflow-hidden [&_img]:size-full [&_img]:object-cover",
108+
},
109+
},
110+
defaultVariants: {
111+
variant: "default",
112+
},
113+
}
114+
);
115+
116+
function ItemMedia({
117+
className,
118+
variant = "default",
119+
...props
120+
}: React.ComponentProps<"div"> & VariantProps<typeof itemMediaVariants>) {
121+
return (
122+
<div
123+
data-slot="item-media"
124+
data-variant={variant}
125+
className={cn(itemMediaVariants({ variant, className }))}
126+
{...props}
127+
/>
128+
);
129+
}
130+
131+
function ItemContent({ className, ...props }: React.ComponentProps<"div">) {
132+
return (
133+
<div
134+
data-slot="item-content"
135+
className={cn(
136+
"flex flex-1 flex-col gap-1 [&+[data-slot=item-content]]:flex-none",
137+
className
138+
)}
139+
{...props}
140+
/>
141+
);
142+
}
143+
144+
function ItemTitle({ className, ...props }: React.ComponentProps<"div">) {
145+
return (
146+
<div
147+
data-slot="item-title"
148+
className={cn(
149+
"flex w-fit items-center gap-2 text-sm leading-snug font-medium",
150+
className
151+
)}
152+
{...props}
153+
/>
154+
);
155+
}
156+
157+
function ItemDescription({ className, ...props }: React.ComponentProps<"p">) {
158+
return (
159+
<p
160+
data-slot="item-description"
161+
className={cn(
162+
"text-muted-foreground line-clamp-2 text-sm leading-normal font-normal text-balance",
163+
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
164+
className
165+
)}
166+
{...props}
167+
/>
168+
);
169+
}
170+
171+
function ItemActions({ className, ...props }: React.ComponentProps<"div">) {
172+
return (
173+
<div
174+
data-slot="item-actions"
175+
className={cn("flex items-center gap-2", className)}
176+
{...props}
177+
/>
178+
);
179+
}
180+
181+
function ItemHeader({ className, ...props }: React.ComponentProps<"div">) {
182+
return (
183+
<div
184+
data-slot="item-header"
185+
className={cn(
186+
"flex basis-full items-center justify-between gap-2",
187+
className
188+
)}
189+
{...props}
190+
/>
191+
);
192+
}
193+
194+
function ItemFooter({ className, ...props }: React.ComponentProps<"div">) {
195+
return (
196+
<div
197+
data-slot="item-footer"
198+
className={cn(
199+
"flex basis-full items-center justify-between gap-2",
200+
className
201+
)}
202+
{...props}
203+
/>
204+
);
205+
}
206+
207+
export {
208+
Item,
209+
ItemMedia,
210+
ItemContent,
211+
ItemActions,
212+
ItemGroup,
213+
ItemSeparator,
214+
ItemTitle,
215+
ItemDescription,
216+
ItemHeader,
217+
ItemFooter,
218+
};

0 commit comments

Comments
 (0)