Skip to content

Commit

Permalink
migrated ToolsCard component
Browse files Browse the repository at this point in the history
  • Loading branch information
devilkiller-ag committed Mar 15, 2024
1 parent 8e3d9e6 commit 6e066b3
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 0 deletions.
12 changes: 12 additions & 0 deletions components/tools/CardData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@ interface CardDataProps {
type: string;
};

/**
* @description This component displays Card.
*
* @param {SelectTagsProps} props - The props for the Cards Data component.
* @param {string} props.className - Additional CSS classes for the component.
* @param {VisibleType} props.visible - Visibility status for different types.
* @param {string} props.heading - The heading text.
* @param {string} props.data - The data to be displayed.
* @param {boolean} props.read - Read status.
* @param {React.Dispatch<React.SetStateAction<boolean>>} props.setRead - Function to set read status.
* @param {React.Dispatch<React.SetStateAction<VisibleType>>} props.type - Function to set visibility status.
*/
export const CardData = ({
className,
visible,
Expand Down
218 changes: 218 additions & 0 deletions components/tools/ToolsCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { useEffect, useRef, useState } from 'react';
import TextTruncate from 'react-text-truncate';

import { HeadingTypeStyle } from '@/types/typography/Heading';
import { ParagraphTypeStyle } from '@/types/typography/Paragraph';

import Data from '../../scripts/tools/tools-schema.json';
import Heading from '../typography/Heading';
import Paragraph from '../typography/Paragraph';
import { CardData } from './CardData';
import Tag from './Tags';

type VisibleType = Record<string, boolean>;

interface Link {
repoUrl?: string;
websiteUrl?: string;
docsUrl?: string;
};

interface Filter {
language: { name: string; color: string; borderColor: string }[];
technology: { name: string; color: string; borderColor: string }[];
hasCommercial: boolean;
};

interface ToolData {
title: string;
description: string;
links: Link;
filters: Filter;
};

interface ToolsCardProp {
toolData: ToolData;
}

/**
* @description This component displays a card for a tool.
*
* @param {ToolsCardProp} props - Props for the ToolsCard component.
* @param {ToolData} props.toolData - Data of the tool.
*/
export default function ToolsCard({ toolData }: ToolsCardProp) {
const [showDescription, setShowDescription] = useState<boolean>(false);
const [showMoreDescription, setShowMoreDescription] = useState<boolean>(false);
const [readMore, setReadMore] = useState<boolean>(false);
const descriptionRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const divHeight = descriptionRef.current?.offsetHeight || 0;
const numberOfLines = divHeight / 20;

if (numberOfLines > 3) {
setShowMoreDescription(true);
} else {
setShowMoreDescription(false);
};
}, []);

let onGit = false;

if (toolData.links.repoUrl) {
const url = new URL(toolData.links.repoUrl);

if (url.host === 'github.com') {
onGit = true;
} else {
onGit = false;
}
}

const [visible, setVisible] = useState<VisibleType>({
lang: false,
tech: false,
desc: false
});

return (
<div className='flex h-auto flex-col rounded-lg border shadow-md'>
<div className='mb-6 px-6 pt-8'>
<div className='flex flex-col gap-2'>
<div className='flex w-full justify-between gap-4'>
<Heading typeStyle={HeadingTypeStyle.smSemibold}>{toolData.title}</Heading>
<div
className='size-fit min-w-[5.3rem] rounded-md border border-green-600 bg-green-100 p-1 text-center text-xs text-green-600'
onMouseEnter={() => (setTimeout(() => { if (!visible.desc) setVisible({ ...visible, desc: true }); }, 400))}
>
<span
className='group relative'
onMouseLeave={() => (setTimeout(() => { if (visible.desc) setVisible({ ...visible, desc: false }); }, 300))}
>
{toolData.filters.hasCommercial === false ? 'Open Source' : 'Commercial'}
{visible.desc && <span className='absolute -left-2/3 top-8 z-10 w-48 -translate-x-12 rounded border border-gray-200 bg-white px-2 py-1 text-left text-gray-700 shadow-md'>
{Data.properties.filters.properties.hasCommercial.description}
</span>}
</span>
</div>
</div>
<div className='relative'>
<Paragraph typeStyle={ParagraphTypeStyle.sm}>
<div ref={descriptionRef} className={`w-full ${showMoreDescription ? 'cursor-pointer' : ''}`} onMouseEnter={() => (setTimeout(() => { if (showMoreDescription) setShowDescription(true); }, 500))}>
<TextTruncate
element='span'
line={3}
text={toolData.description}
/></div>
</Paragraph>
{showDescription && <div className='absolute top-0 z-10 w-full border border-gray-200 bg-white p-2 shadow-md' onMouseLeave={() => (setShowDescription(false))}>
<Paragraph typeStyle={ParagraphTypeStyle.sm} className=''>
{toolData.description}
</Paragraph>
</div>}
</div>
</div>
</div>
<hr className='mx-6' />
<div className='grow'>
{(toolData?.filters?.language || toolData?.filters?.technology?.length > 0) ? <div className='my-6'>
{toolData.filters.language && <div className='mx-6 flex flex-col gap-2'>
<CardData
className='text-sm
text-gray-700'
heading='LANGUAGE'
data={Data.properties.filters.properties.language.description}
type='lang'
visible={visible}
setVisible={setVisible}
read={readMore}
setRead={setReadMore}
/>
<div className='flex gap-2'>
{toolData.filters.language.map((item, index) => (
<Tag key={index}
name={item.name}
bgColor={item.color}
borderColor={item.borderColor}
/>
))}
</div>
</div>}
{toolData.filters.technology.length > 0 && <div className='mx-6 my-4 flex flex-col gap-2'>
<CardData
className='text-sm text-gray-700'
heading='TECHNOLOGIES'
data={Data.properties.filters.properties.technology.description}
type='tech'
visible={visible}
setVisible={setVisible}
read={readMore}
setRead={setReadMore}
/>
<div className='flex flex-wrap gap-2'>
{toolData.filters.technology.map((item, index) => (
<Tag key={index}
name={item.name}
bgColor={item.color}
borderColor={item.borderColor}
/>
))}
</div>
</div>}
</div> :
<div className='relative size-full p-8 text-center text-gray-700'>
<div className='absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'> No further details provided </div>
</div>}
</div>
{(toolData.links.repoUrl || toolData.links.websiteUrl || toolData.links.docsUrl) && <>
<hr className='' />
<div className='flex'>
{toolData.links.repoUrl && <>
{onGit ?
<a className='w-full cursor-pointer border-x px-1 py-6 text-center hover:bg-gray-200' href={toolData.links.repoUrl} target='_blank' rel='noreferrer' data-testid='ToolsCard-repoUrl'>
<div className='m-auto flex w-fit gap-2'>
<img
src='/img/logos/github-black.svg'
alt='GitHub'
className='w-5'
/>
<div className='text-sm text-gray-700'>View Github</div>
</div>
</a> :
<a className='w-full cursor-pointer border-x border-gray-200 px-1 py-6 text-center hover:bg-gray-200' href={toolData.links.repoUrl} target='_blank' rel='noreferrer'>
<div className='m-auto flex w-fit gap-2'>
<div className='text-sm text-gray-700'>View Source Code</div>
</div>
</a>
}
</>}
{toolData.links.websiteUrl && (
<a className='w-full cursor-pointer border-x border-gray-200 px-1 py-6 text-center hover:bg-gray-200' href={toolData.links.websiteUrl} target='_blank' rel='noreferrer' data-testid='ToolsCard-websiteUrl'>
<div className='m-auto flex w-fit gap-2'>
<img
src='/img/illustrations/icons/share.svg'
alt='Share'
className='w-5'
/>
<div className='text-sm text-gray-700'>Visit Website</div>
</div>
</a>
)}
{toolData.links.docsUrl && (
<a className='w-full cursor-pointer border-x border-gray-200 px-1 py-6 text-center hover:bg-gray-200' href={toolData.links.docsUrl} target='_blank' rel='noreferrer' data-testid='ToolsCard-docsUrl'>
<div className='m-auto flex w-fit gap-2'>
<img
src='/img/illustrations/icons/docs-icon.svg'
alt='Docs'
className='w-5'
/>
<div className='text-sm text-gray-700'>Visit Docs</div>
</div>
</a>
)}
</div>
</>}
</div>
);
};

0 comments on commit 6e066b3

Please sign in to comment.