2025-09-04 15:20:26 +09:00
|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState } from "react";
|
|
|
|
|
|
import { Monitor, Tablet, Smartphone, Settings } from "lucide-react";
|
|
|
|
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
|
|
|
|
import { Input } from "@/components/ui/input";
|
|
|
|
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import { ScreenResolution, SCREEN_RESOLUTIONS } from "@/types/screen";
|
|
|
|
|
|
|
|
|
|
|
|
interface ResolutionPanelProps {
|
|
|
|
|
|
currentResolution: ScreenResolution;
|
|
|
|
|
|
onResolutionChange: (resolution: ScreenResolution) => void;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const ResolutionPanel: React.FC<ResolutionPanelProps> = ({ currentResolution, onResolutionChange }) => {
|
|
|
|
|
|
const [customWidth, setCustomWidth] = useState(currentResolution.width.toString());
|
|
|
|
|
|
const [customHeight, setCustomHeight] = useState(currentResolution.height.toString());
|
|
|
|
|
|
const [selectedPreset, setSelectedPreset] = useState<string>(
|
|
|
|
|
|
SCREEN_RESOLUTIONS.find((r) => r.width === currentResolution.width && r.height === currentResolution.height)
|
|
|
|
|
|
?.name || "custom",
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const handlePresetChange = (presetName: string) => {
|
|
|
|
|
|
setSelectedPreset(presetName);
|
|
|
|
|
|
|
|
|
|
|
|
if (presetName === "custom") {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const preset = SCREEN_RESOLUTIONS.find((r) => r.name === presetName);
|
|
|
|
|
|
if (preset) {
|
|
|
|
|
|
setCustomWidth(preset.width.toString());
|
|
|
|
|
|
setCustomHeight(preset.height.toString());
|
|
|
|
|
|
onResolutionChange(preset);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCustomResolution = () => {
|
|
|
|
|
|
const width = parseInt(customWidth);
|
|
|
|
|
|
const height = parseInt(customHeight);
|
|
|
|
|
|
|
|
|
|
|
|
if (width > 0 && height > 0) {
|
|
|
|
|
|
const customResolution: ScreenResolution = {
|
|
|
|
|
|
width,
|
|
|
|
|
|
height,
|
|
|
|
|
|
name: `사용자 정의 (${width}×${height})`,
|
|
|
|
|
|
category: "custom",
|
|
|
|
|
|
};
|
|
|
|
|
|
onResolutionChange(customResolution);
|
|
|
|
|
|
setSelectedPreset("custom");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getCategoryIcon = (category: string) => {
|
|
|
|
|
|
switch (category) {
|
|
|
|
|
|
case "desktop":
|
|
|
|
|
|
return <Monitor className="h-4 w-4" />;
|
|
|
|
|
|
case "tablet":
|
|
|
|
|
|
return <Tablet className="h-4 w-4" />;
|
|
|
|
|
|
case "mobile":
|
|
|
|
|
|
return <Smartphone className="h-4 w-4" />;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return <Settings className="h-4 w-4" />;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getCategoryColor = (category: string) => {
|
|
|
|
|
|
switch (category) {
|
|
|
|
|
|
case "desktop":
|
2025-10-02 14:34:15 +09:00
|
|
|
|
return "text-primary";
|
2025-09-04 15:20:26 +09:00
|
|
|
|
case "tablet":
|
|
|
|
|
|
return "text-green-600";
|
|
|
|
|
|
case "mobile":
|
|
|
|
|
|
return "text-purple-600";
|
|
|
|
|
|
default:
|
2025-10-02 14:34:15 +09:00
|
|
|
|
return "text-muted-foreground";
|
2025-09-04 15:20:26 +09:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
|
{/* 프리셋 선택 */}
|
|
|
|
|
|
<div className="space-y-2">
|
2025-10-28 16:16:00 +09:00
|
|
|
|
<Label className="text-xs font-medium">해상도 프리셋</Label>
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<Select value={selectedPreset} onValueChange={handlePresetChange}>
|
2025-10-28 17:33:03 +09:00
|
|
|
|
<SelectTrigger className="h-6 w-full px-2 py-0" style={{ fontSize: "12px" }}>
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<SelectValue placeholder="해상도를 선택하세요" />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
|
|
|
|
|
{/* Desktop */}
|
|
|
|
|
|
<div className="px-2 py-1 text-xs font-medium text-gray-500">데스크톱</div>
|
|
|
|
|
|
{SCREEN_RESOLUTIONS.filter((r) => r.category === "desktop").map((resolution) => (
|
|
|
|
|
|
<SelectItem key={resolution.name} value={resolution.name}>
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
2025-10-28 16:16:00 +09:00
|
|
|
|
<Monitor className="text-primary h-4 w-4" />
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<span>{resolution.name}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Tablet */}
|
|
|
|
|
|
<div className="px-2 py-1 text-xs font-medium text-gray-500">태블릿</div>
|
|
|
|
|
|
{SCREEN_RESOLUTIONS.filter((r) => r.category === "tablet").map((resolution) => (
|
|
|
|
|
|
<SelectItem key={resolution.name} value={resolution.name}>
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
|
<Tablet className="h-4 w-4 text-green-600" />
|
|
|
|
|
|
<span>{resolution.name}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Mobile */}
|
|
|
|
|
|
<div className="px-2 py-1 text-xs font-medium text-gray-500">모바일</div>
|
|
|
|
|
|
{SCREEN_RESOLUTIONS.filter((r) => r.category === "mobile").map((resolution) => (
|
|
|
|
|
|
<SelectItem key={resolution.name} value={resolution.name}>
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
|
<Smartphone className="h-4 w-4 text-purple-600" />
|
|
|
|
|
|
<span>{resolution.name}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
))}
|
|
|
|
|
|
|
|
|
|
|
|
{/* Custom */}
|
|
|
|
|
|
<div className="px-2 py-1 text-xs font-medium text-gray-500">사용자 정의</div>
|
|
|
|
|
|
<SelectItem value="custom">
|
|
|
|
|
|
<div className="flex items-center space-x-2">
|
2025-10-28 16:16:00 +09:00
|
|
|
|
<Settings className="text-muted-foreground h-4 w-4" />
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<span>사용자 정의</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{/* 사용자 정의 해상도 */}
|
|
|
|
|
|
{selectedPreset === "custom" && (
|
|
|
|
|
|
<div className="space-y-3 rounded-lg border bg-gray-50 p-3">
|
|
|
|
|
|
<Label className="text-sm font-medium">사용자 정의 해상도</Label>
|
|
|
|
|
|
<div className="grid grid-cols-2 gap-3">
|
|
|
|
|
|
<div className="space-y-1">
|
2025-10-28 16:16:00 +09:00
|
|
|
|
<Label className="text-muted-foreground text-xs">너비 (px)</Label>
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<Input
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
value={customWidth}
|
|
|
|
|
|
onChange={(e) => setCustomWidth(e.target.value)}
|
|
|
|
|
|
placeholder="1920"
|
|
|
|
|
|
min="1"
|
2025-11-06 10:37:20 +09:00
|
|
|
|
step="1"
|
2025-10-28 17:33:03 +09:00
|
|
|
|
className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }}
|
|
|
|
|
|
style={{ fontSize: "12px" }}
|
2025-09-04 15:20:26 +09:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div className="space-y-1">
|
2025-10-28 16:16:00 +09:00
|
|
|
|
<Label className="text-muted-foreground text-xs">높이 (px)</Label>
|
2025-09-04 15:20:26 +09:00
|
|
|
|
<Input
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
value={customHeight}
|
|
|
|
|
|
onChange={(e) => setCustomHeight(e.target.value)}
|
|
|
|
|
|
placeholder="1080"
|
|
|
|
|
|
min="1"
|
2025-11-06 10:37:20 +09:00
|
|
|
|
step="1"
|
2025-10-28 17:33:03 +09:00
|
|
|
|
className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }}
|
|
|
|
|
|
style={{ fontSize: "12px" }}
|
2025-09-04 15:20:26 +09:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-10-28 17:33:03 +09:00
|
|
|
|
<Button
|
|
|
|
|
|
onClick={handleCustomResolution}
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
className="h-6 w-full px-2 py-0 text-xs" style={{ fontSize: "12px" }}
|
|
|
|
|
|
style={{ fontSize: "12px" }}
|
|
|
|
|
|
>
|
2025-09-04 15:20:26 +09:00
|
|
|
|
적용
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export default ResolutionPanel;
|