Add SettingsPanel component for managing dropper nicknames

This commit is contained in:
MayaTheShy
2026-03-21 20:54:55 -04:00
parent 6e2c17de29
commit 846f3e8b80

View File

@@ -0,0 +1,145 @@
import React, { useState, useCallback } from 'react';
import { useInventoryStore } from '../store/inventoryStore';
import './SettingsPanel.css';
function SettingsPanel({ isOpen, onClose }) {
const droppers = useInventoryStore((state) => state.inventory.droppers) || [];
const dropperNicknames = useInventoryStore((state) => state.dropperNicknames) || {};
const setDropperNickname = useInventoryStore((state) => state.setDropperNickname);
const [editingDropper, setEditingDropper] = useState(null);
const [editValue, setEditValue] = useState('');
const [saving, setSaving] = useState(false);
const startEditing = useCallback((dropperName) => {
setEditingDropper(dropperName);
setEditValue(dropperNicknames[dropperName] || '');
}, [dropperNicknames]);
const saveNickname = useCallback(async () => {
if (!editingDropper) return;
setSaving(true);
await setDropperNickname(editingDropper, editValue);
setSaving(false);
setEditingDropper(null);
setEditValue('');
}, [editingDropper, editValue, setDropperNickname]);
const removeNickname = useCallback(async (dropperName) => {
setSaving(true);
await setDropperNickname(dropperName, '');
setSaving(false);
}, [setDropperNickname]);
const handleKeyDown = useCallback((e) => {
if (e.key === 'Enter') saveNickname();
if (e.key === 'Escape') {
setEditingDropper(null);
setEditValue('');
}
}, [saveNickname]);
if (!isOpen) return null;
return (
<div className="settings-overlay" onClick={onClose}>
<div className="settings-panel" onClick={(e) => e.stopPropagation()}>
<div className="settings-header">
<h2> Settings</h2>
<button className="settings-close" onClick={onClose}></button>
</div>
<div className="settings-body">
<div className="settings-section">
<h3>🏷 Dropper Nicknames</h3>
<p className="settings-hint">
Give your dispensers friendly names so they're easier to identify.
</p>
{droppers.length === 0 ? (
<div className="settings-empty">
No droppers detected. Connect the bridge to discover droppers.
</div>
) : (
<div className="dropper-nickname-list">
{droppers.map((d) => {
const nickname = dropperNicknames[d.name];
const isEditing = editingDropper === d.name;
const shortName = d.name.replace(/^minecraft:/, '');
return (
<div key={d.name} className="dropper-nickname-row">
<div className="dropper-nickname-info">
<span className="dropper-raw-name">
{shortName}
{d.isDefault && <span className="dropper-badge default">default</span>}
{d.clientId && <span className="dropper-badge client">client {d.clientId}</span>}
</span>
{nickname && !isEditing && (
<span className="dropper-current-nick">"{nickname}"</span>
)}
</div>
<div className="dropper-nickname-actions">
{isEditing ? (
<>
<input
type="text"
className="nickname-input"
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Enter nickname..."
maxLength={32}
autoFocus
disabled={saving}
/>
<button
className="mc-btn green nick-btn"
onClick={saveNickname}
disabled={saving}
>
</button>
<button
className="mc-btn nick-btn"
onClick={() => { setEditingDropper(null); setEditValue(''); }}
disabled={saving}
>
</button>
</>
) : (
<>
<button
className="mc-btn nick-btn"
onClick={() => startEditing(d.name)}
title="Edit nickname"
>
</button>
{nickname && (
<button
className="mc-btn red nick-btn"
onClick={() => removeNickname(d.name)}
title="Remove nickname"
disabled={saving}
>
🗑
</button>
)}
</>
)}
</div>
</div>
);
})}
</div>
)}
</div>
</div>
</div>
</div>
);
}
export default SettingsPanel;