From 4a7ad9bb5b95824acbabfebd7197993a298f5eb0 Mon Sep 17 00:00:00 2001 From: Johnson Mao Date: Sun, 28 Jul 2024 15:48:07 +0800 Subject: [PATCH 1/4] feat: add carousel v2 component --- .../shared/Carousel/v2/Carousel.stories.tsx | 47 ++++++++++++ components/shared/Carousel/v2/Carousel.tsx | 74 +++++++++++++++++++ components/shared/Carousel/v2/index.tsx | 5 ++ 3 files changed, 126 insertions(+) create mode 100644 components/shared/Carousel/v2/Carousel.stories.tsx create mode 100644 components/shared/Carousel/v2/Carousel.tsx create mode 100644 components/shared/Carousel/v2/index.tsx diff --git a/components/shared/Carousel/v2/Carousel.stories.tsx b/components/shared/Carousel/v2/Carousel.stories.tsx new file mode 100644 index 00000000..ec6d9e42 --- /dev/null +++ b/components/shared/Carousel/v2/Carousel.stories.tsx @@ -0,0 +1,47 @@ +import type { Meta, StoryObj } from "@storybook/react"; +import Carousel from "./"; + +const Card = (props: any) => ( +
+ {props.name} +
+); + +const meta: Meta = { + title: "Data Display/CarouselV2", + component: Carousel, + tags: ["autodocs"], + decorators: [ + (Story, ctx) => { + return ( +
+ +
+ ); + }, + ], + args: { + uniqueKey: "name", + items: [{ name: "TEST 1" }, { name: "TEST 2" }, { name: "TEST 3" }], + Component: Card, + }, + argTypes: { + Component: { + options: ["Card"], + defaultValue: Card, + mapping: { + Card, + }, + }, + }, +}; + +export default meta; + +type Story = StoryObj; + +export const Playground: Story = { + render: (args) => , +}; + +Playground.args = {}; diff --git a/components/shared/Carousel/v2/Carousel.tsx b/components/shared/Carousel/v2/Carousel.tsx new file mode 100644 index 00000000..aaed1570 --- /dev/null +++ b/components/shared/Carousel/v2/Carousel.tsx @@ -0,0 +1,74 @@ +import { FC, useState } from "react"; +import Icon from "../../Icon/v2/Icon"; +import { cn } from "@/lib/utils"; + +interface CarouselProps< + T extends Record, + K extends keyof T = keyof T, +> { + items: (T & Record)[]; + uniqueKey: K; + Component: FC; +} + +export default function Carousel>({ + items, + uniqueKey, + Component, +}: Readonly>) { + const [showIndex, setShowIndex] = useState(0); + + const handleChangePage = (action: "prev" | "next") => () => { + const maxIndex = items.length - 1; + + switch (action) { + case "prev": + setShowIndex((preIndex) => { + const newIndex = preIndex - 1; + return newIndex > -1 ? newIndex : maxIndex; + }); + break; + case "next": + setShowIndex((preIndex) => { + const newIndex = preIndex + 1; + return newIndex <= maxIndex ? newIndex : 0; + }); + break; + default: + } + }; + + const buttonClassName = + "p-2.5 shrink-0 bg-white/4 shadow-default rounded-2xl"; + const buttonIconClassName = "stroke-white w-6 h-6 pointer-events-none"; + + return ( +
+ +
    + {Array.isArray(items) && + items.map((item, index) => ( +
  • + +
  • + ))} +
+ +
+ ); +} diff --git a/components/shared/Carousel/v2/index.tsx b/components/shared/Carousel/v2/index.tsx new file mode 100644 index 00000000..4fe36731 --- /dev/null +++ b/components/shared/Carousel/v2/index.tsx @@ -0,0 +1,5 @@ +import Carousel from "./Carousel"; + +export * from "./Carousel"; + +export default Carousel; From 3d544df48024a0b62947215f1aeb180f47b9f467 Mon Sep 17 00:00:00 2001 From: Johnson Mao Date: Sun, 28 Jul 2024 15:49:03 +0800 Subject: [PATCH 2/4] chore: update tailwind shadow and style --- components/shared/Chat/v2/Chat.tsx | 2 +- components/shared/Icon/v2/icons/index.ts | 4 ++-- tailwind.config.js | 12 +++++++++--- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/components/shared/Chat/v2/Chat.tsx b/components/shared/Chat/v2/Chat.tsx index 5f05911f..43f1b329 100644 --- a/components/shared/Chat/v2/Chat.tsx +++ b/components/shared/Chat/v2/Chat.tsx @@ -20,7 +20,7 @@ export default function Chat({ lobbyMessages, friendList, roomMessages, - maxHeight = "calc(100vh - 10rem)", + maxHeight = "calc(100vh - 168px)", }: Readonly) { const [messages, setMessages] = useState(lobbyMessages); const [target, setTarget] = useState<[ChatTab["id"], string | null]>([ diff --git a/components/shared/Icon/v2/icons/index.ts b/components/shared/Icon/v2/icons/index.ts index 2cf55179..1f8f3d46 100644 --- a/components/shared/Icon/v2/icons/index.ts +++ b/components/shared/Icon/v2/icons/index.ts @@ -27,7 +27,7 @@ import leadingIcon from "./svgs/leading-icon.svg"; import linkedin from "./svgs/linkedin.svg"; import logOut from "./svgs/log-out.svg"; import longArrowUpLeft from "./svgs/long-arrow-up-left.svg"; -import navAarrowLeft from "./svgs/nav-arrow-left.svg"; +import navArrowLeft from "./svgs/nav-arrow-left.svg"; import navArrowRight from "./svgs/nav-arrow-right.svg"; import nonpublic from "./svgs/nonpublic.svg"; import notificationDefault from "./svgs/notification-default.svg"; @@ -73,7 +73,7 @@ const icons = { linkedin, logOut, longArrowUpLeft, - navAarrowLeft, + navArrowLeft, navArrowRight, nonpublic, notificationDefault, diff --git a/tailwind.config.js b/tailwind.config.js index 6af164fd..91012eab 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -8,10 +8,10 @@ module.exports = { "./containers/**/*.{js,ts,jsx,tsx}", ], theme: { - fontFamily: { - body: ['"Noto Sans TC"', "Roboto"], - }, extend: { + fontFamily: { + body: ['"Noto Sans TC"', "Roboto"], + }, width: { 18: "4.5rem" /** 72px */, }, @@ -139,6 +139,12 @@ module.exports = { 0 -1px 1px rgba(255, 255, 255, 0.1) `, }, + ".shadow-default": { + "box-shadow": ` + inset 2px 2px 4px rgba(135, 135, 135, 0.1), + 1px 2px 4px rgba(0, 0, 0, 0.1) + `, + }, }); }), plugin(({ addUtilities }) => { From 7174602afa3bc462c4fda106b88bc6d1c26fdf14 Mon Sep 17 00:00:00 2001 From: Johnson Mao Date: Sun, 28 Jul 2024 15:49:54 +0800 Subject: [PATCH 3/4] feat: add carousel v2 to home page --- pages/index.tsx | 72 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/pages/index.tsx b/pages/index.tsx index 861d5d89..722e70fb 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,19 +1,74 @@ import { GetStaticProps } from "next"; -import Button from "@/components/shared/Button"; -import CreateRoomModal from "@/components/lobby/CreateRoomModal"; +import Image from "next/image"; import Link from "next/link"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { useTranslation } from "next-i18next"; +import Button from "@/components/shared/Button"; +import CreateRoomModal from "@/components/lobby/CreateRoomModal"; import Carousel, { mockCarouselItems } from "@/components/shared/Carousel"; +import CarouselV2 from "@/components/shared/Carousel/v2"; import FastJoinButton from "@/components/lobby/FastJoinButton"; import SearchBar from "@/components/shared/SearchBar"; +function CarouselCard({ imgUrl, imgAlt }: (typeof mockCarouselItems)[number]) { + return ( +
+
+ {imgAlt +
+
+
Massive Monster
+
AZUL ({imgAlt})
+
+
4.6 * * * * * (14)
+ +
+
+
    +
  • img1
  • +
  • img2
  • +
  • img3
  • +
+
+
+ 《AZUL》是強手棋類休閒遊戲,在遊戲中,你可以任意選擇比賽地圖、參賽角色和組隊方式,使用卡片等方式賺取金錢,最終取得比賽勝利。 +
+
+
    +
  • + 回合制 +
  • +
  • + 第三人稱 +
  • +
  • + 策略型 +
  • +
  • + 玩家對戰 +
  • +
  • + 輕鬆休閒 +
  • +
+
+
+
+ ); +} + export default function Home() { const { t } = useTranslation("rooms"); return ( -
-
+
+
@@ -22,7 +77,14 @@ export default function Home() { } />
-
+
+ +
+
Date: Sun, 4 Aug 2024 16:42:37 +0800 Subject: [PATCH 4/4] feat: carousel v2 transition animation --- components/shared/Carousel/v2/Carousel.tsx | 59 +++++++++----- pages/index.tsx | 95 ++++++++++++++-------- 2 files changed, 98 insertions(+), 56 deletions(-) diff --git a/components/shared/Carousel/v2/Carousel.tsx b/components/shared/Carousel/v2/Carousel.tsx index aaed1570..5c010fd7 100644 --- a/components/shared/Carousel/v2/Carousel.tsx +++ b/components/shared/Carousel/v2/Carousel.tsx @@ -1,22 +1,23 @@ -import { FC, useState } from "react"; +import { CSSProperties, FC, useEffect, useRef, useState } from "react"; import Icon from "../../Icon/v2/Icon"; -import { cn } from "@/lib/utils"; interface CarouselProps< - T extends Record, - K extends keyof T = keyof T, + Item extends Record, + Key extends keyof Item = keyof Item, > { - items: (T & Record)[]; - uniqueKey: K; - Component: FC; + items: (Item & Record)[]; + uniqueKey: Key; + Component: FC; } -export default function Carousel>({ +export default function Carousel>({ items, uniqueKey, Component, -}: Readonly>) { +}: Readonly>) { const [showIndex, setShowIndex] = useState(0); + const [maxWidth, setMaxWidth] = useState(0); + const carouselRef = useRef(null); const handleChangePage = (action: "prev" | "next") => () => { const maxIndex = items.length - 1; @@ -42,6 +43,10 @@ export default function Carousel>({ "p-2.5 shrink-0 bg-white/4 shadow-default rounded-2xl"; const buttonIconClassName = "stroke-white w-6 h-6 pointer-events-none"; + useEffect(() => { + setMaxWidth(carouselRef.current?.clientWidth || 0); + }, []); + return (
-
    - {Array.isArray(items) && - items.map((item, index) => ( -
  • - -
  • - ))} -
+
+
    + {Array.isArray(items) && + items.map((item) => ( +
  • + +
  • + ))} +
+
+
- - -
); }