Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 67 additions & 20 deletions src/react/SimulationFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
isVisionMode,
} from '../core/constants/modes.js';
import { VisionMode, VisionOptions } from '../types/simulationTypes.js';
import { useEffect, useRef, useState } from 'react';
import { useEffect, useId, useRef, useState } from 'react';
import { SimulationKey, useTheme } from './ThemeProvider.js';
import { VisionFilterPortal } from './VisionPortal.js';
import { TOOLBAR_POSITION } from '../core/constants/position.js';
Expand Down Expand Up @@ -65,6 +65,7 @@ export default function SimulationFilter(props?: SimulationFilterProps) {
const [open, setOpen] = useState(false);
const { simulationFilter, setSimulationFilter, language } = useTheme();
const initialized = useRef(false);
const optionsId = useId();

if (!visible) return null;
if (!allowInProd && !IS_DEV) return null;
Expand Down Expand Up @@ -169,37 +170,83 @@ export default function SimulationFilter(props?: SimulationFilterProps) {

<div
className={`cb-vision-toolbar h-[34px] fixed z-[100] inline-flex items-center gap-[6px] rounded-[8px] bg-[#555555] px-[18px] py-2 text-[14px] text-white font-medium opacity-90 shadow-[0_6px_18px_rgba(0,0,0,0.25)] backdrop-blur-[6px] ${toolBarClass}`}
role="toolbar"
aria-label={
language === 'Korean'
? '시각 시뮬레이션 도구 모음'
: 'Vision simulation toolbar'
}
>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
<path d="M18 9C18 9 14.625 2.8125 9 2.8125C3.375 2.8125 0 9 0 9C0 9 3.375 15.1875 9 15.1875C14.625 15.1875 18 9 18 9ZM1.31929 9C1.38333 8.90236 1.45636 8.79387 1.53818 8.67647C1.91493 8.13592 2.47085 7.41702 3.18612 6.70175C4.63616 5.2517 6.6157 3.9375 9 3.9375C11.3843 3.9375 13.3638 5.2517 14.8139 6.70175C15.5292 7.41702 16.0851 8.13592 16.4618 8.67647C16.5436 8.79387 16.6167 8.90236 16.6807 9C16.6167 9.09764 16.5436 9.20613 16.4618 9.32353C16.0851 9.86408 15.5292 10.583 14.8139 11.2983C13.3638 12.7483 11.3843 14.0625 9 14.0625C6.6157 14.0625 4.63616 12.7483 3.18612 11.2983C2.47085 10.583 1.91493 9.86408 1.53818 9.32353C1.45636 9.20613 1.38333 9.09764 1.31929 9Z" fill="white"/>
<path d="M9 6.1875C7.4467 6.1875 6.1875 7.4467 6.1875 9C6.1875 10.5533 7.4467 11.8125 9 11.8125C10.5533 11.8125 11.8125 10.5533 11.8125 9C11.8125 7.4467 10.5533 6.1875 9 6.1875ZM5.0625 9C5.0625 6.82538 6.82538 5.0625 9 5.0625C11.1746 5.0625 12.9375 6.82538 12.9375 9C12.9375 11.1746 11.1746 12.9375 9 12.9375C6.82538 12.9375 5.0625 11.1746 5.0625 9Z" fill="white"/>
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
aria-hidden="true"
>
<path
d="M18 9C18 9 14.625 2.8125 9 2.8125C3.375 2.8125 0 9 0 9C0 9 3.375 15.1875 9 15.1875C14.625 15.1875 18 9 18 9ZM1.31929 9C1.38333 8.90236 1.45636 8.79387 1.53818 8.67647C1.91493 8.13592 2.47085 7.41702 3.18612 6.70175C4.63616 5.2517 6.6157 3.9375 9 3.9375C11.3843 3.9375 13.3638 5.2517 14.8139 6.70175C15.5292 7.41702 16.0851 8.13592 16.4618 8.67647C16.5436 8.79387 16.6167 8.90236 16.6807 9C16.6167 9.09764 16.5436 9.20613 16.4618 9.32353C16.0851 9.86408 15.5292 10.583 14.8139 11.2983C13.3638 12.7483 11.3843 14.0625 9 14.0625C6.6157 14.0625 4.63616 12.7483 3.18612 11.2983C2.47085 10.583 1.91493 9.86408 1.53818 9.32353C1.45636 9.20613 1.38333 9.09764 1.31929 9Z"
fill="white"
/>
<path
d="M9 6.1875C7.4467 6.1875 6.1875 7.4467 6.1875 9C6.1875 10.5533 7.4467 11.8125 9 11.8125C10.5533 11.8125 11.8125 10.5533 11.8125 9C11.8125 7.4467 10.5533 6.1875 9 6.1875ZM5.0625 9C5.0625 6.82538 6.82538 5.0625 9 5.0625C11.1746 5.0625 12.9375 6.82538 12.9375 9C12.9375 11.1746 11.1746 12.9375 9 12.9375C6.82538 12.9375 5.0625 11.1746 5.0625 9Z"
fill="white"
/>
</svg>
<span
<button
type="button"
className="font-medium hover:cursor-pointer ml-[11px]"
aria-haspopup="true"
aria-expanded={open}
aria-controls={optionsId}
aria-label={
language === 'Korean'
? open
? '시각 시뮬레이션 옵션 닫기'
: '시각 시뮬레이션 옵션 열기'
: open
? 'Close vision simulation options'
: 'Open vision simulation options'
}
onClick={() => setOpen(!open)}
>
{SIMULATE_LABEL[language]}
</span>
</button>

{open && (
<div className="w-0 h-5 mx-3 outline outline-[0.40px] outline-offset-[-0.20px] outline-[#909090]" />
<div className="w-0 h-5 mx-3 outline-[0.40px] outline-offset-[-0.20px] outline-[#909090]" />
)}

{open &&
MODES.map((value) => (
<button
key={value}
type="button"
className={`rounded-[8px] px-2 py-1 text-[12px] font-normal ${
simulationFilter === value
? 'bg-white text-black'
: 'hover:bg-[rgba(255,255,255,0.2)]'
}`}
onClick={() => updateSimulationFilter(value)}
(
<div
id={optionsId}
role="group"
aria-label={
language === 'Korean'
? '시각 시뮬레이션 모드'
: 'Vision simulation modes'
}
className="inline-flex items-center gap-[6px]"
>
{MODE_LABELS[language][value] ?? value}
</button>
))}
{MODES.map((value) => (
<button
key={value}
type="button"
className={`rounded-[8px] px-2 py-1 text-[12px] font-normal ${
simulationFilter === value
? 'bg-white text-black'
: 'hover:bg-[rgba(255,255,255,0.2)]'
}`}
aria-pressed={simulationFilter === value}
onClick={() => updateSimulationFilter(value)}
>
{MODE_LABELS[language][value] ?? value}
</button>
))}
</div>
)}
</div>
</VisionFilterPortal>
);
Expand Down
42 changes: 33 additions & 9 deletions src/react/ThemeSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import React, { useEffect, useRef, useState, useMemo } from 'react';
import React, { useEffect, useId, useMemo, useRef, useState } from 'react';
import { useTheme, getThemeOptions, type ThemeKey } from './ThemeProvider.js';
import Logo from '../icons/Logo.js';
import US from '../icons/Us.js';
Expand Down Expand Up @@ -38,6 +38,7 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {
);
const [isOpen, setIsOpen] = useState(false);
const wrapperRef = useRef<HTMLDivElement>(null);
const menuId = useId();

const switcherClass = SWITCHER_POSITION[position ?? 'right-bottom'];
const switcherMenuClass =
Expand All @@ -62,6 +63,15 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {
setIsOpen((prev) => !prev);
};

const toggleAriaLabel =
language === 'Korean'
? isOpen
? '테마 전환 메뉴 닫기'
: '테마 전환 메뉴 열기'
: isOpen
? 'Close theme switcher menu'
: 'Open theme switcher menu';

return (
<ThemeSwitcherPortal>
<div ref={wrapperRef} className="z-[10000]">
Expand All @@ -71,14 +81,20 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {
type="button"
aria-haspopup="menu"
aria-expanded={isOpen}
aria-controls={menuId}
aria-label={toggleAriaLabel}
onClick={toggle}
className={`fixed w-[60px] h-[60px] p-[10px] ${isOpen ? 'bg-[#252525] border-[1px] border-[#8144FF]' : 'bg-[rgba(129,68,255,0.2)]'} rounded-full flex justify-center items-center shadow-[0_0_3px_0_rgba(0,0,0,0.17)]
${switcherClass}
`}
>
{isOpen ? (
<div className="absolute flex justify-center items-center inset-0 w-full h-full">
<XButton className="self-center" width={30} />
<XButton
aria-hidden="true"
className="self-center"
width={30}
/>
</div>
) : (
<div
Expand All @@ -96,11 +112,16 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {
<div className="absolute flex justify-center items-center inset-0 w-full h-full">
{isOpen ? (
<XButton
aria-hidden="true"
className="self-center"
width={30}
/>
) : (
<Logo className="self-center" width={30} />
<Logo
aria-hidden="true"
className="self-center"
width={30}
/>
)}
</div>
</div>
Expand All @@ -112,6 +133,7 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {
<div
role="menu"
aria-label="Select theme"
id={menuId}
className={`
fixed flex-col bg-[#252525] px-[10px] py-[14px] border-[1px] border-[#8144FF] rounded-[18px] w-[220px] gap-[11px] filter drop-shadow-[0_0_1.3px_rgba(0,0,0,0.25)]
${switcherMenuClass}
Expand Down Expand Up @@ -167,21 +189,23 @@ export function ThemeSwitcher({ options, position }: TThemeSwitcherProps) {

<div className="flex justify-evenly items-center gap-[10px] px-[10px] mt-[15px]">
{language === 'English' ? (
<div
className={`hover:cursor-pointer flex text-[18px] text-[#909090] gap-[8px] items-center justify-center`}
<button
type="button"
className="hover:cursor-pointer flex text-[18px] text-[#909090] gap-[8px] items-center justify-center"
onClick={() => updateLanguage('Korean')}
>
<US width={24} />
English
</div>
</button>
) : (
<div
className={`hover:cursor-pointer flex text-[18px] text-[#909090] gap-[8px] items-center justify-center `}
<button
type="button"
className="hover:cursor-pointer flex text-[18px] text-[#909090] gap-[8px] items-center justify-center "
onClick={() => updateLanguage('English')}
>
<KR width={24} />
한국어
</div>
</button>
)}
</div>
</div>
Expand Down