Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 64 additions & 62 deletions src/components/QuestionItem/question-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { QuestionItemSummaryButton } from "@/components/QuestionItem/question-it
import summaryStore from "@/stores/summaryStore.ts";
import type { Question } from "@/types/question.types.ts";
import { useStore } from "@nanostores/react";
import { type MouseEventHandler, useCallback, useState } from "react";
import { memo, type MouseEventHandler, useCallback, useState } from "react";
import { Card } from "@/components/ui";
import { clsx } from "clsx";

Expand All @@ -14,68 +14,70 @@ interface QuestionItemProperties extends Question {
noSummary?: boolean;
}

export const QuestionItem = ({
className,
name,
tags,
answer,
references,
slug,
similars,
noSummary,
}: QuestionItemProperties) => {
const [isExpanded, setIsExpanded] = useState(false);
export const QuestionItem = memo(
({
className,
name,
tags,
answer,
references,
slug,
similars,
noSummary,
}: QuestionItemProperties) => {
const [isExpanded, setIsExpanded] = useState(false);

const summary = useStore(summaryStore.state);
const isQuestionInSummary =
summary.questions.findIndex((question) => question.slug === slug) !== -1;
const summary = useStore(summaryStore.state);
const isQuestionInSummary =
summary.questions.findIndex((question) => question.slug === slug) !== -1;

const onSummaryButtonClick = useCallback<
MouseEventHandler<HTMLButtonElement>
>((event) => {
event.stopPropagation();
const onSummaryButtonClick = useCallback<
MouseEventHandler<HTMLButtonElement>
>((event) => {
event.stopPropagation();

summaryStore.questions.toggle({
slug,
tags,
answer,
name,
references,
similars,
});
}, []);
summaryStore.questions.toggle({
slug,
tags,
answer,
name,
references,
similars,
});
}, []);

// QuestionItem className={clsx(styles.questionItem, {[styles.questionItem_active]: isQuestionInSummary, [styles.questionItem_summary]: !noSummary}, className)}
return (
<div
className={clsx(
"flex select-none hover:pl-0",
{
"-ml-16 pl-16 group": !noSummary,
"!pl-0": isQuestionInSummary,
},
className,
)}
onClick={() => setIsExpanded(!isExpanded)}
>
{!noSummary && (
<QuestionItemSummaryButton
className="group-hover:block"
onClick={onSummaryButtonClick}
isActive={isQuestionInSummary}
/>
)}
<Card className="w-full py-3 px-4 cursor-pointer transition-all">
<QuestionItemHeader name={name} tags={tags} />
{isExpanded && <QuestionItemDescription answer={answer} />}
<QuestionItemReferences
references={references}
similars={similars}
noSummary={noSummary}
slug={slug}
isExpanded={isExpanded}
/>
</Card>
</div>
);
};
// QuestionItem className={clsx(styles.questionItem, {[styles.questionItem_active]: isQuestionInSummary, [styles.questionItem_summary]: !noSummary}, className)}
return (
<div
className={clsx(
"flex select-none hover:pl-0",
{
"-ml-16 pl-16 group": !noSummary,
"!pl-0": isQuestionInSummary,
},
className,
)}
onClick={() => setIsExpanded(!isExpanded)}
>
{!noSummary && (
<QuestionItemSummaryButton
className="group-hover:block"
onClick={onSummaryButtonClick}
isActive={isQuestionInSummary}
/>
)}
<Card className="w-full py-3 px-4 cursor-pointer transition-all">
<QuestionItemHeader name={name} tags={tags} />
{isExpanded && <QuestionItemDescription answer={answer} />}
<QuestionItemReferences
references={references}
similars={similars}
noSummary={noSummary}
slug={slug}
isExpanded={isExpanded}
/>
</Card>
</div>
);
},
);
29 changes: 14 additions & 15 deletions src/components/QuestionList/question-list.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { QuestionItem } from "@/components/QuestionItem";
import type { Question } from "@/types/question.types.ts";
import { clsx } from "clsx";
import { memo } from "react";

interface QuestionListProperties {
list: Question[];
noSummary?: boolean;
className?: string | string[];
}

export const QuestionList = ({
className,
list,
noSummary,
}: QuestionListProperties) => {
return (
<ul className={clsx("flex flex-col gap-4 py-4", className)}>
{list.map((item) => (
<li key={item.slug} id={item.slug} className="scroll-my-32">
<QuestionItem noSummary={noSummary} {...item} />
</li>
))}
</ul>
);
};
export const QuestionList = memo(
({ className, list, noSummary }: QuestionListProperties) => {
return (
<ul className={clsx("flex flex-col gap-4 py-4", className)}>
{list.map((item) => (
<li key={item.slug} id={item.slug} className="scroll-my-32">
<QuestionItem noSummary={noSummary} {...item} />
</li>
))}
</ul>
);
},
);
63 changes: 37 additions & 26 deletions src/components/ThemeToggle/theme-toggle.tsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,77 @@
import { Button, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui";
import {
Button,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui";
import { capitalize } from "@/utils/capitalize.ts";
import { loadIcons } from "@iconify-icon/react";
import { Icon } from "@iconify-icon/react/dist/iconify.js";
import { useEffect, useState } from "react";

loadIcons(['gravity-ui:moon', 'gravity-ui:sun', 'gravity-ui:display'])
loadIcons(["gravity-ui:moon", "gravity-ui:sun", "gravity-ui:display"]);

type Theme = 'light' | 'dark' | 'system';
type Theme = "light" | "dark" | "system";

export const ThemeToggle = () => {
const [theme, setTheme] = useState<Theme>('system');
const [theme, setTheme] = useState<Theme>("system");

const icon = theme === 'light' ? 'gravity-ui:sun' : (theme === 'dark' ? 'gravity-ui:moon' : 'gravity-ui:display');
const icon =
theme === "light"
? "gravity-ui:sun"
: theme === "dark"
? "gravity-ui:moon"
: "gravity-ui:display";

const toggleTheme = () => {
if (theme === 'system') {
setTheme('dark')
} else if (theme === 'dark') {
setTheme('light')
if (theme === "system") {
setTheme("dark");
} else if (theme === "dark") {
setTheme("light");
} else {
setTheme('system')
setTheme("system");
}
};

useEffect(() => {
let finalTheme: Exclude<Theme, "system">;

switch (theme) {
case 'system': {
finalTheme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
break
case "system": {
finalTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
break;
}
case "dark": {
finalTheme = 'dark';
break
finalTheme = "dark";
break;
}
case "light": {
finalTheme = 'light';
finalTheme = "light";
}
}

if (finalTheme === 'dark') {
document.documentElement.classList.add('dark');
if (finalTheme === "dark") {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove('dark');
document.documentElement.classList.remove("dark");
}
}, [theme]);
}, [theme]);

return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<TooltipTrigger asChild>
<Button onClick={toggleTheme} size="icon" variant="outline">
<Icon
icon={icon}
/>
<Icon icon={icon} />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>{capitalize(theme) + " theme"}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)
}
);
};
Loading