mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-09 02:36:40 +02:00
Make the question on the roadmap top sticky
This commit is contained in:
@@ -17,7 +17,9 @@ import { totalRoadmapNodes } from '../../stores/roadmap.ts';
|
|||||||
|
|
||||||
type FlowRoadmapRendererProps = {
|
type FlowRoadmapRendererProps = {
|
||||||
isEmbed?: boolean;
|
isEmbed?: boolean;
|
||||||
roadmap: RoadmapDocument;
|
roadmap: RoadmapDocument & {
|
||||||
|
canManage?: boolean
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
||||||
@@ -159,7 +161,7 @@ export function FlowRoadmapRenderer(props: FlowRoadmapRendererProps) {
|
|||||||
{hideRenderer && (
|
{hideRenderer && (
|
||||||
<EmptyRoadmap
|
<EmptyRoadmap
|
||||||
roadmapId={roadmapId}
|
roadmapId={roadmapId}
|
||||||
canManage={roadmap.canManage}
|
canManage={roadmap.canManage || false}
|
||||||
className="grow"
|
className="grow"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
@@ -8,24 +8,43 @@ import {
|
|||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { useOutsideClick } from '../hooks/use-outside-click';
|
import { useOutsideClick } from '../hooks/use-outside-click';
|
||||||
import { markdownToHtml } from '../lib/markdown';
|
import { markdownToHtml } from '../lib/markdown';
|
||||||
|
import { cn } from '../lib/classname';
|
||||||
|
import { useScrollPosition } from '../hooks/use-scroll-position';
|
||||||
|
|
||||||
type RoadmapTitleQuestionProps = {
|
type RoadmapTitleQuestionProps = {
|
||||||
question: string;
|
question: string;
|
||||||
answer: string;
|
answer: string;
|
||||||
|
roadmapId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
||||||
const { question, answer } = props;
|
const { question, answer, roadmapId } = props;
|
||||||
|
|
||||||
const [isAnswerVisible, setIsAnswerVisible] = useState(false);
|
const [isAnswerVisible, setIsAnswerVisible] = useState(false);
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const h2Ref = useRef<HTMLHeadingElement>(null);
|
||||||
|
|
||||||
useOutsideClick(ref, () => {
|
useOutsideClick(ref, () => {
|
||||||
setIsAnswerVisible(false);
|
setIsAnswerVisible(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { y: scrollY } = useScrollPosition();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative hidden rounded-b-[5px] border-t bg-white text-sm font-medium hover:bg-gray-50 sm:block">
|
<div
|
||||||
|
className={cn(
|
||||||
|
'relative hidden rounded-b-[5px] border-t bg-white text-sm font-medium hover:bg-gray-50 sm:block',
|
||||||
|
{
|
||||||
|
'rounded-0 -mx-4 sm:mx-0': isAnswerVisible,
|
||||||
|
// @FIXME:
|
||||||
|
// The line below is to keep the question hidden on mobile devices except for
|
||||||
|
// the frontend roadmap. This is because we did not use to have the question
|
||||||
|
// on mobile devices before and we don't want to cause any SEO issues. It will
|
||||||
|
// be enabled on other roadmaps in the future.
|
||||||
|
block: roadmapId === 'frontend',
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
{isAnswerVisible && (
|
{isAnswerVisible && (
|
||||||
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div>
|
<div className="fixed left-0 right-0 top-0 z-[100] h-full items-center justify-center overflow-y-auto overflow-x-hidden overscroll-contain bg-black/50"></div>
|
||||||
)}
|
)}
|
||||||
@@ -47,15 +66,25 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`absolute left-0 right-0 top-0 z-[100] mt-0 rounded-md border bg-white ${
|
className={`absolute left-0 right-0 top-0 z-[100] mt-0 border bg-white ${
|
||||||
isAnswerVisible ? 'block' : 'hidden'
|
isAnswerVisible ? 'rounded-0 block sm:rounded-md' : 'hidden'
|
||||||
}`}
|
}`}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{isAnswerVisible && (
|
{isAnswerVisible && (
|
||||||
<h2
|
<h2
|
||||||
className="flex cursor-pointer select-none items-center border-b px-[7px] py-[9px] text-base font-medium"
|
className={cn(
|
||||||
onClick={() => setIsAnswerVisible(false)}
|
'sticky top-0 flex cursor-pointer select-none items-center rounded-t-md border-b bg-white px-[7px] py-[9px] text-base font-medium',
|
||||||
|
)}
|
||||||
|
onClick={() => {
|
||||||
|
setIsAnswerVisible(false);
|
||||||
|
if (
|
||||||
|
scrollY > (h2Ref?.current?.getBoundingClientRect().top || 0)
|
||||||
|
) {
|
||||||
|
ref.current?.scrollIntoView();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
ref={h2Ref}
|
||||||
>
|
>
|
||||||
<span className="flex flex-grow items-center">
|
<span className="flex flex-grow items-center">
|
||||||
<Info className="mr-2 inline-block h-4 w-4" strokeWidth={2.5} />
|
<Info className="mr-2 inline-block h-4 w-4" strokeWidth={2.5} />
|
||||||
@@ -67,7 +96,7 @@ export function RoadmapTitleQuestion(props: RoadmapTitleQuestionProps) {
|
|||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
<div
|
<div
|
||||||
className="bg-gray-100 p-3 text-base [&>h2]:mb-2 [&>h2]:mt-5 [&>h2]:text-[17px] [&>h2]:font-medium [&>p:last-child]:mb-0 [&>p>a]:font-semibold [&>p>a]:underline [&>p>a]:underline-offset-2 [&>p]:mb-3 [&>p]:font-normal [&>p]:leading-relaxed [&>p]:text-gray-800"
|
className="bg-gray-100 p-3 text-base [&>h2]:mb-2 [&>h2]:mt-5 [&>h2]:text-[17px] [&>h2]:font-medium [&>p:last-child]:mb-0 [&>p>a]:font-semibold [&>p>a]:underline [&>p>a]:underline-offset-2 [&>p]:mb-3 [&>p]:font-normal [&>p]:leading-relaxed [&>p]:text-gray-800 [&>ul>li]:mb-2 [&>ul>li]:font-normal"
|
||||||
dangerouslySetInnerHTML={{ __html: markdownToHtml(answer, false) }}
|
dangerouslySetInnerHTML={{ __html: markdownToHtml(answer, false) }}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -123,6 +123,7 @@ const projects = await getProjectsByRoadmapId(roadmapId);
|
|||||||
question?.title && (
|
question?.title && (
|
||||||
<RoadmapTitleQuestion
|
<RoadmapTitleQuestion
|
||||||
client:load
|
client:load
|
||||||
|
roadmapId={roadmapId}
|
||||||
question={question?.title}
|
question={question?.title}
|
||||||
answer={question?.description}
|
answer={question?.description}
|
||||||
/>
|
/>
|
||||||
|
Reference in New Issue
Block a user