FIX: Media Explorer panel preview nút Synth cho phép lọc nhanh nhạc cụ
This commit is contained in:
+40
-2
@@ -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 có SoundFont nào</div>
|
<div className="px-2 py-2 text-slate-400 italic">Không có 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
Reference in New Issue
Block a user