FIX: Media Explorer panel preview nút Synth cho phép lọc nhanh nhạc cụ

This commit is contained in:
2026-08-02 21:09:01 +07:00
parent a0b6110fd1
commit 9f762c2a78
2 changed files with 45 additions and 6 deletions
+40 -2
View File
@@ -9311,6 +9311,7 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
return saved ? JSON.parse(saved) : null; return saved ? JSON.parse(saved) : null;
}()); }());
const [synthOpen, setSynthOpen] = React.useState(false); const [synthOpen, setSynthOpen] = React.useState(false);
const [synthFilter, setSynthFilter] = React.useState('');
const [synthList, setSynthList] = React.useState(null); const [synthList, setSynthList] = React.useState(null);
const [synthLoading, setSynthLoading] = React.useState(false); const [synthLoading, setSynthLoading] = React.useState(false);
const synthListRef = React.useRef(null); const synthListRef = React.useRef(null);
@@ -9840,6 +9841,7 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
const toggleSynthDropdown = () => { const toggleSynthDropdown = () => {
if (synthOpen) { setSynthOpen(false); return; } if (synthOpen) { setSynthOpen(false); return; }
setSynthFilter(''); // Clear search filter on open
setSynthOpen(true); setSynthOpen(true);
if (synthListRef.current) return; if (synthListRef.current) return;
setSynthLoading(true); setSynthLoading(true);
@@ -9874,6 +9876,19 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
} }
}; };
const filteredSynthList = React.useMemo(() => {
if (!synthList) return [];
if (!synthFilter.trim()) return synthList;
const query = synthFilter.toLowerCase().trim();
return synthList.map(group => {
const presets = (group.presets || []).filter(p =>
(p.name || '').toLowerCase().includes(query) ||
String(p.program).includes(query)
);
return { ...group, presets };
}).filter(group => group.presets.length > 0);
}, [synthList, synthFilter]);
const playMidiPreview = async (f, token) => { const playMidiPreview = async (f, token) => {
// Play real MIDI file through selected synth instrument (SonicSF) // Play real MIDI file through selected synth instrument (SonicSF)
if (!f || !window.SonicSF) return; if (!f || !window.SonicSF) return;
@@ -10451,10 +10466,30 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
</button> </button>
{synthOpen && ( {synthOpen && (
<div className="absolute bottom-8 left-0 z-40 w-64 bg-white border border-[#808080] shadow-xl rounded-sm text-xs text-slate-800 font-sans max-h-72 overflow-y-auto"> <div className="absolute bottom-8 left-0 z-40 w-64 bg-white border border-[#808080] shadow-xl rounded-sm text-xs text-slate-800 font-sans max-h-72 overflow-y-auto">
<div className="sticky top-0 bg-[#e0e0e0] px-2 py-1 font-bold border-b border-[#a0a0a0] flex items-center justify-between"> <div className="sticky top-0 bg-[#e0e0e0] px-2 py-1 font-bold border-b border-[#a0a0a0] flex items-center justify-between z-20">
<span>Select Instrument</span> <span>Select Instrument</span>
<button onClick={() => setSynthOpen(false)} className="text-slate-500 hover:text-slate-900"><i className="fa-solid fa-xmark"></i></button> <button onClick={() => setSynthOpen(false)} className="text-slate-500 hover:text-slate-900"><i className="fa-solid fa-xmark"></i></button>
</div> </div>
{/* SEARCH INPUT */}
<div className="sticky top-[23px] bg-white p-1 border-b border-[#c0c0c0] z-20 flex items-center gap-1">
<i className="fa-solid fa-magnifying-glass text-slate-400 pl-1 text-[10px]"></i>
<input
type="text"
placeholder="Tìm nhạc cụ..."
value={synthFilter}
onChange={e => setSynthFilter(e.target.value)}
onClick={e => e.stopPropagation()}
className="w-full px-1 py-0.5 border border-[#c0c0c0] rounded-sm text-xs font-sans focus:outline-none focus:border-blue-500"
/>
{synthFilter && (
<button
onClick={(e) => { e.stopPropagation(); setSynthFilter(''); }}
className="text-slate-400 hover:text-slate-700 px-1"
>
<i className="fa-solid fa-xmark text-[10px]"></i>
</button>
)}
</div>
{synthLoading && <div className="px-2 py-2 text-slate-400 italic">Loading...</div>} {synthLoading && <div className="px-2 py-2 text-slate-400 italic">Loading...</div>}
{!synthLoading && (!synthList || synthList.length === 0) && ( {!synthLoading && (!synthList || synthList.length === 0) && (
<div className="px-2 py-2 text-slate-400 italic">Không SoundFont nào</div> <div className="px-2 py-2 text-slate-400 italic">Không SoundFont nào</div>
@@ -10462,7 +10497,7 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
<div className={`flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-blue-100 ${!synthInst ? 'bg-slate-200' : ''}`} onClick={() => selectSynthInst(null)}> <div className={`flex items-center gap-1 px-2 py-1 cursor-pointer hover:bg-blue-100 ${!synthInst ? 'bg-slate-200' : ''}`} onClick={() => selectSynthInst(null)}>
<i className="fa-solid fa-ban text-slate-400"></i> None (mặc định) <i className="fa-solid fa-ban text-slate-400"></i> None (mặc định)
</div> </div>
{!synthLoading && synthList && synthList.map(group => ( {!synthLoading && filteredSynthList && filteredSynthList.map(group => (
<div key={group.sf.id || group.sf.name}> <div key={group.sf.id || group.sf.name}>
<div className="px-2 py-1 bg-[#f0f0f0] font-semibold text-slate-600 border-t border-[#e0e0e0] truncate">{group.sf.display || group.sf.name || group.sf.id}</div> <div className="px-2 py-1 bg-[#f0f0f0] font-semibold text-slate-600 border-t border-[#e0e0e0] truncate">{group.sf.display || group.sf.name || group.sf.id}</div>
{(group.presets || []).slice(0, 200).map((p, pi) => { {(group.presets || []).slice(0, 200).map((p, pi) => {
@@ -10476,6 +10511,9 @@ const MediaExplorerPanel = ({ height, clipboardRef }) => {
})} })}
</div> </div>
))} ))}
{!synthLoading && filteredSynthList && filteredSynthList.length === 0 && synthFilter && (
<div className="px-2 py-2 text-slate-400 italic">Không tìm thấy nhạc cụ trùng khớp</div>
)}
</div> </div>
)} )}
</div> </div>
File diff suppressed because one or more lines are too long