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..5c010fd7 --- /dev/null +++ b/components/shared/Carousel/v2/Carousel.tsx @@ -0,0 +1,93 @@ +import { CSSProperties, FC, useEffect, useRef, useState } from "react"; +import Icon from "../../Icon/v2/Icon"; + +interface CarouselProps< + Item extends Record, + Key extends keyof Item = keyof Item, +> { + items: (Item & Record)[]; + uniqueKey: Key; + Component: FC; +} + +export default function Carousel>({ + items, + uniqueKey, + Component, +}: 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; + + 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"; + + useEffect(() => { + setMaxWidth(carouselRef.current?.clientWidth || 0); + }, []); + + return ( +
+ +
+
    + {Array.isArray(items) && + items.map((item) => ( +
  • + +
  • + ))} +
+
+ +
+ ); +} 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; 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/pages/index.tsx b/pages/index.tsx index 861d5d89..1e6598f7 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,19 +1,104 @@ 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 Carousel, { mockCarouselItems } from "@/components/shared/Carousel"; +import Button from "@/components/shared/Button"; +import CreateRoomModal from "@/components/lobby/CreateRoomModal"; +import { 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, +}: Readonly<(typeof mockCarouselItems)[number]>) { + return ( +
+
+ {imgAlt} +
+
+
Massive Monster
+
AZUL ({imgAlt})
+
+
4.6 * * * * * (14)
+ +
+
+
    +
  • + {imgAlt} +
  • +
  • + {imgAlt} +
  • +
  • + {imgAlt} +
  • +
  • + {imgAlt} +
  • +
+
+
+ 《AZUL》是強手棋類休閒遊戲,在遊戲中,你可以任意選擇比賽地圖、參賽角色和組隊方式,使用卡片等方式賺取金錢,最終取得比賽勝利。 +
+
+
    +
  • + 回合制 +
  • +
  • + 第三人稱 +
  • +
  • + 策略型 +
  • +
  • + 玩家對戰 +
  • +
  • + 輕鬆休閒 +
  • +
+
+
+
+ ); +} + export default function Home() { const { t } = useTranslation("rooms"); return ( -
-
+
+
@@ -22,20 +107,20 @@ export default function Home() { } />
-
- +
- - - +
+ + + +
); } 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 }) => {