Enhance SettingsModal with dynamic device loading for microphones, speakers, and cameras; implement video testing functionality; improve UI with loading states and error handling; update Button and Select components for better interactivity.

This commit is contained in:
2025-10-10 19:14:01 +05:00
parent f9406cf6fa
commit d7d8f4771f
6 changed files with 911 additions and 80 deletions
+3
View File
@@ -6,6 +6,7 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
className?: string;
size?: "small" | "medium" | "large";
ref?: React.RefObject<HTMLButtonElement | null>;
isActive?: boolean;
}
function Button({
@@ -16,6 +17,7 @@ function Button({
ref,
type,
onClick,
isActive,
...props
}: ButtonProps) {
return (
@@ -28,6 +30,7 @@ function Button({
}}
className={clsx(
"transition-all select-none cursor-pointer disabled:!cursor-default flex outline-none gap-2 items-center justify-center font-medium disabled:bg-[#F6F6F6] disabled:!text-[#D6D6D6]",
isActive && "bg-[#F3F1FD] !text-[#7B60F3]",
variant === "menu" &&
"text-[#7D7D7D] hover:bg-[#F3F3F3] active:bg-[#F3F1FD] active:text-[#7B60F3]",
variant === "cta" &&
+45 -25
View File
@@ -2,6 +2,7 @@ import { useClickAway } from "@uidotdev/usehooks";
import clsx from "clsx";
import { useEffect, useRef, useState } from "react";
import ChevronDownIcon from "../icons/ChevronDownIcon";
import CheckIcon from "../icons/CheckIcon";
interface SelectProps {
options: string[];
@@ -14,16 +15,16 @@ function Select({
options,
onSelect,
className,
defaultOption: defaultValue = "",
defaultOption = "",
}: SelectProps) {
const [isOpen, setIsOpen] = useState(false);
const [selectedOption, setSelectedOption] = useState(defaultValue);
const [selectedOption, setSelectedOption] = useState(defaultOption);
const ref = useClickAway<HTMLDivElement>(() => setIsOpen(false));
const dropDownRef = useRef<HTMLDivElement>(null);
useEffect(() => setSelectedOption(defaultValue), [defaultValue]);
useEffect(() => setSelectedOption(defaultOption), [defaultOption]);
useEffect(() => onSelect(selectedOption), [onSelect, selectedOption]);
@@ -42,38 +43,57 @@ function Select({
}, [isOpen]);
return (
<div
ref={ref}
className={clsx(
"bg-[#F3F3F3] 2xl:p-[1.111vw] p-4 flex justify-between items-center 2xl:gap-[0.556vw] gap-2 relative select-none cursor-pointer",
isOpen
? "2xl:rounded-t-[1.111vw] rounded-t-2xl"
: "2xl:rounded-[1.111vw] rounded-2xl",
className
)}
onClick={() => setIsOpen(!isOpen)}
>
<p className="text-m">{selectedOption}</p>
<div
<div ref={ref} className={clsx("relative", className)}>
{/* Select Button */}
<button
className={clsx(
"2xl:size-[1.111vw] size-4 text-[#7D7D7D]",
isOpen && "rotate-180"
"w-full bg-[#F3F3F3] 2xl:py-[0.972vw] py-3.5 2xl:px-[1.111vw] px-4 flex justify-between items-center 2xl:gap-[0.833vw] gap-3 select-none cursor-pointer transition-colors",
"2xl:rounded-[1.111vw] rounded-2xl",
"hover:bg-[#F0F0F0]",
isOpen && "ring-1 ring-[#7B60F3]"
)}
onClick={() => setIsOpen(!isOpen)}
>
<ChevronDownIcon />
</div>
<p className="text-m text-ellipsis line-clamp-1 text-left">
{selectedOption || "Не выбрано"}
</p>
<div
className={clsx(
"2xl:size-[1.389vw] size-5 text-[#7D7D7D] shrink-0 transition-transform",
isOpen && "rotate-180"
)}
>
<ChevronDownIcon />
</div>
</button>
{/* Dropdown Menu */}
{isOpen && (
<div
className="absolute left-0 top-full z-10 w-full 2xl:rounded-b-[1.111vw] rounded-b-2xlo overflow-hidden"
ref={dropDownRef}
className="absolute left-0 2xl:top-[calc(100%+0.556vw)] top-[calc(100%+8px)] z-10 w-full bg-white 2xl:rounded-[1.111vw] rounded-2xl shadow-[0px_4px_40px_0px_rgba(0,0,0,0.05),0px_2px_2px_0px_rgba(0,0,0,0.05)] 2xl:p-[0.833vw] p-3 2xl:space-y-[0.278vw] space-y-1 overflow-auto"
>
{options.map((option) => (
<p
<button
key={option}
className="bg-[#F3F3F3] 2xl:p-[1.111vw] p-4 text-m hover:bg-[#f0f0f0] transition-colors"
className={clsx(
"w-full flex items-center 2xl:gap-[0.278vw] gap-1 2xl:p-[0.833vw] p-3 2xl:rounded-[0.556vw] rounded-lg text-left transition-colors hover:bg-[#F3F3F3]"
)}
onClick={() => {
setSelectedOption(option);
setIsOpen(false);
}}
>
{option}
</p>
<div
className={clsx(
"2xl:size-[1.111vw] size-4 shrink-0 text-[#7B60F3]",
option !== selectedOption && "opacity-0"
)}
>
<CheckIcon />
</div>
<span className="text-s">{option}</span>
</button>
))}
</div>
)}