Files
vrclipboard-ime-gui/src/SettingsComponent.tsx
2024-09-23 17:07:27 +09:00

210 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect, useRef } from 'react';
import { invoke } from '@tauri-apps/api/tauri';
import { ChevronDown } from 'lucide-react';
interface Config {
prefix: string;
split: string;
command: string;
ignore_prefix: boolean;
on_copy_mode: OnCopyMode;
skip_url: boolean;
use_tsf_reconvert: boolean;
}
enum OnCopyMode {
ReturnToClipboard = 'ReturnToClipboard',
ReturnToChatbox = 'ReturnToChatbox',
SendDirectly = 'SendDirectly'
}
const SettingsComponent = () => {
const [settings, setSettings] = useState<Config>({
prefix: ';',
split: '/',
command: ';',
ignore_prefix: false,
on_copy_mode: OnCopyMode.ReturnToChatbox,
skip_url: true,
use_tsf_reconvert: false
});
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
useEffect(() => {
loadSettings();
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
const loadSettings = async () => {
try {
const loadedSettings: Config = await invoke('load_settings');
setSettings(loadedSettings);
} catch (error) {
console.error('Failed to load settings:', error);
}
};
const saveSettings = async () => {
try {
await invoke('save_settings', { config: settings });
alert('設定が正常に保存されました。');
} catch (error) {
console.error('Failed to save settings:', error);
alert('設定の保存に失敗しました。もう一度お試しください。');
}
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value, type, checked } = e.target;
setSettings(prev => ({
...prev,
[name]: type === 'checkbox' ? checked : value
}));
};
const handleSelectChange = (value: OnCopyMode) => {
setSettings(prev => ({ ...prev, on_copy_mode: value }));
setIsOpen(false);
};
const handleClickOutside = (event: MouseEvent) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
const getOnCopyModeLabel = (mode: OnCopyMode) => {
switch (mode) {
case OnCopyMode.ReturnToClipboard:
return 'クリップボードへ送信';
case OnCopyMode.ReturnToChatbox:
return 'チャットボックスへ送信';
case OnCopyMode.SendDirectly:
return '直接チャットへ送信';
}
};
return (
<div>
<h2 className="text-lg font-semibold mb-2"></h2>
<div className="bg-white p-3 rounded-md shadow-inner h-[calc(100vh-100px)] overflow-y-auto">
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="text"
name="split"
value={settings.split}
onChange={handleChange}
className="w-full p-2 border rounded-md"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="text"
name="command"
value={settings.command}
onChange={handleChange}
className="w-full p-2 border rounded-md"
/>
</div>
<div className="flex items-center">
<input
type="checkbox"
id="ignore_prefix"
name="ignore_prefix"
checked={settings.ignore_prefix}
onChange={handleChange}
className="mr-2"
/>
<label htmlFor="ignore_prefix" className="text-sm font-medium text-gray-700">
</label>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="text"
name="prefix"
value={settings.prefix}
onChange={handleChange}
className={`w-full p-2 border rounded-md ${settings.ignore_prefix ? 'bg-gray-100' : ''}`}
disabled={settings.ignore_prefix}
/>
</div>
<div className="flex items-center">
<input
type="checkbox"
id="skip_url"
name="skip_url"
checked={settings.skip_url}
onChange={handleChange}
className="mr-2"
/>
<label htmlFor="skip_url" className="text-sm font-medium text-gray-700">
URL
</label>
</div>
<div className="relative" ref={dropdownRef}>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<div
className="w-full p-2 border rounded-md bg-white flex justify-between items-center cursor-pointer"
onClick={() => setIsOpen(!isOpen)}
>
<span>{getOnCopyModeLabel(settings.on_copy_mode)}</span>
<ChevronDown className={`transition-transform duration-200 ${isOpen ? 'transform rotate-180' : ''}`} />
</div>
{isOpen && (
<div className="absolute z-10 mt-1 w-full bg-white border border-gray-300 rounded-md shadow-lg">
{Object.values(OnCopyMode).map((mode) => (
<div
key={mode}
className="p-2 hover:bg-gray-100 cursor-pointer"
onClick={() => handleSelectChange(mode)}
>
{getOnCopyModeLabel(mode)}
</div>
))}
</div>
)}
</div>
<div className="flex items-center">
<input
type="checkbox"
id="use_tsf_reconvert"
name="use_tsf_reconvert"
checked={settings.use_tsf_reconvert}
onChange={handleChange}
className="mr-2"
/>
<label htmlFor="use_tsf_reconvert" className="text-sm font-medium text-gray-700">
ベータ機能: Text Services Framework 使<br />
Windows11を使用している場合は Microsoft IME 使
</label>
</div>
<button
onClick={saveSettings}
className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition-colors"
>
</button>
</div>
</div>
</div>
);
};
export default SettingsComponent;