Commit b59fdac1 authored by Chunchi Che's avatar Chunchi Che Committed by BBeretta

optimize some implementation, including paths of components and so on

parent e811fd11
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
const LanguageSelector: React.FC = () => {
const { i18n } = useTranslation();
const onClickLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const language = e.target.value;
i18n.changeLanguage(language);
};
useEffect(() => {
//Adding language state as a dependency to force re-render when the language changes
}, [i18n.language]);
return (
<select value={i18n.language} onChange={onClickLanguageChange}>
<option value="cn">Chinese</option>
<option value="en">English</option>
<option value="fr">French</option>
<option value="jp">Japanese</option>
<option value="br">Brazilian</option>
<option value="pt">Portuguese</option>
<option value="es">Spanish</option>
</select>
);
};
export default LanguageSelector;
...@@ -18,7 +18,6 @@ import "u-reset.css"; ...@@ -18,7 +18,6 @@ import "u-reset.css";
import "overlayscrollbars/overlayscrollbars.css"; import "overlayscrollbars/overlayscrollbars.css";
import "@/styles/core.scss"; import "@/styles/core.scss";
import "@/styles/inject.scss"; import "@/styles/inject.scss";
import "./i18n";
import { ProConfigProvider } from "@ant-design/pro-provider"; import { ProConfigProvider } from "@ant-design/pro-provider";
import { App, ConfigProvider } from "antd"; import { App, ConfigProvider } from "antd";
...@@ -28,7 +27,7 @@ import ReactDOM from "react-dom/client"; ...@@ -28,7 +27,7 @@ import ReactDOM from "react-dom/client";
import { theme } from "@/ui/theme"; import { theme } from "@/ui/theme";
import { LanguageProvider } from "./Language/LanguageContext"; import { I18NProvider } from "./ui/I18N";
import { NeosRouter } from "./ui/NeosRouter"; import { NeosRouter } from "./ui/NeosRouter";
const root = ReactDOM.createRoot( const root = ReactDOM.createRoot(
...@@ -36,7 +35,7 @@ const root = ReactDOM.createRoot( ...@@ -36,7 +35,7 @@ const root = ReactDOM.createRoot(
); );
root.render( root.render(
<LanguageProvider> <I18NProvider>
<ConfigProvider theme={theme} locale={zhCN}> <ConfigProvider theme={theme} locale={zhCN}>
<App> <App>
<ProConfigProvider dark> <ProConfigProvider dark>
...@@ -44,5 +43,5 @@ root.render( ...@@ -44,5 +43,5 @@ root.render(
</ProConfigProvider> </ProConfigProvider>
</App> </App>
</ConfigProvider> </ConfigProvider>
</LanguageProvider>, </I18NProvider>,
); );
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
import { easings } from "@react-spring/web"; import { easings } from "@react-spring/web";
import { isMe } from "@/stores"; import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { AttackFunc } from "./types"; import type { AttackFunc } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { isMe } from "@/stores"; import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types"; import type { MoveFunc } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
......
...@@ -2,8 +2,8 @@ import { easings } from "@react-spring/web"; ...@@ -2,8 +2,8 @@ import { easings } from "@react-spring/web";
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { isMe } from "@/stores"; import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types"; import type { MoveFunc } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { cardStore, isMe } from "@/stores"; import { cardStore, isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types"; import type { MoveFunc } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { isMe } from "@/stores"; import { isMe } from "@/stores";
import { matConfig } from "@/ui/Shared";
import { matConfig } from "../../css";
import type { MoveFunc } from "./types"; import type { MoveFunc } from "./types";
import { asyncStart } from "./utils"; import { asyncStart } from "./utils";
......
import React, { createContext, useContext, useState } from "react"; import React, { createContext, useContext, useState } from "react";
interface LanguageContextType { interface I18NContextType {
language: string; language: string;
changeLanguage: (newLanguage: string) => void; changeLanguage: (newLanguage: string) => void;
} }
const LanguageContext = createContext<LanguageContextType | undefined>( const I18NContext = createContext<I18NContextType | undefined>(undefined);
undefined,
);
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ export const I18NProvider: React.FC<{ children: React.ReactNode }> = ({
children, children,
}) => { }) => {
const [language, setLanguage] = useState<string>("cn"); // default language const [language, setLanguage] = useState<string>("cn"); // default language
...@@ -19,16 +17,16 @@ export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({ ...@@ -19,16 +17,16 @@ export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({
}; };
return ( return (
<LanguageContext.Provider value={{ language, changeLanguage }}> <I18NContext.Provider value={{ language, changeLanguage }}>
{children} {children}
</LanguageContext.Provider> </I18NContext.Provider>
); );
}; };
export const useLanguage = (): LanguageContextType => { export const useI18N = (): I18NContextType => {
const context = useContext(LanguageContext); const context = useContext(I18NContext);
if (!context) { if (!context) {
throw new Error("useLanguage must be used within a LanguageProvider"); throw new Error("useI18N must be used within a I18NProvider");
} }
return context; return context;
}; };
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { Select } from "@/ui/Shared";
export const I18NSelector: React.FC = () => {
const { i18n } = useTranslation();
const onClickLanguageChange = (language: any) => {
i18n.changeLanguage(language);
};
useEffect(() => {
// Adding language state as a dependency to force re-render
// when the language changes
}, [i18n.language]);
return (
<Select
value={i18n.language}
onChange={onClickLanguageChange}
options={[
{ value: "cn", label: "简体中文" },
{ value: "en", label: "English" },
{ value: "fr", label: "Français" },
{ value: "jp", label: "日本語" },
{ value: "br", label: "português do Brasil" },
{ value: "pt", label: "português" },
{ value: "es", label: "Castellano" },
]}
/>
);
};
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "Match", "Match": "Match",
"DeckBuilding": "Montagem de baralho", "DeckBuilding": "Montagem de baralho",
"PersonalCenter": "Centro pessoal", "PersonalCenter": "Centro pessoal",
"MengkaCommunity": "Comunidade Mengka", "MyCardCommunity": "Comunidade Mengka",
"DuelDatabase": "Base de dados de duelos", "DuelDatabase": "Base de dados de duelos",
"LogOut": "Sair", "LogOut": "Sair",
"LoginToMengka": "Entrar no Mengka", "Login": "Entrar no Mengka",
"Fullscreen": "Tela cheia" "Fullscreen": "Tela cheia"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "匹配", "Match": "匹配",
"DeckBuilding": "组卡", "DeckBuilding": "组卡",
"PersonalCenter": "个人中心", "PersonalCenter": "个人中心",
"MengkaCommunity": "萌卡社区", "MyCardCommunity": "萌卡社区",
"DuelDatabase": "决斗数据库", "DuelDatabase": "决斗数据库",
"LogOut": "退出登录", "LogOut": "退出登录",
"LoginToMengka": "登录萌卡", "Login": "登录萌卡",
"Fullscreen": "全屏" "Fullscreen": "全屏"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "Match", "Match": "Match",
"DeckBuilding": "Deck Building", "DeckBuilding": "Deck Building",
"PersonalCenter": "Personal Center", "PersonalCenter": "Personal Center",
"MengkaCommunity": "Mengka Community", "MyCardCommunity": "Mengka Community",
"DuelDatabase": "Duel Database", "DuelDatabase": "Duel Database",
"LogOut": "Log out", "LogOut": "Log out",
"LoginToMengka": "Login to Mengka", "Login": "Login to Mengka",
"Fullscreen": "Fullscreen" "Fullscreen": "Fullscreen"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "Correspondance", "Match": "Correspondance",
"DeckBuilding": "Construction de Deck", "DeckBuilding": "Construction de Deck",
"PersonalCenter": "Centre personnel", "PersonalCenter": "Centre personnel",
"MengkaCommunity": "Communauté Mengka", "MyCardCommunity": "Communauté Mengka",
"DuelDatabase": "Base de données de duels", "DuelDatabase": "Base de données de duels",
"LogOut": "Déconnexion", "LogOut": "Déconnexion",
"LoginToMengka": "Connexion à Mengka", "Login": "Connexion à Mengka",
"Fullscreen": "Plein écran" "Fullscreen": "Plein écran"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "マッチ", "Match": "マッチ",
"DeckBuilding": "デッキ構築", "DeckBuilding": "デッキ構築",
"PersonalCenter": "個人センター", "PersonalCenter": "個人センター",
"MengkaCommunity": "萌卡コミュニティ", "MyCardCommunity": "萌卡コミュニティ",
"DuelDatabase": "デュエルデータベース", "DuelDatabase": "デュエルデータベース",
"LogOut": "ログアウト", "LogOut": "ログアウト",
"LoginToMengka": "萌卡にログインする", "Login": "萌卡にログインする",
"Fullscreen": "フルスクリーン" "Fullscreen": "フルスクリーン"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "Match", "Match": "Match",
"DeckBuilding": "Montagem de baralho", "DeckBuilding": "Montagem de baralho",
"PersonalCenter": "Centro pessoal", "PersonalCenter": "Centro pessoal",
"MengkaCommunity": "Comunidade Mengka", "MyCardCommunity": "Comunidade Mengka",
"DuelDatabase": "Base de dados de duelos", "DuelDatabase": "Base de dados de duelos",
"LogOut": "Terminar sessão", "LogOut": "Terminar sessão",
"LoginToMengka": "Iniciar sessão no Mengka", "Login": "Iniciar sessão no Mengka",
"Fullscreen": "Ecrã completo" "Fullscreen": "Ecrã completo"
}, },
"Start": { "Start": {
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
"Match": "Emparejamiento", "Match": "Emparejamiento",
"DeckBuilding": "Construcción de Mazo", "DeckBuilding": "Construcción de Mazo",
"PersonalCenter": "Centro personal", "PersonalCenter": "Centro personal",
"MengkaCommunity": "Comunidad Mengka", "MyCardCommunity": "Comunidad Mengka",
"DuelDatabase": "Base de datos de duelos", "DuelDatabase": "Base de datos de duelos",
"LogOut": "Cerrar sesión", "LogOut": "Cerrar sesión",
"LoginToMengka": "Iniciar sesión en Mengka", "Login": "Iniciar sesión en Mengka",
"Fullscreen": "Pantalla completa" "Fullscreen": "Pantalla completa"
}, },
"Start": { "Start": {
......
export * from "./I18NContext";
export * from "./I18NSelector";
import i18next from "i18next"; import i18next from "i18next";
import { initReactI18next } from "react-i18next"; import { initReactI18next } from "react-i18next";
import translationBrazilian from "./Translation/Brazilian/translation.json"; /* Import all translation files */
import translationChinese from "./Translation/Chinese/translation.json"; import translationBrazilian from "./Source/Brazilian/translation.json";
//Import all translation files import translationChinese from "./Source/Chinese/translation.json";
import translationEnglish from "./Translation/English/translation.json"; import translationEnglish from "./Source/English/translation.json";
import translationFrench from "./Translation/French/translation.json"; import translationFrench from "./Source/French/translation.json";
import translationJapanese from "./Translation/Japanese/translation.json"; import translationJapanese from "./Source/Japanese/translation.json";
import translationPortuguese from "./Translation/Portuguese/translation.json"; import translationPortuguese from "./Source/Portuguese/translation.json";
import translationSpanish from "./Translation/Spanish/translation.json"; import translationSpanish from "./Source/Spanish/translation.json";
//---Using translation
// const resources = {
// en: {
// translation: translationEnglish,
// },
// es: {
// translation: translationSpanish,
// },
// fr: {
// translation: translationFrench,
// },
// }
//---Using different namespaces
const resources = { const resources = {
cn: { cn: {
Header: translationChinese.Header, Header: translationChinese.Header,
......
...@@ -17,9 +17,9 @@ import { ...@@ -17,9 +17,9 @@ import {
removeCookie, removeCookie,
} from "@/api"; } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import LanguageSelector from "@/Language/LanguageSelector";
import { accountStore } from "@/stores"; import { accountStore } from "@/stores";
import { I18NSelector } from "../I18N";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
import { import {
getLoginStatus, getLoginStatus,
...@@ -60,7 +60,7 @@ const HeaderBtn: React.FC< ...@@ -60,7 +60,7 @@ const HeaderBtn: React.FC<
}; };
export const Component = () => { export const Component = () => {
const { t } = useTranslation("Header"); const { t: i18n } = useTranslation("Header");
// 捕获SSO登录 // 捕获SSO登录
const routerLocation = useLocation(); const routerLocation = useLocation();
...@@ -99,15 +99,15 @@ export const Component = () => { ...@@ -99,15 +99,15 @@ export const Component = () => {
/> />
</a> </a>
<HeaderBtn to="/">{t("HomePage")}</HeaderBtn> <HeaderBtn to="/">{i18n("HomePage")}</HeaderBtn>
<HeaderBtn to="/match" disabled={!logined}> <HeaderBtn to="/match" disabled={!logined}>
{t("Match")} {i18n("Match")}
</HeaderBtn> </HeaderBtn>
<HeaderBtn to="/build" disabled={!logined}> <HeaderBtn to="/build" disabled={!logined}>
{t("DeckBuilding")} {i18n("DeckBuilding")}
</HeaderBtn> </HeaderBtn>
<span style={{ flexGrow: 1 }} /> <span style={{ flexGrow: 1 }} />
<LanguageSelector /> <I18NSelector />
<span className={styles.profile}> <span className={styles.profile}>
<Dropdown <Dropdown
arrow arrow
...@@ -116,14 +116,14 @@ export const Component = () => { ...@@ -116,14 +116,14 @@ export const Component = () => {
{ {
label: ( label: (
<a href={NeosConfig.profileUrl} target="_blank"> <a href={NeosConfig.profileUrl} target="_blank">
{t("PersonalCenter")} {i18n("PersonalCenter")}
</a> </a>
), ),
}, },
{ {
label: ( label: (
<a href="https://ygobbs.com" target="_blank"> <a href="https://ygobbs.com" target="_blank">
{t("MengkaCommunity")} {i18n("MyCardCommunity")}
</a> </a>
), ),
}, },
...@@ -133,16 +133,16 @@ export const Component = () => { ...@@ -133,16 +133,16 @@ export const Component = () => {
href="https://mycard.moe/ygopro/arena/#/" href="https://mycard.moe/ygopro/arena/#/"
target="_blank" target="_blank"
> >
{t("DuelDatabase")} {i18n("DuelDatabase")}
</a> </a>
), ),
}, },
{ {
label: logined ? t("LogOut") : t("LoginToMengka"), label: logined ? i18n("LogOut") : i18n("Login"),
onClick: logined ? onLogout : onLogin, onClick: logined ? onLogout : onLogin,
}, },
{ {
label: t("Fullscreen"), label: i18n("Fullscreen"),
onClick: () => document.documentElement.requestFullscreen(), onClick: () => document.documentElement.requestFullscreen(),
danger: true, danger: true,
}, },
......
...@@ -19,7 +19,7 @@ import { ...@@ -19,7 +19,7 @@ import {
} from "@/api"; } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { accountStore, deckStore, resetUniverse, roomStore } from "@/stores"; import { accountStore, deckStore, resetUniverse, roomStore } from "@/stores";
import { Background, IconFont, Select } from "@/ui/Shared"; import { Background, IconFont, ScrollableArea, Select } from "@/ui/Shared";
import { import {
CustomRoomContent, CustomRoomContent,
...@@ -52,7 +52,7 @@ export const Component: React.FC = () => { ...@@ -52,7 +52,7 @@ export const Component: React.FC = () => {
const [entertainMatchLoading, setEntertainMatchLoading] = useState(false); // 娱乐匹配的loading状态 const [entertainMatchLoading, setEntertainMatchLoading] = useState(false); // 娱乐匹配的loading状态
const [watchLoading, setWatchLoading] = useState(false); // 观战模式的loading状态 const [watchLoading, setWatchLoading] = useState(false); // 观战模式的loading状态
const navigate = useNavigate(); const navigate = useNavigate();
const { t } = useTranslation("Match"); const { t: i18n } = useTranslation("Match");
// 匹配 // 匹配
const onMatch = async (arena: "athletic" | "entertain") => { const onMatch = async (arena: "athletic" | "entertain") => {
...@@ -224,7 +224,7 @@ export const Component: React.FC = () => { ...@@ -224,7 +224,7 @@ export const Component: React.FC = () => {
<div className={styles.wrap}> <div className={styles.wrap}>
<Space size={16}> <Space size={16}>
<Select <Select
title={t("Deck")} title={i18n("Deck")}
showSearch showSearch
value={deckName} value={deckName}
style={{ width: 200 }} style={{ width: 200 }}
...@@ -248,13 +248,13 @@ export const Component: React.FC = () => { ...@@ -248,13 +248,13 @@ export const Component: React.FC = () => {
onClick={() => navigate("/build")} onClick={() => navigate("/build")}
size="large" size="large"
> >
{t("DeckEdit")} {i18n("DeckEdit")}
</Button> </Button>
</Space> </Space>
<div className={styles["mode-select"]}> <div className={styles["mode-select"]}>
<Mode <Mode
title={t("MCCompetitiveMatchmakingTitle")} title={i18n("MCCompetitiveMatchmakingTitle")}
desc={t("MCCompetitiveMatchmakingDesc")} desc={i18n("MCCompetitiveMatchmakingDesc")}
icon={ icon={
athleticMatchLoading ? ( athleticMatchLoading ? (
<LoadingOutlined /> <LoadingOutlined />
...@@ -265,8 +265,8 @@ export const Component: React.FC = () => { ...@@ -265,8 +265,8 @@ export const Component: React.FC = () => {
onClick={onCompetitiveMatch} onClick={onCompetitiveMatch}
/> />
<Mode <Mode
title={t("MCCasualMatchmakingTitle")} title={i18n("MCCasualMatchmakingTitle")}
desc={t("MCCasualMatchmakingDesc")} desc={i18n("MCCasualMatchmakingDesc")}
icon={ icon={
entertainMatchLoading ? ( entertainMatchLoading ? (
<LoadingOutlined /> <LoadingOutlined />
...@@ -277,20 +277,20 @@ export const Component: React.FC = () => { ...@@ -277,20 +277,20 @@ export const Component: React.FC = () => {
onClick={onEntertainMatch} onClick={onEntertainMatch}
/> />
<Mode <Mode
title={t("MCCustomRoomTitle")} title={i18n("MCCustomRoomTitle")}
desc={t("MCCustomRoomDesc")} desc={i18n("MCCustomRoomDesc")}
icon={<BulbOutlined />} icon={<BulbOutlined />}
onClick={onMCCustomRoom} onClick={onMCCustomRoom}
/> />
<Mode <Mode
title={t("MCSpectatorListTitle")} title={i18n("MCSpectatorListTitle")}
desc={t("MCSpectatorListDesc")} desc={i18n("MCSpectatorListDesc")}
icon={watchLoading ? <LoadingOutlined /> : <PlayCircleFilled />} icon={watchLoading ? <LoadingOutlined /> : <PlayCircleFilled />}
onClick={onMCWatch} onClick={onMCWatch}
/> />
<Mode <Mode
title={t("SinglePlayerModeTitle")} title={i18n("SinglePlayerModeTitle")}
desc={t("SinglePlayerModeDesc")} desc={i18n("SinglePlayerModeDesc")}
icon={ icon={
singleLoading ? ( singleLoading ? (
<LoadingOutlined /> <LoadingOutlined />
...@@ -301,18 +301,18 @@ export const Component: React.FC = () => { ...@@ -301,18 +301,18 @@ export const Component: React.FC = () => {
onClick={onAIMatch} onClick={onAIMatch}
/> />
<Mode <Mode
title={t("CustomRoomTitle")} title={i18n("CustomRoomTitle")}
desc={t("CustomRoomDesc")} desc={i18n("CustomRoomDesc")}
icon={<SettingFilled />} icon={<SettingFilled />}
onClick={onCustomRoom} onClick={onCustomRoom}
/> />
<Mode <Mode
title={t("ReplayTitle")} title={i18n("ReplayTitle")}
desc={t("ReplayDesc")} desc={i18n("ReplayDesc")}
icon={<IconFont type="icon-record" size={24} />} icon={<IconFont type="icon-record" size={24} />}
onClick={replayOpen} onClick={replayOpen}
/> />
<Mode title={t("WIPTitle")} desc={t("WIPDesc")} icon={null} /> <Mode title={i18n("WIPTitle")} desc={i18n("WIPDesc")} icon={null} />
</div> </div>
</div> </div>
</div> </div>
...@@ -330,8 +330,10 @@ const Mode: React.FC<{ ...@@ -330,8 +330,10 @@ const Mode: React.FC<{
onClick?: () => void; onClick?: () => void;
}> = ({ title, desc, icon, onClick }) => ( }> = ({ title, desc, icon, onClick }) => (
<div className={styles.mode} onClick={onClick}> <div className={styles.mode} onClick={onClick}>
<ScrollableArea maxHeight="15rem">
<div className={styles.icon}>{icon}</div> <div className={styles.icon}>{icon}</div>
<div className={styles.title}>{title}</div> <div className={styles.title}>{title}</div>
<div className={styles.desc}>{desc}</div> <div className={styles.desc}>{desc}</div>
</ScrollableArea>
</div> </div>
); );
...@@ -4,10 +4,10 @@ import classNames from "classnames"; ...@@ -4,10 +4,10 @@ import classNames from "classnames";
import styles from "./index.module.scss"; import styles from "./index.module.scss";
export const Select: React.FC< export const Select: React.FC<
React.ComponentProps<typeof AntdSelect> & { title: string } React.ComponentProps<typeof AntdSelect> & { title?: string }
> = ({ title, className, dropdownStyle, ...rest }) => ( > = ({ title, className, dropdownStyle, ...rest }) => (
<div className={styles["custom-select"]}> <div className={styles["custom-select"]}>
<span className={styles.prefix}>{title}</span> {title && <span className={styles.prefix}>{title}</span>}
<AntdSelect <AntdSelect
className={classNames(styles.select, className)} className={classNames(styles.select, className)}
size="large" size="large"
......
...@@ -2,7 +2,6 @@ export * from "./Background"; ...@@ -2,7 +2,6 @@ export * from "./Background";
export * from "./CardEffectText"; export * from "./CardEffectText";
export * from "./Chain"; export * from "./Chain";
export * from "./chatHook"; export * from "./chatHook";
export * from "./css";
export * from "./DeckCard"; export * from "./DeckCard";
export * from "./DeckZone"; export * from "./DeckZone";
export * from "./IconFont"; export * from "./IconFont";
......
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