Skip to content

Commit 266f323

Browse files
committed
feat: changed Skills section to list of badges
1 parent e49cc6f commit 266f323

File tree

3 files changed

+67
-136
lines changed

3 files changed

+67
-136
lines changed

src/app/page.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,23 @@ export default function Portfolio() {
8787
},
8888
]
8989

90+
const skillsArray = [
91+
"Python",
92+
"JavaScript (ES6)",
93+
"TypeScript",
94+
"Django",
95+
"Flask",
96+
"React",
97+
"Next.js",
98+
"Node.js",
99+
"Postgres",
100+
"MySQL",
101+
"Docker",
102+
"Bash",
103+
"Powershell",
104+
"C",
105+
"C++"
106+
]
90107

91108
return (
92109
<div className="bg-neutral-100 dark:bg-neutral-950 text-gray-800 dark:text-gray-200 transition-colors duration-300">
@@ -97,7 +114,7 @@ export default function Portfolio() {
97114
<About />
98115
<Experiences arr={experiencesArray}/>
99116
<Education />
100-
<Skills />
117+
<Skills arr={skillsArray}/>
101118
<Projects arr={projectsArray}/>
102119
</main>
103120
<Footer />

src/components/Skills.tsx

Lines changed: 15 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,24 @@
1-
import { useMediaQuery } from '@/hooks/useMediaQuery';
2-
import { BaselineIcon, GalleryVerticalEndIcon } from 'lucide-react';
3-
import { Fragment, FunctionComponent, useState } from 'react';
4-
import { techIcons } from '@/lib/techIcons';
1+
import { Badge } from '@/components/ui/Badge';
2+
import { useState } from 'react';
53

6-
interface Props extends React.SVGProps<SVGElement> {
7-
size?: number | string;
4+
interface SkillsProps {
5+
arr: string[];
86
}
97

10-
type TechIconsDisplayProps = {
11-
heading: string
12-
techNames: string[]
13-
}
14-
15-
function TechIconsDisplay({ heading, techNames }: TechIconsDisplayProps) {
16-
const hasRemainingIcons = (list: string[], startIndex: number, icons = techIcons): boolean => {
17-
for (let i = startIndex + 1; i < list.length; i++) {
18-
if (list[i] in icons) {
19-
return true;
20-
}
21-
}
22-
return false;
23-
}
24-
25-
const isDesktop = useMediaQuery('(min-width: 768px)')
26-
const TechIcon: React.FunctionComponent<{ techName: string }> = ({techName}) => {
27-
// TODO: For techName = SQL, handle multiple SQL service like Postgres, MYSQL, MongoDB, etc.
28-
const IconComponent: React.FunctionComponent<Props> = techIcons[techName]
29-
return (
30-
<div className="items-center justify-center">
31-
<IconComponent size={isDesktop ? 50 : 35}/>
32-
</div>
33-
)
34-
}
35-
const [showIcons, setShowIcons] = useState(false)
36-
const toggleView = () => setShowIcons(!showIcons)
37-
38-
return (
39-
<div className="px-4">
40-
<div className="inline-flex items-center mb-2">
41-
<h3 className="text-xl underline underline-offset-4 items-center">
42-
<span className="font-bold">{heading}</span>
43-
</h3>
44-
<span>&nbsp;&nbsp;&nbsp;</span>
45-
{/* {
46-
showIcons
47-
? <BaselineIcon onClick={toggleView} className="w-5 h-5 md:w-6 md:h-6"/>
48-
: <GalleryVerticalEndIcon onClick={toggleView} className="w-5 h-5 md:w-6 md:h-6"/>
49-
} */}
50-
</div>
51-
{techNames.length > 0 ? (
52-
<div>
53-
{/* TODO: Make this compatible for mobile screens in the future */}
54-
{/* className="md:flex md:flex-row md:items-start md:justify-start grid grid-cols-2" */}
55-
{/* {techNames.map((lang, index) => (
56-
(lang in techIcons) && <Fragment key={lang}>
57-
{showIcons ? (
58-
<div className="inline-flex items-center justify-center" aria-label={lang}>
59-
<div className="bg-white p-1 rounded-md shadow-sm">
60-
<TechIcon techName={lang} />
61-
</div>
62-
</div>
63-
) : (
64-
<span className="inline-flex items-center">
65-
{lang}
66-
{index < techNames.length - 1 && hasRemainingIcons(techNames, index) && (
67-
<span className="mr-1">,</span>
68-
)}
69-
</span>
70-
)}
71-
</Fragment>
72-
))} */}
73-
{techNames.map((lang, index) => (
74-
<span className="inline-flex items-center">
75-
{lang}
76-
{index < techNames.length - 1 && (
77-
<span key={index} className="mr-1">,</span>
78-
)}
79-
</span>
80-
))}
81-
</div>
82-
) : (
83-
<p className="text-gray-600">Nothing to display.</p>
84-
)}
85-
</div>
86-
)
87-
}
88-
89-
export function Skills() {
8+
export function Skills({ arr }: SkillsProps) {
9+
const [skills, setSkills] = useState(arr)
9010
return (
9111
<section id="skills" className="mb-12">
9212
<h2 className="text-2xl md:text-3xl lg:text-4xl font-section font-bold mb-4">Skills</h2>
93-
<div className="flex flex-col justify-around">
94-
{/* TODO: Check if this fits in small screens & if this is good at all or not */}
95-
<TechIconsDisplay
96-
heading="Programming Languages:"
97-
techNames={["Python3", "HTML5", "CSS3", "JavaScript (ES6)", "TypeScript", "Go", "C", "C++20", "SQL"]}
98-
/>
99-
<TechIconsDisplay
100-
heading="Frameworks:"
101-
techNames={[
102-
"Django",
103-
"Flask",
104-
"React",
105-
"Next.js",
106-
"Node.js",
107-
"Axios",
108-
"Socket.IO",
109-
"TailwindCSS",
110-
"D3.js",
111-
"Jest",
112-
"Cypress",
113-
"Pytest"
114-
]}
115-
/>
116-
<TechIconsDisplay
117-
heading="Libraries:"
118-
techNames={[
119-
"Pandas",
120-
"Matplotlib",
121-
"Plotly",
122-
"NumPy",
123-
"OpenCV",
124-
"Pillow",
125-
"NetworkX",
126-
]}
127-
/>
128-
<TechIconsDisplay
129-
heading="Tools:"
130-
techNames={[
131-
"Git",
132-
"Linux",
133-
"Bash",
134-
"Powershell",
135-
"Docker",
136-
"Postman",
137-
"GCP",
138-
"AWS",
139-
]}
140-
/>
141-
</div>
13+
{skills && skills.length > 0 ? (
14+
<div className="space-x-2">
15+
{skills.map((skillText, index) => (
16+
<Badge key={index}>{skillText}</Badge>
17+
))}
18+
</div>
19+
) : (
20+
<p>No skills to display.</p>
21+
)}
14222
</section>
14323
)
14424
}

src/components/ui/Badge.tsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import * as React from "react"
2+
import { cva, type VariantProps } from "class-variance-authority"
3+
4+
import { cn } from "@/lib/utils"
5+
6+
const badgeVariants = cva(
7+
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8+
{
9+
variants: {
10+
variant: {
11+
default:
12+
"border-transparent shadow bg-neutral-950 text-gray-200 hover:bg-neutral-950/80 dark:bg-neutral-100 dark:text-gray-800 dark:hover:bg-neutral-100/80 focus:bg-neutral-900 dark:focus:bg-neutral-200",
13+
secondary:
14+
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80 focus:bg-secondary-focus",
15+
outline: "text-foreground focus:bg-accent focus:text-accent-foreground",
16+
},
17+
},
18+
defaultVariants: {
19+
variant: "default",
20+
},
21+
}
22+
)
23+
24+
export interface BadgeProps
25+
extends React.HTMLAttributes<HTMLDivElement>,
26+
VariantProps<typeof badgeVariants> {}
27+
28+
function Badge({ className, variant, ...props }: BadgeProps) {
29+
return (
30+
<div className={cn(badgeVariants({ variant }), className)} {...props} />
31+
)
32+
}
33+
34+
export { Badge, badgeVariants }

0 commit comments

Comments
 (0)