Skip to content

Commit

Permalink
Merge pull request #114 from Canvas-Diary/feature/onboard
Browse files Browse the repository at this point in the history
Feature/onboard
  • Loading branch information
leve68 authored Nov 27, 2024
2 parents b50b0d7 + 791c36c commit 7ba3dd2
Show file tree
Hide file tree
Showing 15 changed files with 463 additions and 18 deletions.
17 changes: 3 additions & 14 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<link
rel="preload"
href="./assets/fonts/Binggrae.otf"
as="font"
type="font/otf"
crossorigin="anonymous"
/>
<link
rel="preload"
href="./assets/fonts/Binggrae-Bold.otf"
as="font"
type="font/otf"
crossorigin="anonymous"
/>
<meta property="og:image" content="ogImage.png" />
<meta property="og:site_name" content="Canvas Diary" />
<meta property="og:description" content="감정을 그리는 일기, 캔버스 일기" />
<link rel="icon" href="icons/favicon.ico" />
<link rel="alternate icon" href="icons/favicon.ico" type="ico" sizes="32x32" />
<link rel="apple-touch-icon" href="icons/favicon-150x150.png" sizes="152x152" />
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.1",
"@tanstack/react-query": "^5.59.20",
"@tanstack/react-query-devtools": "^5.60.5",
Expand All @@ -22,6 +23,7 @@
"clsx": "^2.1.1",
"echarts": "^5.5.1",
"echarts-wordcloud": "^2.1.0",
"embla-carousel-react": "^8.5.1",
"lucide-react": "^0.456.0",
"next-themes": "^0.4.3",
"postcss": "^8.4.47",
Expand Down
Binary file added public/ogImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion public/vite.svg

This file was deleted.

Binary file added src/assets/images/onboard1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/onboard2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/onboard3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/images/onboard4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/pages/main/home/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ const Calendar = ({
useEffect(() => {
const calendar = generateCalendar(currentDate.getFullYear(), currentDate.getMonth());
setCalendarDays(calendar);
}, [calendarData, currentDate]);
}, [calendarData]);

const generateCalendar = (year: number, month: number) => {
const firstDayOfMonth = new Date(year, month, 1);
Expand Down
56 changes: 56 additions & 0 deletions src/components/ui/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline:
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 rounded-md px-3",
lg: "h-11 rounded-md px-8",
icon: "h-10 w-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)

export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}

const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
)
Button.displayName = "Button"

export { Button, buttonVariants }
240 changes: 240 additions & 0 deletions src/components/ui/carousel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
import * as React from "react";
import useEmblaCarousel, { type UseEmblaCarouselType } from "embla-carousel-react";
import { ArrowLeft, ArrowRight } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";

type CarouselApi = UseEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];

type CarouselProps = {
opts?: CarouselOptions;
plugins?: CarouselPlugin;
orientation?: "horizontal" | "vertical";
setApi?: (api: CarouselApi) => void;
};

type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
api: ReturnType<typeof useEmblaCarousel>[1];
scrollPrev: () => void;
scrollNext: () => void;
canScrollPrev: boolean;
canScrollNext: boolean;
} & CarouselProps;

const CarouselContext = React.createContext<CarouselContextProps | null>(null);

function useCarousel() {
const context = React.useContext(CarouselContext);

if (!context) {
throw new Error("useCarousel must be used within a <Carousel />");
}

return context;
}

const Carousel = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement> & CarouselProps
>(({ orientation = "horizontal", opts, setApi, plugins, className, children, ...props }, ref) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
axis: orientation === "horizontal" ? "x" : "y",
},
plugins
);
const [canScrollPrev, setCanScrollPrev] = React.useState(false);
const [canScrollNext, setCanScrollNext] = React.useState(false);

const onSelect = React.useCallback((api: CarouselApi) => {
if (!api) {
return;
}

setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext());
}, []);

const scrollPrev = React.useCallback(() => {
api?.scrollPrev();
}, [api]);

const scrollNext = React.useCallback(() => {
api?.scrollNext();
}, [api]);

const handleKeyDown = React.useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "ArrowLeft") {
event.preventDefault();
scrollPrev();
} else if (event.key === "ArrowRight") {
event.preventDefault();
scrollNext();
}
},
[scrollPrev, scrollNext]
);

React.useEffect(() => {
if (!api || !setApi) {
return;
}

setApi(api);
}, [api, setApi]);

React.useEffect(() => {
if (!api) {
return;
}

onSelect(api);
api.on("reInit", onSelect);
api.on("select", onSelect);

return () => {
api?.off("select", onSelect);
};
}, [api, onSelect]);

return (
<CarouselContext.Provider
value={{
carouselRef,
api: api,
opts,
orientation: orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext,
}}
>
<div
ref={ref}
onKeyDownCapture={handleKeyDown}
className={cn("relative", className)}
role="region"
aria-roledescription="carousel"
{...props}
>
{children}
</div>
</CarouselContext.Provider>
);
});
Carousel.displayName = "Carousel";

const CarouselContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const { carouselRef, orientation } = useCarousel();

return (
<div ref={carouselRef} className="h-full overflow-hidden">
<div
ref={ref}
className={cn(
"flex",
orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col",
className
)}
{...props}
/>
</div>
);
}
);
CarouselContent.displayName = "CarouselContent";

const CarouselItem = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className, ...props }, ref) => {
const { orientation } = useCarousel();

return (
<div
ref={ref}
role="group"
aria-roledescription="slide"
className={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
className
)}
{...props}
/>
);
}
);
CarouselItem.displayName = "CarouselItem";

const CarouselPrevious = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel();

return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollPrev}
onClick={scrollPrev}
{...props}
>
<ArrowLeft className="h-4 w-4" />
<span className="sr-only">Previous slide</span>
</Button>
);
}
);
CarouselPrevious.displayName = "CarouselPrevious";

const CarouselNext = React.forwardRef<HTMLButtonElement, React.ComponentProps<typeof Button>>(
({ className, variant = "outline", size = "icon", ...props }, ref) => {
const { orientation, scrollNext, canScrollNext } = useCarousel();

return (
<Button
ref={ref}
variant={variant}
size={size}
className={cn(
"absolute h-8 w-8 rounded-full",
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
className
)}
disabled={!canScrollNext}
onClick={scrollNext}
{...props}
>
<ArrowRight className="h-4 w-4" />
<span className="sr-only">Next slide</span>
</Button>
);
}
);
CarouselNext.displayName = "CarouselNext";

export {
type CarouselApi,
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
};
2 changes: 1 addition & 1 deletion src/hooks/useInView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ const useInView = <T extends Element>(threshold: number, onViewEscape?: () => vo
if (entry.isIntersecting) {
setIsInView(true);
} else {
setIsInView(false);
if (onViewEscape) onViewEscape();
else setIsInView(false);
}
});
},
Expand Down
12 changes: 12 additions & 0 deletions src/pages/main/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useEffect, useState } from "react";

import Calendar from "@/components/pages/main/home/Calendar";
import useCalendarData from "@/hooks/query/useCalendarData";
import Onboarding from "../user/Onboarding";
const STORAGE_KEY = "currentDate";
/**
* 메인 화면
Expand All @@ -17,6 +18,16 @@ const Home = () => {
return storedDate ? new Date(storedDate) : new Date();
});
const { calendarData, isActiveToday } = useCalendarData(currentDate);
const [showOnboarding, setShowOnboarding] = useState(false);

useEffect(() => {
const hasVisited = localStorage.getItem("hasVisited");

if (!hasVisited) {
setShowOnboarding(true);
localStorage.setItem("hasVisited", "true");
}
}, []);

const navigate = useNavigate();
const onClickCreateDiary = () => {
Expand Down Expand Up @@ -79,6 +90,7 @@ const Home = () => {
bgColor="dark"
/>
</div>
{showOnboarding && <Onboarding onClose={() => setShowOnboarding(false)} />}
</div>
);
};
Expand Down
Loading

0 comments on commit 7ba3dd2

Please sign in to comment.