[Update] Migrate to tauri-app(Web UI)

This commit is contained in:
Sakamoto Shiina
2024-07-25 01:01:22 +09:00
parent 25899b63da
commit ebd1a8d70d
342 changed files with 14616 additions and 13428 deletions

View File

@@ -0,0 +1,20 @@
import "../../../locales/config.js";
import "@utils/root.css";
import styles from "./ConfigWindow.module.scss";
import { Topbar } from "./topbar/Topbar";
import { SidebarSection } from "./sidebar_section/SidebarSection";
import { SettingSection } from "./setting_section/SettingSection.jsx";
export const ConfigWindow = () => {
return (
<div className={styles.container}>
<Topbar />
<div className={styles.main_container}>
<SidebarSection />
<SettingSection />
</div>
</div>
);
};

View File

@@ -0,0 +1,16 @@
.container {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: space-between;
background-color: var(--dark_950_color);
overflow: hidden;
}
.main_container {
width: 100%;
height: 100%;
display: flex;
padding-top: var(--config_window_topbar_height);
}

View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Config Window</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.jsx"></script>
</body>
</html>

View File

@@ -0,0 +1,15 @@
import React from "react";
import ReactDOM from "react-dom/client";
// import "../locales/config.js";
import { ConfigWindow } from "./ConfigWindow";
// import "./reset.css";
// import "./root.css";
// import { useWindow } from "@utils/useWindow";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<ConfigWindow />
</React.StrictMode>,
);

View File

@@ -0,0 +1,12 @@
import styles from "./SettingSection.module.scss";
import { SettingBox } from "./setting_box/SettingBox";
export const SettingSection = () => {
return (
<div className={styles.scroll_container}>
<div className={styles.container}>
<SettingBox />
</div>
</div>
);
};

View File

@@ -0,0 +1,10 @@
.scroll_container {
width: 100%;
margin-left: 4rem;
overflow-y: auto;
overflow-x: hidden;
}
.container {
margin: 4rem;
}

View File

@@ -0,0 +1,25 @@
import { useSettingBox } from "../useSettingBox";
import { useSelectedMicDevice, useMicDeviceList } from "@store";
export const Appearance = () => {
const { currentSelectedMicDevice, updateSelectedMicDevice } = useSelectedMicDevice();
const { currentMicDeviceList } = useMicDeviceList();
const { DropdownMenuContainer } = useSettingBox();
const selectFunction = (selected_data) => {
const asyncFunction = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(selected_data.selected_id);
}, 3000);
});
};
updateSelectedMicDevice(asyncFunction);
};
return (
<>
<DropdownMenuContainer dropdown_id="mic_host" label="Mic Host/Driver" desc="description" selected_id="b" list={{a: "A", b: "B", c: "C"}} />
<DropdownMenuContainer dropdown_id="mic_device" label="Mic Device" desc="description" selected_id={currentSelectedMicDevice.data} list={currentMicDeviceList} selectFunction={selectFunction} state={currentSelectedMicDevice.state} />
</>
);
};

View File

@@ -0,0 +1,18 @@
import styles from "./SettingBox.module.scss";
import { useSelectedConfigTab } from "@store";
import { Appearance } from "./appearance/Appearance";
import { AboutVrct } from "./about_vrct/AboutVrct";
export const SettingBox = () => {
const { currentSelectedConfigTab } = useSelectedConfigTab();
switch (currentSelectedConfigTab) {
case "appearance":
return <Appearance />;
case "about_vrct":
return <AboutVrct />;
default:
return null;
}
};

View File

@@ -0,0 +1,135 @@
import styles from "./AboutVrct.module.scss";
import dev_section_title from "@images/about_vrct/dev_section_title.png";
import dev_misya from "@images/about_vrct/dev_misya.png";
import dev_shiina from "@images/about_vrct/dev_shiina.png";
import vrct_logo_for_about_vrct from "@images/about_vrct/vrct_logo_for_about_vrct.png";
import contributors_section_title from "@images/about_vrct/contributors_section_title.png";
import contributors_members from "@images/about_vrct/contributors_members.png";
import localization_section_title from "@images/about_vrct/localization_section_title.png";
import localization_members from "@images/about_vrct/localization_members.png";
import special_thanks_section_title from "@images/about_vrct/special_thanks_section_title.png";
import special_thanks_members from "@images/about_vrct/special_thanks_members.png";
import special_thanks_message_en from "@images/about_vrct/special_thanks_message_en.png";
import special_thanks_message_ja from "@images/about_vrct/special_thanks_message_ja.png";
import poster_showcase_section_title from "@images/about_vrct/poster_showcase_section_title.png";
import vrchat_disclaimer from "@images/about_vrct/vrchat_disclaimer.png";
import clsx from "clsx";
import { useTranslation } from "react-i18next";
import { useUiLanguage } from "@store";
import { PosterShowcaseContents } from "./poster_showcase_contents/PosterShowcaseContents";
export const AboutVrct = () => {
const { t } = useTranslation();
const { currentUiLanguage } = useUiLanguage();
return (
<div className={styles.container}>
<div className={styles.dev_section}>
<img src={dev_section_title} className={clsx(styles.section_title, styles.the_developers)} />
<div className={styles.dev_section_wrapper}>
<div className={styles.dev_card_wrapper}>
<img src={dev_misya} className={styles.dev_card_img} />
<OpenLinkContainer className={styles.dev_misya_x} href_id="dev_misya_x" />
<OpenLinkContainer className={styles.dev_misya_github} href_id="dev_misya_github" />
</div>
<div className={styles.dev_card_wrapper}>
<img src={dev_shiina} className={styles.dev_card_img} />
<OpenLinkContainer className={styles.dev_shiina_x} href_id="dev_shiina_x" />
</div>
</div>
</div>
<div className={styles.project_links_and_logo_section}>
<img src={vrct_logo_for_about_vrct} className={styles.about_vrct_logo} />
<div className={styles.project_links_wrapper}>
<OpenLinkContainer className={styles.project_link} href_id="project_link_booth" />
<OpenLinkContainer className={styles.project_link} href_id="project_link_documents" />
<OpenLinkContainer className={styles.project_link} href_id="project_link_vrct_github" />
<OpenLinkContainer className={styles.project_link} href_id="project_link_contact_us" />
</div>
</div>
<div className={styles.contributors_section}>
<img src={contributors_section_title} className={clsx(styles.section_title, styles.contributors)} />
<div className={styles.contributors_img_wrapper}>
<img src={contributors_members} className={clsx(styles.contributors_img, styles.contributors)} />
<OpenLinkContainer className={styles.contributors_done_san_x} href_id="contributors_done_san_x" />
<OpenLinkContainer className={styles.contributors_iya_x} href_id="contributors_iya_x" />
<OpenLinkContainer className={styles.contributors_rera_x} href_id="contributors_rera_x" />
<OpenLinkContainer className={styles.contributors_rera_github} href_id="contributors_rera_github" />
<OpenLinkContainer className={styles.contributors_poposuke_x} href_id="contributors_poposuke_x" />
<OpenLinkContainer className={styles.contributors_kumaguma_x} href_id="contributors_kumaguma_x" />
</div>
</div>
<div className={styles.localization_section}>
<img src={localization_section_title} className={clsx(styles.section_title, styles.localization)} />
<img src={localization_members} className={clsx(styles.localization_members_img, styles.localization)} />
</div>
<div className={styles.special_thanks_section}>
<img src={special_thanks_section_title} className={clsx(styles.section_title, styles.special_thanks)} />
<img src={special_thanks_members} className={styles.special_thanks_members_img} />
{
currentUiLanguage === "ja"
? <img src={special_thanks_message_ja} className={styles.special_thanks_message_img} />
: <img src={special_thanks_message_en} className={styles.special_thanks_message_img} />
}
</div>
<div className={styles.poster_showcase_section}>
<img src={poster_showcase_section_title} className={clsx(styles.section_title, styles.poster_showcase)} />
<PosterShowcaseContents />
</div>
<div className={styles.vrchat_disclaimer_section}>
<img src={vrchat_disclaimer} className={styles.vrchat_disclaimer} />
</div>
</div>
);
};
import dev_x_icon from "@images/about_vrct/dev_x_icon.png";
import dev_github_icon from "@images/about_vrct/dev_github_icon.png";
import contributors_x_icon from "@images/about_vrct/contributors_x_icon.png";
import contributors_github_icon from "@images/about_vrct/contributors_github_icon.png";
import project_link_booth from "@images/about_vrct/project_link_booth.png";
import project_link_documents from "@images/about_vrct/project_link_documents.png";
import project_link_vrct_github from "@images/about_vrct/project_link_vrct_github.png";
import project_link_contact_us from "@images/about_vrct/project_link_contact_us.png";
const about_vrct_links = {
dev_misya_x: { img: dev_x_icon, href: "https://twitter.com/misya_ai" },
dev_misya_github: { img: dev_github_icon, href: "https://github.com/misyaguziya" },
dev_shiina_x: { img: dev_x_icon, href: "https://twitter.com/Shiina_12siy" },
project_link_booth: { img: project_link_booth, href: "https://misyaguziya.booth.pm/items/5155325" },
project_link_documents: { img: project_link_documents, href: "https://mzsoftware.notion.site/VRCT-Documents-be79b7a165f64442ad8f326d86c22246" },
project_link_vrct_github: { img: project_link_vrct_github, href: "https://github.com/misyaguziya/VRCT" },
project_link_contact_us: { img: project_link_contact_us, href: "https://docs.google.com/forms/d/e/1FAIpQLSei-xoydOY60ivXqhOjaTzNN8PiBQIDcNhzfy6cw2sjYkcg_g/viewform" },
contributors_done_san_x: { img: contributors_x_icon, href: "https://twitter.com/done_vrc" },
contributors_iya_x: { img: contributors_x_icon, href: "https://twitter.com/IYAA_HHHH" },
contributors_rera_x: { img: contributors_x_icon, href: "https://twitter.com/rerassi" },
contributors_rera_github: { img: contributors_github_icon, href: "https://github.com/soumt-r" },
contributors_poposuke_x: { img: contributors_x_icon, href: "https://twitter.com/sig_popo" },
contributors_kumaguma_x: { img: contributors_x_icon, href: "https://twitter.com/K_kumaguma_A" },
};
const OpenLinkContainer = ({className, href_id}) => {
const href = about_vrct_links[href_id].href;
const img = about_vrct_links[href_id].img;
return (
<a className={className} href={href} target="_blank" rel="noreferrer" >
{/* for adjust size to their parent component's width. */}
<img style={ {height: "100%", width: "100%", "objectFit": "contain" }} src={img} />
</a>
);
};

View File

@@ -0,0 +1,171 @@
.container {
display: flex;
gap: 2.2rem;
flex-direction: column;
width: 72rem;
// background-color: gray;
}
.section_title {
height: 1.2rem;
object-fit: contain;
object-position: left;
&.the_developers {
margin-bottom: 0.8rem;
}
&.contributors {
margin-bottom: 0.8rem;
}
&.special_thanks {
margin-bottom: 0.6rem;
}
&.poster_showcase {
margin-bottom: 0.6rem;
}
}
.dev_section {
display: flex;
flex-direction: column;
}
.dev_section_wrapper {
display: flex;
justify-content: space-between;
}
.dev_card_wrapper {
width: 34.6rem;
position: relative;
}
.dev_card_img {
width: 100%;
}
@mixin dev_sns_styles($right) {
position: absolute;
right: $right;
bottom: 1.2rem;
width: 2.8rem;
padding: 0.4rem;
border-radius: 0.4rem;
&:hover {
background-color: var(--dark_800_color);
}
&:active {
background-color: var(--dark_900_color)
}
}
.dev_misya_x {
@include dev_sns_styles(6rem);
}
.dev_misya_github {
@include dev_sns_styles(3rem);
}
.dev_shiina_x {
@include dev_sns_styles(3rem);
}
.project_links_and_logo_section {
display: flex;
flex-direction: row;
justify-content: space-between;
text-align: center;
padding: 0 5.5rem;
}
.about_vrct_logo {
width: 20rem;
object-fit: contain;
}
.project_links_wrapper {
display: flex;
flex-direction: column;
gap: 0.2rem;
align-items: start;
}
.project_link {
height: 2.6rem;
padding: 0.4rem 1rem;
border-radius: 0.4rem;
&:hover {
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_900_color)
}
}
.contributors_img_wrapper {
position: relative;
}
.contributors_img {
width: 100%;
}
@mixin contributors_sns_styles($top, $left) {
position: absolute;
left: $left;
top: $top;
width: 2.4rem;
padding: 0.4rem;
border-radius: 0.4rem;
&:hover {
background-color: var(--dark_825_color);
}
&:active {
background-color: var(--dark_888_color)
}
}
$first_line_top: 6.2rem;
.contributors_done_san_x {
@include contributors_sns_styles($first_line_top, 2rem);
}
.contributors_iya_x {
@include contributors_sns_styles($first_line_top, 27.6rem);
}
.contributors_rera_x {
@include contributors_sns_styles($first_line_top, 52.2rem);
}
.contributors_rera_github {
@include contributors_sns_styles($first_line_top, 55rem);
}
$second_line_top: 16.6rem;
.contributors_poposuke_x {
@include contributors_sns_styles($second_line_top, 14.8rem);
}
.contributors_kumaguma_x {
@include contributors_sns_styles($second_line_top, 40.8rem);
}
.localization_section {
display: flex;
flex-direction: column;
}
.localization_members_img {
width: 100%;
}
.special_thanks_section {
display: flex;
flex-direction: column;
}
.special_thanks_members_img {
width: 100%;
margin-bottom: 0.4rem;
}
.special_thanks_message_img {
width: 100%;
}
.poster_showcase_section {
display: flex;
flex-direction: column;
}
.vrchat_disclaimer {
width: 100%;
margin-top: 8rem;
}

View File

@@ -0,0 +1,12 @@
import styles from "./PosterShowcaseContents.module.scss";
import { PostersContents } from "./posters_contents/PostersContents";
import { PosterShowcaseWorldsContents } from "./poster_showcase_worlds_contents/PosterShowcaseWorldsContents";
export const PosterShowcaseContents = () => {
return (
<div className={styles.container}>
<PosterShowcaseWorldsContents />
<PostersContents />
</div>
);
};

View File

@@ -0,0 +1,6 @@
.container {
display: flex;
justify-content: space-between;
align-items: start;
gap: 2rem;
}

View File

@@ -0,0 +1,91 @@
import clsx from "clsx";
import styles from "./PosterShowcaseWorldsContents.module.scss";
import { usePosterShowcaseWorldPageIndex } from "@store";
const images = import.meta.glob("@images/about_vrct/showcased_worlds/*.{png,jpg,jpeg,svg}", { eager: true });
const getImageByFileName = (file_name) => {
const imagePath = Object.keys(images).find((path) => path.includes(file_name));
return imagePath ? images[imagePath]?.default : null;
};
import poster_showcase_worlds_settings from "./poster_showcase_worlds_settings";
import { chunkArray } from "@utils/chunkArray";
export const PosterShowcaseWorldsContents = () => {
const { currentPosterShowcaseWorldPageIndex } = usePosterShowcaseWorldPageIndex();
const poster_showcase_world_images = poster_showcase_worlds_settings.map((setting) => ({
img: getImageByFileName(setting.image_file_name),
x_post_num: setting.x_post_num
}));
const chunked_poster_showcase_world_images = chunkArray(poster_showcase_world_images, 8);
const target_poster_showcase_world_images = chunked_poster_showcase_world_images[currentPosterShowcaseWorldPageIndex];
return (
<div className={styles.container}>
<div className={styles.poster_showcase_world_container}>
{target_poster_showcase_world_images.map((poster, index) => {
let content = (
<div className={styles.poster_showcase_world_img} >
<img style={ {height: "100%", width: "100%", "objectFit": "contain" }} src={poster.img} />
</div>
);
if (poster.x_post_num !== null) {
content = (
<a href={`https://twitter.com/Shiina_12siy/status/${poster.x_post_num}`} target="_blank" rel="noreferrer" >
{content}
</a>
);
}
const class_names = clsx(styles.poster_showcase_world_wrapper, {
[styles.clickable]: (poster.x_post_num !== null)
});
return (
<div className={class_names} key={index}>
{content}
</div>
);
})}
</div>
<PosterShowcaseWorldsPagination page_length={chunked_poster_showcase_world_images.length}/>
</div>
);
};
import chat_white_square from "@images/chato_white_square.png";
import { useEffect } from "react";
import { randomIntMinMax } from "@utils/randomIntMinMax";
const PosterShowcaseWorldsPagination = ({ page_length }) => {
const { currentPosterShowcaseWorldPageIndex, updatePosterShowcaseWorldPageIndex } = usePosterShowcaseWorldPageIndex();
useEffect(() => {
updatePosterShowcaseWorldPageIndex(randomIntMinMax(page_length -1));
},[page_length]);
const setPage = (index) => {
updatePosterShowcaseWorldPageIndex(index);
};
const getClassNames = (index, baseClass) => clsx(baseClass, {
[styles.is_active]: (currentPosterShowcaseWorldPageIndex === index),
});
return (
<div className={styles.pagination_container}>
{[...Array(page_length).keys()].map((index) => {
return (
<div key={index} className={getClassNames(index, styles.pagination_box)} onClick={() => setPage(index)}>
<div className={styles.chato_box}>
<img src={chat_white_square} className={getClassNames(index, styles.pagination_chato_img)}/>
</div>
<div className={styles.indicator_box}>
<div className={getClassNames(index, styles.indicator)}></div>
<p className={getClassNames(index, styles.pagination_num)}>{index + 1}</p>
</div>
</div>
);
})}
</div>
);
};

View File

@@ -0,0 +1,123 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
gap: 1rem;
justify-content: space-between;
}
$image_height: 2.8rem;
$y_padding: 0.4rem;
$image_height_gap: 0.4rem;
.poster_showcase_world_container {
display: flex;
flex-direction: column;
align-items: stretch;
gap: $image_height_gap;
height: calc( (($image_height + ($y_padding*2)) * 8) + ($image_height_gap * (8 - 1)) );
}
.poster_showcase_world_wrapper {
display: flex;
padding: $y_padding 0.6rem $y_padding 0.8rem;
border-radius: 0.4rem 0 0 0.4rem;
&.clickable {
cursor: pointer;
&:hover {
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_925_color);
}
}
}
.poster_showcase_world_img {
height: $image_height;
}
.pagination_container {
display: flex;
justify-content: space-between;
gap: 6%;
margin: 0 2.6rem;
}
$animation_duration: .15s;
.pagination_box {
width: 100%;
cursor: pointer;
&:active .pagination_chato_img {
animation: tremble_animation $animation_duration ease-out;
}
&:active.is_active .pagination_chato_img {
transform: translate(-50%, -50%) rotate(-22deg);
}
&.is_active .pagination_chato_img {
top: 48%;
animation: rotate_animation $animation_duration ease-out;
}
&:hover {
& .pagination_chato_img {
top: 108%;
}
&.is_active .pagination_chato_img {
animation: tremble_animation $animation_duration ease-out;
}
}
}
.indicator_box {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.indicator {
width: 100%;
height: 0.2rem;
background-color: var(--dark_800_color);
&.is_active {
background-color: var(--primary_400_color);
}
}
.pagination_num {
font-size: 1.8rem;
padding: 1rem;
color: var(--dark_600_color);
&.is_active {
color: var(--primary_300_color);
}
}
.chato_box {
position: relative;
width: 100%;
height: 6rem;
overflow: hidden;
}
.pagination_chato_img {
position: absolute;
top: 200%;
left: 51%;
transform: translate(-50%, -50%) rotate(22deg);
width: 2.8rem;
transition: top $animation_duration ease-out;
}
@keyframes rotate_animation {
0% {
transform: translate(-50%, -50%) rotate(22deg);
}
100% {
transform: translate(-50%, -50%) rotate(360deg + 22deg);
}
}
@keyframes tremble_animation {
0% { left: 51%; }
25% { left: 55%; }
50% { left: 45%; }
75% { left: 48%; }
100% { left: 51%; }
}

View File

@@ -0,0 +1,63 @@
const poster_showcase_worlds_settings = [
// トサカひよ
{ image_file_name: "kokekkopiyopiyo", x_post_num: "1779076974369276014" },
// MiuJepang
{ image_file_name: "ippaidou", x_post_num: "1787801976354513319" },
{ image_file_name: "nihongokurabu", x_post_num: "1779004631936614893" },
{ image_file_name: "language_exchange_tervern", x_post_num: "1779749425923150317" },
{ image_file_name: "japanese_culture_osenbeito", x_post_num: "1788522972409721137" },
{ image_file_name: "silakan_datang_ke_rumahku", x_post_num: "1788522607631056941" },
{ image_file_name: "uj_club", x_post_num: "1780791654196388201" },
{ image_file_name: "sushi_stand_guruguru", x_post_num: "1788523302404952218" },
{ image_file_name: "sushi_guru_annex", x_post_num: null },
// poposuke_sig
{ image_file_name: "usanezumi_shrine2", x_post_num: "1781224020383506649" },
// KUROINU_YOUHEI
{ image_file_name: "kuroinu_work_room", x_post_num: "1779750007564112146" },
// いちや_ICHIYA
{ image_file_name: "ehon_no_heikousekai_jimusho", x_post_num: "1780792306976850285" },
{ image_file_name: "ikoiba", x_post_num: "1782723006923780580" },
{ image_file_name: "kimodameshi", x_post_num: "1781224391692714133" },
{ image_file_name: "parallel_collar", x_post_num: null },
// HayaTikaze
{ image_file_name: "study_japanese_world_japanichijou", x_post_num: "1781539871829766550" },
// aji_3
{ image_file_name: "yuttari_eikaiwa", x_post_num: "1779002892999078046" },
// 八葉そるち
{ image_file_name: "re_yatuha_room", x_post_num: "1779830390435590196" },
// chakamoto
{ image_file_name: "chakachaka_multipurpose_room", x_post_num: null },
// MloYolM (よるむ)
{ image_file_name: "cafe_cian", x_post_num: "1787802552907739504" },
// ミラクル・オルカ
{ image_file_name: "mamehinata_dogrun", x_post_num: "1782723423179100471" },
// いんくEenkoo
{ image_file_name: "tyuuniti_kouryuukai", x_post_num: null },
// 1ban_meno
{ image_file_name: "bar_asagao", x_post_num: "1788523857642758370" },
// 沈黙静寂
{ image_file_name: "monogatari_meetup", x_post_num: "1781538415789674976" },
// tommie_500
{ image_file_name: "stretch_club_starting_from_minus", x_post_num: null },
// MiMi_Sorahana # VRC日韓交流会 (KRJPEX.1355)
{ image_file_name: "kr_jp_exchange", x_post_num: null },
// Einアイン
{ image_file_name: "smokerz_guild_v2", x_post_num: null },
];
export default poster_showcase_worlds_settings;

View File

@@ -0,0 +1,69 @@
import clsx from "clsx";
import styles from "./PostersContents.module.scss";
import { useUiLanguage } from "@store";
import { useVrctPosterIndex } from "@store";
import ArrowLeftSvg from "@images/arrow_left.svg?react";
import iya_vrct_poster_ja from "@images/about_vrct/vrct_posters/iya_vrct_poster_ja.png";
import iya_vrct_poster_en from "@images/about_vrct/vrct_posters/iya_vrct_poster_en.png";
import iya_vrct_poster_cn from "@images/about_vrct/vrct_posters/iya_vrct_poster_cn.png";
import iya_vrct_poster_ko from "@images/about_vrct/vrct_posters/iya_vrct_poster_ko.png";
import iya_vrct_manga_ja from "@images/about_vrct/vrct_posters/iya_vrct_manga_ja.png";
import iya_vrct_manga_en from "@images/about_vrct/vrct_posters/iya_vrct_manga_en.png";
import iya_vrct_manga_ko from "@images/about_vrct/vrct_posters/iya_vrct_manga_ko.png";
const poster_images = [
{ img: iya_vrct_poster_ja, poster_type: "poster" },
{ img: iya_vrct_poster_en, poster_type: "poster" },
{ img: iya_vrct_poster_cn, poster_type: "poster" },
{ img: iya_vrct_poster_ko, poster_type: "poster" },
{ img: iya_vrct_manga_ja, poster_type: "manga" },
{ img: iya_vrct_manga_en, poster_type: "manga" },
{ img: iya_vrct_manga_ko, poster_type: "manga" },
];
import poster_images_authors_ja from "@images/about_vrct/vrct_posters/authors/poster_images_authors_ja.png";
import poster_images_authors_en from "@images/about_vrct/vrct_posters/authors/poster_images_authors_en.png";
import poster_images_authors_m_ja from "@images/about_vrct/vrct_posters/authors/poster_images_authors_m_ja.png";
import poster_images_authors_m_en from "@images/about_vrct/vrct_posters/authors/poster_images_authors_m_en.png";
export const PostersContents = () => {
const { currentVrctPosterIndex, updateVrctPosterIndex } = useVrctPosterIndex();
const { currentUiLanguage } = useUiLanguage();
const updateIndex = (delta) => {
const newIndex = (currentVrctPosterIndex + delta + poster_images.length) % poster_images.length;
updateVrctPosterIndex(newIndex);
};
const current_poster = poster_images[currentVrctPosterIndex];
const current_poster_authors_img_ja = (current_poster.poster_type === "poster") ? poster_images_authors_ja : poster_images_authors_m_ja;
const current_poster_authors_img_en = (current_poster.poster_type === "poster") ? poster_images_authors_en : poster_images_authors_m_en;
return (
<div className={styles.poster_pagination_container}>
<div className={styles.poster_pagination_wrapper}>
<button
className={clsx(styles.poster_pagination_button, styles.poster_prev)}
onClick={() => updateIndex(-1)}
>
<ArrowLeftSvg className={clsx(styles.poster_pagination_svg, styles.poster_prev_svg)} />
</button>
<img src={current_poster.img} className={styles.poster_img} />
<button
className={clsx(styles.poster_pagination_button, styles.poster_next)}
onClick={() => updateIndex(1)}
>
<ArrowLeftSvg className={clsx(styles.poster_pagination_svg, styles.poster_next_svg)} />
</button>
</div>
{
currentUiLanguage === "ja"
? <img src={current_poster_authors_img_ja} className={styles.poster_authors_img} />
: <img src={current_poster_authors_img_en} className={styles.poster_authors_img} />
}
</div>
);
};

View File

@@ -0,0 +1,46 @@
.poster_pagination_container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.poster_pagination_wrapper {
display: flex;
}
$poster_img_width: 18rem;
.poster_img {
width: $poster_img_width;
}
$poster_pagination_button_width: 4.6rem;
.poster_pagination_button {
width: $poster_pagination_button_width;
display: flex;
justify-content: center;
align-items: center;
color: var(--dark_700_color);
&:hover {
background-color: var(--dark_900_color);
}
&:active {
background-color: var(--dark_925_color);
}
&.poster_prev {
border-radius: 0.8rem 0 0 0.8rem;
}
&.poster_next {
border-radius: 0 0.8rem 0.8rem 0;
}
}
.poster_pagination_svg {
width: 3.2rem;
&.poster_next_svg {
transform: rotate(180deg);
}
}
.poster_authors_img {
width: $poster_img_width + $poster_pagination_button_width;
}

View File

@@ -0,0 +1,58 @@
import styles from "./DropdownMenu.module.scss";
import clsx from "clsx";
import { useOpenedDropdownMenu } from "@store";
export const DropdownMenu = (props) => {
const { updateOpenedDropdownMenu, currentOpenedDropdownMenu } = useOpenedDropdownMenu();
const openDropdownMenu = () => {
updateOpenedDropdownMenu(props.dropdown_id);
};
const selectValue = (key) => {
updateOpenedDropdownMenu("");
props.selectFunction({
dropdown_id: props.dropdown_id,
selected_id: key,
});
};
const dropdown_content_wrapper_class_name = clsx(styles["dropdown_content_wrapper"], {
[styles["is_opened"]]: (currentOpenedDropdownMenu === props.dropdown_id) ? true : false
});
const dropdown_toggle_button_class_name = clsx(styles["dropdown_toggle_button"], {
[styles["is_loading"]]: (props.state === "loading") ? true : false
});
return (
<div className={styles.container}>
<div className={dropdown_toggle_button_class_name} onClick={openDropdownMenu}>
{(props.state === "loading")
? <p className={styles.dropdown_selected_text}>Loading...</p>
: <p className={styles.dropdown_selected_text}>{props.list[props.selected_id]}</p>
}
{(props.state === "loading")
? <span className={styles.loader}></span>
: null
}
</div>
<div className={dropdown_content_wrapper_class_name}>
<div className={styles.dropdown_content}>
{
Object.entries(props.list).map(([key, value]) => {
return (
<div key={key} className={styles.value_button} onClick={() => selectValue(key)}>
<p className={styles.value_text} >{value}</p>
</div>
);
})
}
</div>
</div>
</div>
);
};

View File

@@ -0,0 +1,64 @@
@import "@scss_mixins";
.container {
position: relative;
}
.dropdown_toggle_button {
position: relative;
background-color: var(--dark_925_color);
min-width: 20rem;
padding: 0.6rem 0.8rem;
&:hover {
background-color: var(--dark_850_color);
}
&:active {
background-color: var(--dark_950_color);
}
&.is_loading {
pointer-events: none;
}
}
.dropdown_selected_text {
font-size: 1.4rem;
}
.dropdown_content_wrapper {
display: none;
position: absolute;
top: 100%; // Position it below the toggle button
left: 0;
width: 100%;
z-index: 1;
&.is_opened {
display: block;
}
}
.dropdown_content {
background-color: var(--dark_900_color);
border: 0.1rem solid var(--dark_600_color);
display: flex;
flex-direction: column;
gap: 0.1rem;
}
.value_button {
background-color: var(--dark_875_color);
padding: 0.8rem;
&:hover {
background-color: var(--dark_800_color);
}
&:active {
background-color: var(--dark_900_color);
}
}
.value_text {
font-size: 1.2rem;
}
.loader {
@include loader(2rem, 0.2rem, right, 0);
}

View File

@@ -0,0 +1,10 @@
import styles from "./LabelComponent.module.scss";
export const LabelComponent = (props) => {
return (
<div className={styles.label_component}>
<p>{props.label}</p>
<p>{props.desc}</p>
</div>
);
};

View File

@@ -0,0 +1,23 @@
import styles from "./useSettingBox.module.scss";
import { LabelComponent } from "./components/label_component/LabelComponent";
import { DropdownMenu } from "./components/dropdown_menu/DropdownMenu";
import { useOpenedDropdownMenu } from "@store";
export const useSettingBox = () => {
const { updateOpenedDropdownMenu } = useOpenedDropdownMenu();
const DropdownMenuContainer = (props) => {
const onMouseLeaveFunction = () => {
updateOpenedDropdownMenu("");
};
return (
<div className={styles.container} onMouseLeave={onMouseLeaveFunction}>
<LabelComponent label={props.label} desc={props.desc} />
<DropdownMenu {...props}/>
</div>
);
};
return { DropdownMenuContainer };
};

View File

@@ -0,0 +1,8 @@
.container {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
background-color: var(--dark_888_color);
padding: 2rem;
}

View File

@@ -0,0 +1,46 @@
import styles from "./SidebarSection.module.scss";
export const SidebarSection = () => {
return (
<div className={styles.container}>
<div className={styles.tabs_wrapper}>
<Tab tab_id="appearance" />
<Tab tab_id="translation" />
<Tab tab_id="transcription" />
<Tab tab_id="vr" />
<Tab tab_id="others" />
<Tab tab_id="advanced_settings" />
</div>
<div className={styles.separated_tabs_wrapper}>
<Tab tab_id="about_vrct" />
</div>
</div>
);
};
import clsx from "clsx";
import { useTranslation } from "react-i18next";
import { useSelectedConfigTab } from "@store";
const Tab = (props) => {
const { t } = useTranslation();
const { updateSelectedConfigTab, currentSelectedConfigTab } = useSelectedConfigTab();
const onclickFunction = () => {
updateSelectedConfigTab(props.tab_id);
};
const tab_container_class_names = clsx(styles["tab_container"], {
[styles["is_selected"]]: (currentSelectedConfigTab === props.tab_id) ? true : false
});
const switch_indicator_class_names = clsx(styles["switch_indicator"], {
[styles["is_selected"]]: (currentSelectedConfigTab === props.tab_id) ? true : false
});
return (
<div className={tab_container_class_names} onClick={onclickFunction}>
<p className={styles.tab_text}>{t(`config_window.side_menu_labels.${props.tab_id}`)}</p>
<div className={switch_indicator_class_names}></div>
</div>
);
};

View File

@@ -0,0 +1,63 @@
.container {
width: var(--config_window_sidebar_width);
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: space-between;
padding: 5.8rem 0rem 5.8rem 2.8rem;
max-height: 60rem;
}
.tabs_wrapper {
width: 100%;
display: flex;
flex-direction: column;
gap: 0.1rem;
flex: 1;
}
.tab_container {
position: relative;
width: 100%;
display: flex;
justify-content: left;
align-items: center;
color: var(--dark_basic_text_color);
padding: 0.8rem 0 0.8rem 1.8rem;
cursor: pointer;
&:hover {
background-color: var(--dark_800_color);
}
&:active {
background-color: var(--dark_900_color);
}
&.is_selected {
background-color: inherit;
color: var(--primary_200_color);
cursor: default;
pointer-events: none;
}
}
.switch_indicator {
display: none;
&.is_selected {
display: block;
position: absolute;
top: 50%;
left: 0rem;
transform: translate(-50%, -50%);
width: 0.2rem;
height: 2.6rem;
border-radius: 0.1rem;
background-color: var(--primary_300_color);
}
}
.tab_text {
font-size: 1.8rem;
}
.separated_tabs_wrapper {
// padding-bottom: 1.2rem;
}

View File

@@ -0,0 +1,17 @@
import styles from "./Topbar.module.scss";
import { TitleBox } from "./title_box/TitleBox";
import { SectionTitleBox } from "./section_title_box/SectionTitleBox";
import { CompactSwitchBox } from "./compact_switch_box/CompactSwitchBox";
export const Topbar = () => {
return (
<div className={styles.container}>
<div className={styles.wrapper}>
<TitleBox />
<SectionTitleBox />
<CompactSwitchBox />
</div>
</div>
);
};

View File

@@ -0,0 +1,12 @@
.container {
width: 100%;
height: 0%;
}
.wrapper {
height: var(--config_window_topbar_height);
background-color: var(--dark_850_color);
display: flex;
justify-content: space-between;
flex-shrink: 0;
}

View File

@@ -0,0 +1,12 @@
import { useTranslation } from "react-i18next";
import styles from "./CompactSwitchBox.module.scss";
export const CompactSwitchBox = () => {
const { t } = useTranslation();
return (
<div className={styles.container}>
<p>{t("config_window.compact_mode")}</p>
</div>
);
};

View File

@@ -0,0 +1,9 @@
.container {
// flex: 0;
// width: 100%;
width: 14rem;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}

View File

@@ -0,0 +1,13 @@
import { useTranslation } from "react-i18next";
import styles from "./SectionTitleBox.module.scss";
import { useSelectedConfigTab } from "@store";
export const SectionTitleBox = () => {
const { t } = useTranslation();
const { currentSelectedConfigTab } = useSelectedConfigTab();
return (
<div className={styles.container}>
<p className={styles.title}>{t(`config_window.side_menu_labels.${currentSelectedConfigTab}`)}</p>
</div>
);
};

View File

@@ -0,0 +1,13 @@
.container {
flex: 1;
width: 100%;
height: 100%;
display: flex;
justify-content: left;
align-items: center;
padding-left: 2rem;
}
.title {
font-size: 2.2rem;
}

View File

@@ -0,0 +1,14 @@
import { useTranslation } from "react-i18next";
import styles from "./TitleBox.module.scss";
import chato_img from "@images/chato_white.png";
export const TitleBox = () => {
const { t } = useTranslation();
return (
<div className={styles.container}>
<img src={chato_img} className={styles.logo_chato} alt="VRCT logo chato" />
<p className={styles.title}>{t("config_window.config_title")}</p>
</div>
);
};

View File

@@ -0,0 +1,19 @@
.container {
// flex: 0;
width: var(--config_window_sidebar_width);
height: 100%;
display: flex;
justify-content: left;
align-items: center;
padding-left: 2.6rem;
gap: 1.4rem;
}
.logo_chato {
width: 3.2rem;
padding-top: 0.6rem;
}
.title {
font-size: 2.2rem;
}