parent
846bbc1533
commit
bc6b100c26
11 changed files with 197 additions and 49 deletions
@ -1,47 +1,57 @@ |
|||||||
--- |
--- |
||||||
import AstroIcon from './AstroIcon.astro'; |
import AstroIcon from './AstroIcon.astro'; |
||||||
import Icon from './AstroIcon.astro'; |
import Icon from './AstroIcon.astro'; |
||||||
|
import { RoadmapTitleQuestion } from './RoadmapTitleQuestion.tsx'; |
||||||
import ResourceProgressStats from './ResourceProgressStats.astro'; |
import ResourceProgressStats from './ResourceProgressStats.astro'; |
||||||
|
|
||||||
export interface Props { |
export interface Props { |
||||||
roadmapId: string; |
roadmapId: string; |
||||||
tnsBannerLink?: string; |
tnsBannerLink?: string; |
||||||
|
titleQuestion?: string; |
||||||
|
titleAnswer?: string; |
||||||
} |
} |
||||||
|
|
||||||
const { roadmapId, tnsBannerLink = '' } = Astro.props; |
const { |
||||||
|
roadmapId, |
||||||
const hasTNSBanner = !!tnsBannerLink; |
titleQuestion = '', |
||||||
const roadmapTitle = |
titleAnswer = '', |
||||||
roadmapId === 'devops' |
tnsBannerLink, |
||||||
? 'DevOps' |
} = Astro.props; |
||||||
: `${roadmapId.charAt(0).toUpperCase()}${roadmapId.slice(1)}`; |
const hasTitleQuestion = titleQuestion && titleAnswer; |
||||||
|
const hasTnsBanner = !!tnsBannerLink; |
||||||
--- |
--- |
||||||
|
|
||||||
<div |
<div |
||||||
class:list={[ |
class:list={[ |
||||||
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0', |
'mt-4 sm:mt-7 border-0 sm:border rounded-md mb-0 bg-white', |
||||||
{ |
...(hasTnsBanner |
||||||
'sm:-mb-[82px]': hasTNSBanner, |
? [ |
||||||
'sm:-mb-[65px]': !hasTNSBanner, |
{ |
||||||
}, |
'sm:-mb-[103px]': hasTitleQuestion, |
||||||
|
'sm:-mb-[81px]': !hasTitleQuestion, |
||||||
|
}, |
||||||
|
] |
||||||
|
: [ |
||||||
|
{ |
||||||
|
'sm:-mb-[88px]': hasTitleQuestion, |
||||||
|
'sm:-mb-[65px]': !hasTitleQuestion, |
||||||
|
}, |
||||||
|
]), |
||||||
]} |
]} |
||||||
> |
> |
||||||
|
<ResourceProgressStats |
||||||
|
resourceId={roadmapId} |
||||||
|
resourceType='roadmap' |
||||||
|
hasSecondaryBanner={hasTitleQuestion} |
||||||
|
/> |
||||||
|
|
||||||
{ |
{ |
||||||
hasTNSBanner && ( |
hasTitleQuestion && ( |
||||||
<div class='hidden border-b bg-gray-100 px-2 py-1.5 sm:block'> |
<RoadmapTitleQuestion |
||||||
<p class='text-sm'> |
client:load |
||||||
Get the latest {roadmapTitle} news from our sister site{' '} |
question={titleQuestion} |
||||||
<a |
answer={titleAnswer} |
||||||
href={tnsBannerLink} |
/> |
||||||
target='_blank' |
|
||||||
class='font-semibold underline' |
|
||||||
> |
|
||||||
TheNewStack.io |
|
||||||
</a> |
|
||||||
</p> |
|
||||||
</div> |
|
||||||
) |
) |
||||||
} |
} |
||||||
|
</div> |
||||||
<ResourceProgressStats isSecondaryBanner={hasTNSBanner} resourceId={roadmapId} resourceType='roadmap' /> |
|
||||||
</div> |
|
||||||
|
@ -0,0 +1,63 @@ |
|||||||
|
import {ChevronDown, ChevronUp, GraduationCap} from 'lucide-react'; |
||||||
|
import { useRef, useState } from 'react'; |
||||||
|
import { useOutsideClick } from '../hooks/use-outside-click'; |
||||||
|
import {markdownToHtml} from "../lib/markdown"; |
||||||
|
|
||||||
|
type RoadmapTitleQuestionProps = { |
||||||
|
question: string; |
||||||
|
answer: string; |
||||||
|
}; |
||||||
|
|
||||||
|
export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) { |
||||||
|
const { question, answer } = props; |
||||||
|
|
||||||
|
const [isAnswerVisible, setIsAnswerVisible] = useState(false); |
||||||
|
const ref = useRef<HTMLDivElement>(null); |
||||||
|
|
||||||
|
useOutsideClick(ref, () => { |
||||||
|
setIsAnswerVisible(false); |
||||||
|
}); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="relative hidden border-t text-sm font-medium sm:block"> |
||||||
|
{isAnswerVisible && ( |
||||||
|
<div className="fixed left-0 right-0 top-0 z-50 h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div> |
||||||
|
)} |
||||||
|
<h2 |
||||||
|
className="z-50 flex cursor-pointer items-center px-2 py-2 " |
||||||
|
aria-expanded={isAnswerVisible ? 'true' : 'false'} |
||||||
|
onClick={(e) => { |
||||||
|
e.preventDefault(); |
||||||
|
setIsAnswerVisible(!isAnswerVisible); |
||||||
|
}} |
||||||
|
> |
||||||
|
<span className="flex flex-grow"> |
||||||
|
<GraduationCap className="mr-2 inline-block h-5 w-5" /> |
||||||
|
{ question } |
||||||
|
</span> |
||||||
|
<span className="flex-shrink-0 text-gray-400"> |
||||||
|
<ChevronDown className={`inline-block h-5 w-5`} /> |
||||||
|
</span> |
||||||
|
</h2> |
||||||
|
|
||||||
|
{isAnswerVisible && ( |
||||||
|
<div |
||||||
|
className="absolute left-0 right-0 top-0 z-50 mt-0 rounded-md border bg-white" |
||||||
|
ref={ref} |
||||||
|
> |
||||||
|
<p className="flex items-center px-[7px] pt-[7px] pb-[8px] text-sm font-medium border-b cursor-pointer" onClick={() => setIsAnswerVisible(false)}> |
||||||
|
<span className="flex flex-grow items-center"> |
||||||
|
<GraduationCap className="mr-2 inline-block h-5 w-5" /> |
||||||
|
{ question } |
||||||
|
</span> |
||||||
|
<span className="flex-shrink-0 text-gray-400"> |
||||||
|
<ChevronUp className={`inline-block h-5 w-5`} /> |
||||||
|
</span> |
||||||
|
</p> |
||||||
|
<div className='p-3 text-base bg-gray-100 [&>h2]:text-lg [&>h2]:font-semibold [&>h2]:mb-1 [&>h2]:mt-5 [&>p>a]:font-semibold [&>p>a]:underline [&>p>a]:underline-offset-2 [&>p:last-child]:mb-0 [&>p]:mb-3 [&>p]:font-normal [&>p]:leading-relaxed' dangerouslySetInnerHTML={{ __html: markdownToHtml(answer, false) }}> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
); |
||||||
|
} |
Loading…
Reference in new issue