parent
17b4b05368
commit
5ef8a3f82a
12 changed files with 235 additions and 378 deletions
@ -0,0 +1,42 @@ |
||||
import { useCopyText } from '../../hooks/use-copy-text'; |
||||
import CopyIcon from '../../icons/copy.svg'; |
||||
|
||||
type EditorProps = { |
||||
title: string; |
||||
text: string; |
||||
}; |
||||
|
||||
export function Editor(props: EditorProps) { |
||||
const { text, title } = props; |
||||
|
||||
const { isCopied, copyText } = useCopyText(); |
||||
|
||||
return ( |
||||
<div className="flex w-full flex-grow flex-col overflow-hidden rounded border border-gray-300 bg-gray-50"> |
||||
<div className="flex items-center justify-between gap-2 border-b border-gray-300 px-3 py-2"> |
||||
<span className="text-xs uppercase leading-none text-gray-400"> |
||||
{title} |
||||
</span> |
||||
<button className="flex items-center" onClick={() => copyText(text)}> |
||||
{isCopied && ( |
||||
<span className="mr-1 text-xs leading-none text-gray-700"> |
||||
Copied! |
||||
</span> |
||||
)} |
||||
|
||||
<img src={CopyIcon} alt="Copy" className="inline-block h-4 w-4" /> |
||||
</button> |
||||
</div> |
||||
<textarea |
||||
className="no-scrollbar block h-12 w-full overflow-x-auto whitespace-nowrap bg-gray-200/70 p-3 text-sm text-gray-900 focus:bg-gray-50 focus:outline-0" |
||||
readOnly |
||||
onClick={(e: any) => { |
||||
e.target.select(); |
||||
copyText(e.target.value); |
||||
}} |
||||
> |
||||
{text} |
||||
</textarea> |
||||
</div> |
||||
); |
||||
} |
@ -1,6 +1,6 @@ |
||||
export function GithubReadmeBanner() { |
||||
export function GitHubReadmeBanner() { |
||||
return ( |
||||
<p className="mt-3 rounded-md border p-2 text-sm"> |
||||
<p className="mt-3 rounded-md border p-2 text-sm w-full bg-yellow-100 border-yellow-400 text-yellow-900"> |
||||
Add this badge to your{' '} |
||||
<a |
||||
href="https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-profile/customizing-your-profile/managing-your-profile-readme" |
@ -0,0 +1,23 @@ |
||||
type SelectionButtonProps = { |
||||
text: string; |
||||
isDisabled: boolean; |
||||
isSelected: boolean; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
export function SelectionButton(props: SelectionButtonProps) { |
||||
const { text, isDisabled, isSelected, onClick } = props; |
||||
|
||||
return ( |
||||
<button |
||||
className={`rounded-md border p-1 px-2 text-sm ${ |
||||
isSelected ? ' border-gray-500 bg-gray-300 ' : '' |
||||
} ${ |
||||
!isDisabled ? ' cursor-pointer ' : ' cursor-not-allowed opacity-40 ' |
||||
}`}
|
||||
onClick={onClick} |
||||
> |
||||
{text} |
||||
</button> |
||||
); |
||||
} |
@ -0,0 +1,17 @@ |
||||
type StepCounterProps = { |
||||
step: number; |
||||
}; |
||||
|
||||
export function StepCounter(props: StepCounterProps) { |
||||
const { step } = props; |
||||
|
||||
return ( |
||||
<span |
||||
className={ |
||||
'flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gray-300 text-white' |
||||
} |
||||
> |
||||
{step} |
||||
</span> |
||||
); |
||||
} |
@ -1,41 +0,0 @@ |
||||
import { downloadImage } from '../../helper/download-image'; |
||||
import { useCopyText } from '../../hooks/use-copy-text'; |
||||
import type { BadgeProps } from './RoadCardPage'; |
||||
|
||||
export function LongBadge({ badgeUrl }: BadgeProps) { |
||||
const { isCopied, copyText } = useCopyText(); |
||||
|
||||
return ( |
||||
<div className="col-span-2"> |
||||
<a |
||||
href={badgeUrl} |
||||
target="_blank" |
||||
rel="noopener noreferrer" |
||||
className="relative block aspect-[422/551] w-full hover:cursor-pointer" |
||||
> |
||||
<img |
||||
src={badgeUrl} |
||||
alt="Road Card" |
||||
className="absolute left-0 top-0 h-full w-full object-cover" |
||||
/> |
||||
</a> |
||||
|
||||
<div className="mt-3 grid grid-cols-2 gap-2"> |
||||
<button |
||||
className="flex h-8 items-center justify-center whitespace-nowrap rounded border border-gray-300 bg-gray-50 px-2 text-sm font-medium leading-none hover:opacity-75" |
||||
onClick={() => |
||||
downloadImage({ url: badgeUrl, name: 'road-card', scale: 4 }) |
||||
} |
||||
> |
||||
Download |
||||
</button> |
||||
<button |
||||
className="flex h-8 cursor-pointer items-center justify-center whitespace-nowrap rounded border border-gray-300 bg-gray-50 px-2 text-sm font-medium leading-none hover:opacity-75" |
||||
onClick={() => copyText(badgeUrl)} |
||||
> |
||||
{isCopied ? 'Copied!' : 'Copy Link'} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -1,90 +0,0 @@ |
||||
import { useState } from 'preact/hooks'; |
||||
import { LongBadge } from './TallBadge'; |
||||
import { Editor } from './RoadCardPage'; |
||||
import { GithubReadmeBanner } from './GithubReadmeBanner'; |
||||
import { useAuth } from '../../hooks/use-auth'; |
||||
import { getBadgeLink } from '../../helper/get-badge-link'; |
||||
import type { RoadmapOptionProps } from './RoadmapSelect'; |
||||
|
||||
export function TallBadgeTab({ |
||||
selectedRoadmaps, |
||||
}: { |
||||
selectedRoadmaps: RoadmapOptionProps[]; |
||||
}) { |
||||
const [selectedVariant, setSelectedVariant] = useState<'dark' | 'light'>( |
||||
'dark' |
||||
); |
||||
const user = useAuth(); |
||||
|
||||
if (!user) { |
||||
return null; |
||||
} |
||||
const { badgeUrl, textareaContent, markdownSnippet } = getBadgeLink({ |
||||
user, |
||||
variant: selectedVariant, |
||||
badge: 'tall', |
||||
roadmaps: selectedRoadmaps, |
||||
}); |
||||
|
||||
return ( |
||||
<div className="sm:grid sm:grid-cols-5 sm:gap-6"> |
||||
<div className="block sm:hidden mb-6"> |
||||
<span className="text-xs uppercase leading-none text-gray-400"> |
||||
Variant |
||||
</span> |
||||
|
||||
<div className="mt-2 flex items-center gap-2"> |
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'dark' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('dark')} |
||||
> |
||||
Dark |
||||
</button> |
||||
|
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'light' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('light')} |
||||
> |
||||
Light |
||||
</button> |
||||
</div> |
||||
</div> |
||||
<LongBadge badgeUrl={badgeUrl} /> |
||||
|
||||
<div className="mt-6 sm:col-span-3 sm:mt-0"> |
||||
<div className="hidden sm:block"> |
||||
<span className="text-xs uppercase leading-none text-gray-400"> |
||||
Variant |
||||
</span> |
||||
|
||||
<div className="mt-2 flex items-center gap-2"> |
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'dark' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('dark')} |
||||
> |
||||
Dark |
||||
</button> |
||||
|
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'light' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('light')} |
||||
> |
||||
Light |
||||
</button> |
||||
</div> |
||||
</div> |
||||
|
||||
<div className="sm:mt-4 flex flex-col gap-3"> |
||||
<Editor title={'HTML'} text={textareaContent} /> |
||||
<Editor title={'Markdown'} text={markdownSnippet} /> |
||||
</div> |
||||
|
||||
<GithubReadmeBanner /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -1,40 +0,0 @@ |
||||
import { downloadImage } from '../../helper/download-image'; |
||||
import { useCopyText } from '../../hooks/use-copy-text'; |
||||
import type { BadgeProps } from './RoadCardPage'; |
||||
|
||||
export function WideBadge({ badgeUrl }: BadgeProps) { |
||||
const { isCopied, copyText } = useCopyText(); |
||||
return ( |
||||
<div> |
||||
<a |
||||
href={badgeUrl} |
||||
target="_blank" |
||||
rel="noopener noreferrer" |
||||
className="relative block aspect-[2.63/1] w-full hover:cursor-pointer" |
||||
> |
||||
<img |
||||
src={badgeUrl} |
||||
alt="Road Card" |
||||
className="absolute left-0 top-0 h-full w-full object-cover" |
||||
/> |
||||
</a> |
||||
|
||||
<div className="mt-3 grid grid-cols-2 gap-4"> |
||||
<button |
||||
className="flex h-8 items-center justify-center whitespace-nowrap rounded border border-gray-300 bg-gray-50 px-2 text-sm font-medium leading-none hover:opacity-75" |
||||
onClick={() => |
||||
downloadImage({ url: badgeUrl, name: 'road-card', scale: 4 }) |
||||
} |
||||
> |
||||
Download |
||||
</button> |
||||
<button |
||||
className="flex h-8 cursor-pointer items-center justify-center whitespace-nowrap rounded border border-gray-300 bg-gray-50 px-2 text-sm font-medium leading-none hover:opacity-75" |
||||
onClick={() => copyText(badgeUrl)} |
||||
> |
||||
{isCopied ? 'Copied!' : 'Copy Link'} |
||||
</button> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -1,67 +0,0 @@ |
||||
import { useState } from 'preact/hooks'; |
||||
import { useAuth } from '../../hooks/use-auth'; |
||||
import { WideBadge } from './WideBadge'; |
||||
import { Editor } from './RoadCardPage'; |
||||
import { GithubReadmeBanner } from './GithubReadmeBanner'; |
||||
import { getBadgeLink } from '../../helper/get-badge-link'; |
||||
import type { RoadmapOptionProps } from './RoadmapSelect'; |
||||
|
||||
export function WideBadgeTab({ |
||||
selectedRoadmaps, |
||||
}: { |
||||
selectedRoadmaps: RoadmapOptionProps[]; |
||||
}) { |
||||
const [selectedVariant, setSelectedVariant] = useState<'dark' | 'light'>( |
||||
'dark' |
||||
); |
||||
const user = useAuth(); |
||||
if (!user) { |
||||
return null; |
||||
} |
||||
|
||||
const { badgeUrl, textareaContent, markdownSnippet } = getBadgeLink({ |
||||
user, |
||||
variant: selectedVariant, |
||||
badge: 'wide', |
||||
roadmaps: selectedRoadmaps, |
||||
}); |
||||
|
||||
return ( |
||||
<div className="flex flex-col gap-6"> |
||||
<div> |
||||
<span className="text-xs uppercase leading-none text-gray-400"> |
||||
Variant |
||||
</span> |
||||
|
||||
<div className="mt-2 flex items-center gap-2"> |
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'dark' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('dark')} |
||||
> |
||||
Dark |
||||
</button> |
||||
|
||||
<button |
||||
className={`flex h-7 items-center justify-center rounded-lg border border-gray-200 px-3 text-sm leading-none hover:opacity-80 ${selectedVariant === 'light' && 'border-gray-300 bg-gray-100' |
||||
}`}
|
||||
onClick={() => setSelectedVariant('light')} |
||||
> |
||||
Light |
||||
</button> |
||||
</div> |
||||
</div> |
||||
|
||||
<WideBadge badgeUrl={badgeUrl} /> |
||||
|
||||
<div> |
||||
<div className={`flex flex-col gap-3 sm:flex-row`}> |
||||
<Editor title={'HTML'} text={textareaContent} /> |
||||
<Editor title={'Markdown'} text={markdownSnippet} /> |
||||
</div> |
||||
|
||||
<GithubReadmeBanner /> |
||||
</div> |
||||
</div> |
||||
); |
||||
} |
@ -1,43 +0,0 @@ |
||||
import type { useAuth } from '../hooks/use-auth'; |
||||
|
||||
export type GetBadgeLinkProps = { |
||||
user: ReturnType<typeof useAuth>; |
||||
variant: 'dark' | 'light'; |
||||
badge: 'tall' | 'wide'; |
||||
roadmaps?: string[]; |
||||
}; |
||||
|
||||
export function getBadgeLink({ |
||||
user, |
||||
variant, |
||||
badge, |
||||
roadmaps, |
||||
}: GetBadgeLinkProps) { |
||||
const badgeUrl = new URL( |
||||
`${import.meta.env.PUBLIC_API_URL}/v1-badge/${badge}/${user?.id}` |
||||
); |
||||
if (variant) { |
||||
badgeUrl.searchParams.set('variant', variant); |
||||
} |
||||
|
||||
if (roadmaps?.length) { |
||||
badgeUrl.searchParams.set('roadmaps', roadmaps.join(',')); |
||||
} |
||||
|
||||
const textareaContent = ` |
||||
<a href="${badgeUrl}"> |
||||
<img src="${badgeUrl}" alt="${user?.name}${user?.name && "'s"} Road Card"/> |
||||
</a> |
||||
`.trim();
|
||||
const markdownSnippet = ` |
||||
[![${user?.name}${ |
||||
user?.name && "'s" |
||||
} Road Card](${badgeUrl})](${badgeUrl}) |
||||
`.trim();
|
||||
|
||||
return { |
||||
badgeUrl: badgeUrl.toString(), |
||||
textareaContent, |
||||
markdownSnippet, |
||||
}; |
||||
} |
Before Width: | Height: | Size: 339 B After Width: | Height: | Size: 331 B |
Loading…
Reference in new issue