Skip to content

Commit adb2527

Browse files
Diubiitoto04lorenzocorallo
authored
Card (#41)
* Card initial commit * Text and icon gradients * Used figma classes * Moved card.tsx to components/ui/cards * refactor: move card.tsx into ui * Card initial commit * Text and icon gradients * Used figma classes * Moved card.tsx to components/ui/cards * refactor: move card.tsx into ui * chore: biome; fix: imports * aligned card.tsx between all cards * feat: optional gradient * fix: text transparency to see gradient * feat: added bg-background-blur to all cards * feat: add card-caption (#48) * created cardCaption.tsx * Updated card-caption to use react-icons * Adjusted font sizes * Updated card-caption to use react-icons * Adjusted font sizes * chore: biome * feat: typography * aligned card.tsx between all cards * fix: homepage cards layout * chore: biome * feat: add card-path-selection (#56) * created cardCaption.tsx * Updated card-caption to use react-icons * Adjusted font sizes * Added path selection cards * chore: biome * Updated card-caption to use react-icons * Adjusted font sizes * Added path selection cards * chore: biome * rm: card-caption from this branch * fix: typography * fix: imports and home layout * remove: bg-background-blur * feat: add card-course (#63) * feat: card-groups initial commit * fix: spacing between elements * chore: biome * fix: homepage layout for cards * remove: bg-background-blur * chore: biome * feat: add card-course-group (#67) * feat: card-course-group initial commit * chore: biome * fix: make text and icons black * feat: added cards to homepage; feat: added bg-background-blur to the card * remove: bg-background-blur * remove: truncate class * feat: secondary variant * chore: biome * chore: biome * fix: some of CodeRabbit's proposed fixes * required changes by @toto04 and CodeRabbit * feat: implemented @BIA3IA's card hover background * feat: added class to Card * feat: CardTitle is now * fix: removed text sizes from the base card components * fix: CardTitle props type --------- Co-authored-by: Tommaso Morganti <tommaso.morganti01@gmail.com> Co-authored-by: Lorenzo Corallo <66379281+lorenzocorallo@users.noreply.github.com>
1 parent 998b219 commit adb2527

5 files changed

Lines changed: 226 additions & 0 deletions

File tree

src/components/card-caption.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { IconType } from "react-icons"
2+
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "./ui/card"
3+
4+
export function CardCaption({
5+
title,
6+
caption,
7+
icon,
8+
iconPosition = "right",
9+
}: {
10+
title: string
11+
caption: string
12+
icon?: IconType
13+
iconPosition?: "top" | "right"
14+
}) {
15+
return (
16+
<Card hoverBackground>
17+
<CardHeader
18+
className={`typo-headline-medium flex ${iconPosition === "right" ? "justify-between" : "flex-col-reverse"}`}
19+
>
20+
<CardTitle className="typo-headline-medium">{title}</CardTitle>
21+
{icon && <CardAction icon={icon} iconSize={iconPosition === "right" ? "normal" : "large"}></CardAction>}
22+
</CardHeader>
23+
<CardContent className="typo-body-medium">
24+
<p>{caption}</p>
25+
</CardContent>
26+
</Card>
27+
)
28+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { cva, type VariantProps } from "class-variance-authority"
2+
import type { IconType } from "react-icons"
3+
import { FaWhatsapp } from "react-icons/fa"
4+
import { LiaTelegramPlane } from "react-icons/lia"
5+
import { cn } from "@/lib/utils"
6+
import { Card, CardAction, CardTitle } from "./ui/card"
7+
8+
export const cardCourseGroupVariants = cva(
9+
"flex h-fit w-full flex-row items-center gap-5 px-7.5 py-6.25 font-normal leading-6 tracking-[0.03125rem]",
10+
{
11+
variants: {
12+
secondary: {
13+
true: "bg-[rgba(148,192,237,0.40)]",
14+
false: "",
15+
},
16+
},
17+
defaultVariants: {
18+
secondary: false,
19+
},
20+
}
21+
)
22+
23+
export function CardCourseGroup({
24+
groupName,
25+
hasWhatsapp = true,
26+
iconWhatsApp: IconWhatsApp = FaWhatsapp,
27+
hasTelegram = true,
28+
iconTelegram: IconTelegram = LiaTelegramPlane,
29+
secondary = false,
30+
}: {
31+
groupName: string
32+
hasWhatsapp?: boolean
33+
iconWhatsApp?: IconType
34+
hasTelegram?: boolean
35+
iconTelegram?: IconType
36+
} & VariantProps<typeof cardCourseGroupVariants>) {
37+
const actionClassName = cn("rounded-full p-3.75", secondary ? "bg-[#51A2FF]" : "bg-[#74D4FF]")
38+
return (
39+
<Card className={cn(cardCourseGroupVariants({ secondary }))}>
40+
<CardTitle gradient={false} className="typo-headline-small grow">
41+
{groupName}
42+
</CardTitle>
43+
{hasWhatsapp && <CardAction gradient={false} className={actionClassName} icon={IconWhatsApp} iconSize="normal" />}
44+
{hasTelegram && <CardAction gradient={false} className={actionClassName} icon={IconTelegram} iconSize="normal" />}
45+
</Card>
46+
)
47+
}

src/components/card-course.tsx

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { IconType } from "react-icons"
2+
import { CiGlobe } from "react-icons/ci"
3+
import { FaAngleRight } from "react-icons/fa6"
4+
import { IoLocationOutline } from "react-icons/io5"
5+
import { Card, CardAction, CardContent } from "./ui/card"
6+
7+
export function CardCourse({
8+
courseName,
9+
iconLocation: IconLocation = IoLocationOutline,
10+
location = "Milano Leonardo",
11+
iconLanguage: IconLanguage = CiGlobe,
12+
language = "ITA",
13+
iconSelect: IconSelect = FaAngleRight,
14+
}: {
15+
courseName: string
16+
iconLocation?: IconType
17+
location?: "Milano Leonardo" | "Milano Bovisa" | "Cremona" | "Lecco" | "Mantova" | "Piacenza" | "Xi'an" //Magari poi si mette solo string come tipo nel caso si pullino da un DB
18+
iconLanguage?: IconType
19+
language?: "ITA" | "ENG" //Idem
20+
iconSelect?: IconType
21+
}) {
22+
return (
23+
<Card className="typo-body-large flex h-fit w-full flex-row px-5 py-3.75 font-normal leading-6 tracking-[0.03125rem]">
24+
<CardContent className="basis-1/3 truncate">{courseName}</CardContent>
25+
<CardContent className="flex basis-1/3 items-center gap-1.25">
26+
<CardAction icon={IconLocation} iconSize="small" /> {location}
27+
</CardContent>
28+
<CardContent className="flex basis-1/3 items-center gap-1.25">
29+
<CardAction icon={IconLanguage} iconSize="small" /> {language}
30+
</CardContent>
31+
<CardContent className="flex items-center">
32+
<CardAction icon={IconSelect} iconSize="small" />
33+
</CardContent>
34+
</Card>
35+
)
36+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { IconType } from "react-icons"
2+
import { BsBook } from "react-icons/bs"
3+
import { Card, CardAction, CardContent } from "./ui/card"
4+
5+
export function CardPathSelection({ caption, icon: Icon = BsBook }: { caption: string; icon?: IconType }) {
6+
return (
7+
<Card className="h-fit w-136 px-10 py-7.5" hoverBackground>
8+
<CardContent className="typo-body-large flex flex-row items-center justify-center gap-1.25 whitespace-nowrap font-normal leading-6 tracking-[0.03125rem]">
9+
<CardAction icon={Icon} iconSize="small" /> {caption}
10+
</CardContent>
11+
</Card>
12+
)
13+
}

src/components/ui/card.tsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import * as React from "react"
2+
import type { IconType } from "react-icons"
3+
import { cn } from "@/lib/utils"
4+
import { CardHoverBackground } from "../card-icon/hover-background"
5+
import { Glass } from "../glass"
6+
import { Button } from "./button"
7+
8+
function Card({
9+
className,
10+
size = "default",
11+
hoverBackground = false,
12+
children,
13+
...props
14+
}: React.ComponentProps<typeof Glass> & { size?: "default" | "sm"; hoverBackground?: boolean }) {
15+
return (
16+
<Glass
17+
data-slot="card"
18+
data-size={size}
19+
className={cn(
20+
"group/card relative flex h-66 w-78 flex-col gap-4 overflow-hidden rounded-[1.25rem] bg-background-blur text-card-foreground text-sm ring-foreground/10 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
21+
className
22+
)}
23+
{...props}
24+
>
25+
{hoverBackground && <CardHoverBackground />}
26+
{children}
27+
</Glass>
28+
)
29+
}
30+
31+
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
32+
return (
33+
<div
34+
data-slot="card-header"
35+
className={cn(
36+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3",
37+
className
38+
)}
39+
{...props}
40+
/>
41+
)
42+
}
43+
44+
function CardTitle({ gradient = true, className, ...props }: React.ComponentProps<"h3"> & { gradient?: boolean }) {
45+
return (
46+
<h3
47+
data-slot="card-title"
48+
className={cn(
49+
`${gradient ? "bg-linear-to-b from-blue-secondary to-blue-primary bg-clip-text text-transparent" : ""} font-medium leading-snug group-data-[size=sm]/card:text-base`,
50+
className
51+
)}
52+
{...props}
53+
/>
54+
)
55+
}
56+
57+
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
58+
return <div data-slot="card-description" className={cn("text-muted-foreground", className)} {...props} />
59+
}
60+
61+
function CardAction({
62+
className,
63+
icon: Icon,
64+
iconSize = "normal",
65+
gradient = true,
66+
...props
67+
}: React.ComponentProps<"div"> & { icon: IconType; iconSize?: "small" | "normal" | "large"; gradient?: boolean }) {
68+
const gradientId = React.useId()
69+
70+
return (
71+
<div
72+
data-slot="card-action"
73+
className={cn("col-start-2 row-span-2 row-start-1 self-auto justify-self-end", className)}
74+
{...props}
75+
>
76+
{gradient && (
77+
<svg width="0" height="0" className="absolute" aria-hidden="true" focusable="false">
78+
<linearGradient id={gradientId} x1="0%" y1="100%" x2="0%" y2="0%">
79+
<stop offset="0%" className="text-blue-secondary" stopColor="currentColor" />
80+
<stop offset="100%" className="text-blue-primary" stopColor="currentColor" />
81+
</linearGradient>
82+
</svg>
83+
)}
84+
85+
<Icon
86+
size={iconSize === "small" ? "1.125rem" : iconSize === "normal" ? "2rem" : "3.5rem"}
87+
fill={gradient ? `url(#${gradientId})` : "currentColor"}
88+
stroke={gradient ? `url(#${gradientId})` : "currentColor"}
89+
/>
90+
</div>
91+
)
92+
}
93+
94+
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
95+
return <div data-slot="card-content" className={cn("group-data-[size=sm]/card:px-3", className)} {...props} />
96+
}
97+
98+
function CardBottomButton({ className, ...props }: React.ComponentProps<typeof Button>) {
99+
return <Button data-slot="card-footer" className={cn("self-end", className)} {...props} />
100+
}
101+
102+
export { Card, CardHeader, CardBottomButton, CardTitle, CardAction, CardDescription, CardContent }

0 commit comments

Comments
 (0)