Commit 37a4772e authored by timel's avatar timel

feat: page of waiting room

parent 23ca402b
Pipeline #22894 failed with stages
in 21 minutes and 47 seconds
......@@ -8,12 +8,12 @@ import {
UndoOutlined,
} from "@ant-design/icons";
import {
Badge,
Button,
ConfigProvider,
Input,
Space,
type ThemeConfig,
Badge,
} from "antd";
import { Background } from "../Shared";
......
......@@ -31,6 +31,7 @@ export const Component = () => {
<NavLink to="/">主页</NavLink>
<NavLink to="/match">匹配</NavLink>
<NavLink to="/build">组卡</NavLink>
<NavLink to="/waitroom">waitroom(temp)</NavLink>
<span style={{ flexGrow: 1 }} />
<span className={styles.profile}>
<NeosAvatar />
......
......@@ -34,7 +34,7 @@ const _router = createBrowserRouter([
lazy: () => import("./NewProfile"),
},
{
path: "/waitroom/:ip/:player/:passWd",
path: "/waitroom/:ip?/:player?/:passWd?",
lazy: () => import("./NewWaitRoom"),
},
{
......
......@@ -14,30 +14,6 @@
}
}
// 奇奇怪怪的bug
:global(.ant-select .ant-select-selection-item) {
flex: initial;
}
.custom-select {
display: flex;
align-items: center;
.prefix {
height: 40px;
line-height: 40px;
padding: 0 1rem;
border-radius: 6px;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: hsla(0, 0%, 100%, 0.1);
}
.select :global(.ant-select-selector) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
.mode-select {
display: grid;
grid-template-columns: repeat(3, 1fr);
......
import { EditFilled, SettingFilled } from "@ant-design/icons";
import { ConfigProvider, Select, Space } from "antd";
import classNames from "classnames";
import { Space } from "antd";
import { type LoaderFunction } from "react-router-dom";
import { CookieKeys, setCookie } from "@/api";
import { accountStore, type User } from "@/stores";
import { Background, IconFont } from "@/ui/Shared";
import { Background, IconFont, Select } from "@/ui/Shared";
import styles from "./index.module.scss";
......@@ -27,7 +26,7 @@ export const Component: React.FC = () => {
<main className={styles.main}>
<div className={styles.container}>
<Space size={16}>
<CustomSelect
<Select
title="服务器"
showSearch
defaultValue="lucy"
......@@ -39,7 +38,7 @@ export const Component: React.FC = () => {
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
<CustomSelect
<Select
title="卡组"
showSearch
defaultValue="lucy"
......@@ -108,35 +107,3 @@ const Mode: React.FC<{
function getSSOUser(searchParams: URLSearchParams): User {
return Object.fromEntries(searchParams) as unknown as User;
}
const CustomSelect: React.FC<
React.ComponentProps<typeof Select> & { title: string }
> = ({ title, className, dropdownStyle, ...rest }) => (
<ConfigProvider
theme={{
components: {
Select: {
colorBgElevated: "hsla(0, 0%, 100%, 0.05)",
controlItemBgActive: "#79797955",
colorBorder: "transparent",
colorBgContainer: "hsla(0, 0%, 100%, 0.05)",
colorPrimaryHover: "#3400d1",
lineWidth: 0,
},
},
}}
>
<div className={styles["custom-select"]}>
<span className={styles.prefix}>{title}</span>
<Select
className={classNames(styles.select, className)}
size="large"
dropdownStyle={{
backdropFilter: "blur(20px)",
...dropdownStyle,
}}
{...rest}
/>
</div>
</ConfigProvider>
);
@use "/src/styles/utils.scss";
.chat {
height: 100%;
max-height: 100%;
display: flex;
flex-direction: column;
}
.input {
width: 100%;
margin-top: auto;
background-color: hsla(0, 0%, 100%, 0.07);
padding: 8px;
display: flex;
}
.dialogs {
@include utils.scrollbar;
.item {
vertical-align: baseline;
// font-size: 15px;
.name {
font-weight: 500;
line-height: 1.375rem;
}
.time {
font-size: 11px;
opacity: 0.8;
margin-left: 8px;
}
.content {
font-size: 14px;
color: #ccc;
}
padding: 8px 8px;
margin: 0 8px;
border-radius: 8px;
transition: 0.2s;
&:hover {
background-color: hsla(0, 0%, 100%, 0.07);
}
}
}
import styles from "./Chat.module.scss";
import { Avatar, Button, ConfigProvider, Skeleton, Space, Input } from "antd";
import { SendOutlined } from "@ant-design/icons";
import { IconFont } from "../Shared";
export const Chat: React.FC = () => {
return (
<div className={styles.chat}>
<div className={styles.dialogs}>
<DialogItem
name="愧心Kanzo"
time="00:34:28"
content="白银城连抽了三张⏰"
/>
<DialogItem
name="愧心Kanzo"
time="00:34:28"
content="白银城连抽了三张⏰"
/>
</div>
<div className={styles.input}>
<Input.TextArea
bordered={false}
autoSize
placeholder="请输入聊天内容"
/>
<Button type="text" icon={<IconFont type="icon-send" size={16} />} />
</div>
</div>
);
};
const DialogItem: React.FC<{
name: string;
time: string;
content: string;
}> = ({ name, time, content }) => {
return (
<div className={styles.item}>
<div className={styles.name}>
{name}
<span className={styles.time}>{time}</span>
</div>
<div className={styles.content}>{content}</div>
</div>
);
};
.container {
height: calc(100% - var(--header-height));
display: flex;
--side-box-width: 400px;
--battle-icon-width: 50px;
--gap-between-side-and-battle: 30px;
--border-radius: 10px;
--sider-width: 300px;
.main {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 5%;
transition: transform 0.3s;
}
.sider {
position: relative;
width: var(--sider-width);
flex: 0 0 300px;
height: 100%;
background-color: hsla(0, 0%, 100%, 0.05);
transition: transform 0.3s;
}
}
.both-side-container {
display: flex;
gap: var(--gap-between-side-and-battle);
align-items: center;
margin-top: 80px;
.battle {
height: var(--battle-icon-width);
width: var(--battle-icon-width);
border-radius: 50%;
background: hsla(0, 0%, 100%, 0.1);
backdrop-filter: blur(10px);
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
}
.side-box {
height: 120px;
width: var(--side-box-width);
border-radius: var(--border-radius);
background: hsla(0, 0%, 100%, 0.1);
backdrop-filter: blur(10px);
overflow: hidden;
padding: 0 32px;
display: flex;
align-items: center;
gap: 16px;
.inner {
position: absolute;
left: 0;
width: 250px;
height: 100px;
filter: blur(120px);
}
&.me {
box-shadow: -5px 0 20px 0 rgba(0, 115, 255, 0.15);
.inner {
background: linear-gradient(to right, blue, rgb(0, 149, 255));
}
}
&.op {
box-shadow: 5px 0 20px 0 rgba(255, 0, 81, 0.1);
.inner {
background: linear-gradient(to right, rgb(255, 0, 106), rgb(255, 0, 0));
}
}
.name {
font-size: 14px;
color: white;
z-index: 1;
}
}
}
.check {
position: absolute;
bottom: 0;
right: 0;
z-index: 99;
color: greenyellow;
box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.5);
}
.btn-join {
margin-left: auto;
}
.watch {
min-width: 100px;
height: 40px;
background: hsla(0, 0%, 100%, 0.1);
border-radius: 6px;
display: flex;
align-items: center;
padding-left: 16px;
gap: 12px;
cursor: pointer;
transition: 0.2s;
.btn-watch {
display: flex;
align-items: center;
gap: 8px;
}
.avatars-watch {
height: 100%;
padding: 0 8px;
place-items: center;
}
& > * {
transition: 0.2s;
}
&:hover > * {
opacity: 0.8;
}
&:active > * {
opacity: 0.6;
}
}
.btn-sider {
--length: 30px;
width: var(--length);
height: var(--length);
background: hsla(0, 0%, 100%, 0.1);
position: absolute;
right: calc(-1 * var(--length) - 10px);
bottom: 10px;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
transition: 0.2s;
}
.collapsed {
.sider {
transform: translateX(calc(-1 * var(--sider-width)));
}
.main {
transform: translateX(calc(-1 * var(--sider-width) / 2));
}
}
import { CheckCircleFilled } from "@ant-design/icons";
import { Avatar, Button, ConfigProvider, Skeleton, Space, Tooltip } from "antd";
import classNames from "classnames";
import { type LoaderFunction, useLoaderData } from "react-router-dom";
import { useSnapshot } from "valtio";
import { accountStore } from "@/stores";
import { Background, IconFont, Select } from "@/ui/Shared";
import { Chat } from "./Chat";
import styles from "./index.module.scss";
import { useState } from "react";
interface Params {
player?: string;
......@@ -9,6 +20,98 @@ interface Params {
export const loader: LoaderFunction = async ({ params }) => params;
export const Component: React.FC = () => {
const params = useLoaderData<Params>();
return <>{JSON.stringify(params)}</>;
const _params = useLoaderData<Params>();
const { user } = useSnapshot(accountStore);
const [collapsed, setCollapsed] = useState(false);
return (
<ConfigProvider
theme={{
components: {
Button: {
lineWidth: 0,
fontSizeLG: 14,
fontSize: 12,
colorBgContainer: "hsla(0, 0%, 100%, 0.05)",
colorPrimaryHover: "#ccc",
colorPrimaryActive: "#aaa",
},
},
}}
>
<div
className={classNames(styles.container, {
[styles.collapsed]: collapsed,
})}
>
<Background />
<div className={styles.sider}>
<Button
className={styles["btn-sider"]}
icon={<IconFont type="icon-side-bar-fill" size={16} />}
onClick={() => setCollapsed(!collapsed)}
/>
<Chat />
</div>
<div className={styles.main}>
<Space>
<Select
title="卡组"
showSearch
defaultValue="lucy"
style={{ width: 300 }}
options={[
{ value: "jack", label: "Jack" },
{ value: "lucy", label: "Lucy" },
{ value: "Yiminghe", label: "yiminghe" },
{ value: "disabled", label: "Disabled", disabled: true },
]}
/>
<div className={styles.watch}>
<span className={styles["btn-watch"]}>
<IconFont type="icon-record" size={18} />
加入观战
</span>
<Avatar.Group className={styles["avatars-watch"]}>
<Avatar
src="https://xsgames.co/randomusers/avatar.php?g=pixel&key=1"
size="small"
/>
<Avatar style={{ backgroundColor: "#f56a00" }} size="small">
K
</Avatar>
<Avatar style={{ backgroundColor: "#87d068" }} size="small" />
</Avatar.Group>
</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>
</div>
</div>
</div>
</div>
</ConfigProvider>
);
};
import { createFromIconfontCN } from "@ant-design/icons";
const _IconFont = createFromIconfontCN({
scriptUrl: ["//at.alicdn.com/t/c/font_4188978_evr8efp70uk.js"],
scriptUrl: ["//at.alicdn.com/t/c/font_4188978_x6rqg2kz3fa.js"],
});
export const IconFont: React.FC<{
type: string;
size?: number;
color?: string;
style?: React.CSSProperties;
}> = ({ type, size = 14, style }) => (
<_IconFont type={type} style={{ ...style, fontSize: size }} />
}> = ({ type, size = "inherit", style, color = "inherit" }) => (
<_IconFont type={type} style={{ ...style, fontSize: size, color }} />
);
.custom-select {
display: flex;
align-items: center;
.prefix {
height: 40px;
line-height: 40px;
padding: 0 1rem;
border-radius: 6px;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
background-color: hsla(0, 0%, 100%, 0.1);
}
.select :global(.ant-select-selector) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
import { ConfigProvider, Select as AntdSelect, Space } from "antd";
import classNames from "classnames";
import styles from "./index.module.scss";
export const Select: React.FC<
React.ComponentProps<typeof AntdSelect> & { title: string }
> = ({ title, className, dropdownStyle, ...rest }) => (
<ConfigProvider
theme={{
components: {
Select: {
colorBgElevated: "hsla(0, 0%, 100%, 0.05)",
controlItemBgActive: "#79797955",
colorBorder: "transparent",
colorBgContainer: "hsla(0, 0%, 100%, 0.05)",
colorPrimaryHover: "#3400d1",
lineWidth: 0,
},
},
}}
>
<div className={styles["custom-select"]}>
<span className={styles.prefix}>{title}</span>
<AntdSelect
className={classNames(styles.select, className)}
size="large"
dropdownStyle={{
backdropFilter: "blur(20px)",
...dropdownStyle,
}}
{...rest}
/>
</div>
</ConfigProvider>
);
export * from "./Background";
export * from "./css";
export * from "./IconFont";
export * from "./Select";
export * from "./YgoCard";
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