Commit d58472a2 authored by timel's avatar timel

feat: waitroom improvement

parent a1e99135
Pipeline #22911 canceled with stages
in 1 minute and 13 seconds
// 全局修改 antd 样式
.ant-btn {
display: flex;
align-items: center;
}
// 点击按钮产生的波浪扩散效果
.ant-wave {
color: hsla(0, 0%, 100%, 0.3);
}
@charset "utf-8";
@import url("https://fonts.googleapis.com/css2?family=Electrolize&display=swap");
@import "./antd.scss";
body {
color-scheme: dark;
......
.container {
// height: calc(100% - var(--header-height));
height: 100%;
display: flex;
--side-box-width: 400px;
......@@ -57,6 +56,7 @@
.inner {
background: linear-gradient(to right, blue, rgb(0, 149, 255));
}
z-index: 1;
}
&.op {
box-shadow: 5px 0 20px 0 rgba(255, 0, 81, 0.1);
......@@ -85,30 +85,8 @@
margin-left: auto;
}
.btn-large {
min-width: 100px;
height: 40px;
background: hsla(0, 0%, 100%, 0.1);
border-radius: 6px;
display: flex;
align-items: center;
padding: 0 16px 0 16px;
gap: 8px;
cursor: pointer;
transition: 0.2s;
.avatars-watch {
height: 100%;
place-items: center;
}
& > * {
transition: 0.2s;
}
&:hover > * {
opacity: 0.8;
}
&:active > * {
opacity: 0.6;
}
.avatars-watch {
padding-left: 8px;
}
.btn-sider {
......@@ -135,7 +113,7 @@
}
}
.room-name {
.status-bar {
position: absolute;
top: 40px;
left: 0;
......@@ -147,4 +125,30 @@
font-size: 13px;
line-height: 28px;
border-radius: 14px;
display: flex;
gap: 8px;
}
.fake-btn-large {
min-width: 88px;
height: 40px;
background: hsla(0, 0%, 100%, 0.1);
border-radius: 6px;
display: flex;
align-items: center;
padding: 0 16px 0 16px;
gap: 8px;
cursor: not-allowed;
transition: 0.2s;
position: relative;
& > * {
transition: 0.2s;
}
&:hover > * {
opacity: 0.8;
}
&:active > * {
opacity: 0.6;
}
overflow: hidden;
}
import { CheckCircleFilled } from "@ant-design/icons";
import { Avatar, Button, ConfigProvider, Skeleton, Space } from "antd";
import { Avatar, Button, ConfigProvider, Popover, Skeleton, Space } from "antd";
import classNames from "classnames";
import { useState } from "react";
import React, { useState } from "react";
import { type LoaderFunction, useLoaderData } from "react-router-dom";
import { useSnapshot } from "valtio";
import { accountStore } from "@/stores";
import { accountStore, type User } from "@/stores";
import { Background, IconFont, Select } from "@/ui/Shared";
import { Chat } from "./Chat";
......@@ -19,13 +19,7 @@ interface Params {
export const loader: LoaderFunction = async ({ params }) => params;
export const Component: React.FC = () => {
const _params = useLoaderData<Params>();
const { user } = useSnapshot(accountStore);
const [collapsed, setCollapsed] = useState(false);
return (
<ConfigProvider
theme={{
const theme = {
components: {
Button: {
lineWidth: 0,
......@@ -35,9 +29,18 @@ export const Component: React.FC = () => {
colorPrimaryHover: "#ccc",
colorPrimaryActive: "#aaa",
},
Popover: {
colorBgElevated: "hsla(0, 0%, 100%, 0.1)",
},
}}
>
},
};
export const Component: React.FC = () => {
const _params = useLoaderData<Params>();
const { user } = useSnapshot(accountStore);
const [collapsed, setCollapsed] = useState(false);
return (
<ConfigProvider theme={theme}>
<div
className={classNames(styles.container, {
[styles.collapsed]: collapsed,
......@@ -54,7 +57,159 @@ export const Component: React.FC = () => {
<Chat />
</div>
<div className={styles.main}>
{/* <div className={styles["room-name"]}>Timel 的房间</div> */}
{/* <div className={styles["status-bar"]}>
<LoadingOutlined />
对方正在选择先后
</div> */}
<Controller />
<div className={styles["both-side-container"]}>
<PlayerZone
isReady
who={Who.Me}
user={user}
btn={
<>
{/* <Button size="large" className={styles["btn-join"]}>
决斗准备
</Button> */}
<MoraButton />
</>
}
/>
<PlayerZone
isReady
who={Who.Op}
btn={
<Button size="large" className={styles["btn-join"]}>
加入决斗
</Button>
}
/>
</div>
</div>
</div>
</ConfigProvider>
);
};
enum Mora {
Rock = "rock",
Scissors = "scissors",
Paper = "paper",
}
const MoraButton: React.FC<{
onClick?: () => Promise<Mora>;
}> = () => {
// TODO: 实现这个onclick
// 防抖
return (
<Popover
overlayStyle={{ backdropFilter: "blur(10px)" }}
content={
<Space>
<Button
size="large"
type="text"
icon={<IconFont type="icon-hand-rock" size={16} />}
>
石头
</Button>
<Button
size="large"
type="text"
icon={<IconFont type="icon-hand-scissors" size={16} />}
>
剪刀
</Button>
<Button
size="large"
type="text"
icon={<IconFont type="icon-hand-paper" size={16} />}
>
</Button>
</Space>
}
trigger="focus"
placement="bottom"
>
<Button
size="large"
className={styles["btn-join"]}
icon={<IconFont type="icon-mora" size={20} />}
>
猜拳
</Button>
</Popover>
);
};
const OrderPopup: React.FC<React.PropsWithChildren<{ open: boolean }>> = ({
children,
open,
}) => {
return (
<Popover
overlayStyle={{ backdropFilter: "blur(10px)" }}
open={open}
content={
<Space>
<Button
size="large"
type="text"
icon={<IconFont type="icon-one" size={18} />}
>
先手
</Button>
<Button
size="large"
type="text"
icon={<IconFont type="icon-two" size={18} />}
>
后手
</Button>
</Space>
}
trigger="focus"
placement="bottom"
>
{children}
</Popover>
);
};
enum Who {
Me = "me",
Op = "op",
}
// 玩家区域: 两侧各有一个
const PlayerZone: React.FC<{
btn?: React.ReactNode; // 在内部右侧可以放一个按钮
isReady: boolean;
who?: Who;
user?: User;
}> = ({ btn, isReady, who, user }) => {
return (
<div className={classNames(styles["side-box"], who && styles[who])}>
<div className={styles.inner}></div>
<OrderPopup open={false}>
<div style={{ position: "relative" }}>
<Avatar src={user?.avatar_url} size={48} />
{isReady && <CheckCircleFilled className={styles.check} />}
</div>
</OrderPopup>
<div className={styles.name}>
{user ? user.username : <Skeleton.Input size="small" />}
</div>
{btn}
</div>
);
};
const Controller: React.FC = () => {
return (
<Space>
<Select
title="卡组"
......@@ -68,8 +223,7 @@ export const Component: React.FC = () => {
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
<div className={styles["btn-large"]}>
<IconFont type="icon-record" size={18} />
<Button size="large" icon={<IconFont type="icon-record" size={18} />}>
加入观战
<Avatar.Group className={styles["avatars-watch"]}>
<Avatar
......@@ -81,40 +235,10 @@ export const Component: React.FC = () => {
</Avatar>
<Avatar style={{ backgroundColor: "#87d068" }} size="small" />
</Avatar.Group>
</div>
<div className={styles["btn-large"]}>
<IconFont type="icon-play" size={12} />
开始游戏
</div>
</Space>
<div className={styles["both-side-container"]}>
<div className={classNames(styles["side-box"], styles.me)}>
<div className={styles.inner}></div>
<div style={{ position: "relative" }}>
<Avatar src={user?.avatar_url} size={48} />
<CheckCircleFilled className={styles.check} />
</div>
<div className={styles.name}>Timel#6675</div>
<Button size="large" className={styles["btn-join"]}>
决斗准备
</Button>
</div>
{/* <div className={styles.battle}>
<IconFont type="icon-battle" size={32} />
</div> */}
<div className={classNames(styles["side-box"], styles.op)}>
<div className={styles.inner}></div>
<Avatar size={48} />
<div className={styles.name}>
<Skeleton.Input size="small" />
</div>
<Button size="large" className={styles["btn-join"]}>
加入决斗
<Button size="large" icon={<IconFont type="icon-play" size={12} />}>
开始游戏
</Button>
</div>
</div>
</div>
</div>
</ConfigProvider>
</Space>
);
};
import { createFromIconfontCN } from "@ant-design/icons";
const _IconFont = createFromIconfontCN({
scriptUrl: ["//at.alicdn.com/t/c/font_4188978_25l7e0zb6xo.js"],
scriptUrl: ["//at.alicdn.com/t/c/font_4188978_6q59nqrc9ix.js"],
});
export const IconFont: React.FC<{
......@@ -10,5 +10,5 @@ export const IconFont: React.FC<{
color?: string;
style?: React.CSSProperties;
}> = ({ type, size = "inherit", style, color = "inherit" }) => (
<_IconFont type={type} style={{ ...style, fontSize: size, color }} />
<_IconFont type={type} style={{ fontSize: size, color, ...style }} />
);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment