mirror of
https://github.com/itsRevela/Revelations-Launcher.git
synced 2026-05-21 18:54:29 +00:00
fix(ui): clear button hover highlight when mouse leaves
Views set focus state on onMouseEnter but had no matching onMouseLeave, so the last-hovered button stayed highlighted until the mouse entered a different one or keyboard navigation moved focus. Add onMouseLeave handlers across VersionsView rows and action buttons, SettingsView / ThemesView / SkinsView menu items, and the Import Fork / Import Instance modals. For the views with nullable focusIndex (number | null) the leave handler sets null; for VersionsView and the modals (plain number) it uses -1 as a no-focus sentinel that never matches a valid index. For VersionsView specifically: - Row-level onMouseLeave clears both focusRow and focusCol to -1; per-button onMouseLeave inside a row resets focusCol to 0 so the row stays highlighted while the mouse is over it but outside any action button. - Guard the Enter key handler with focusRow >= 0 so a cleared focus state doesn't crash editions[-1] lookups.
This commit is contained in:
@@ -151,6 +151,7 @@ export default function CustomTUModal({
|
||||
<div className="flex gap-4 mt-8 w-full">
|
||||
<button
|
||||
onMouseEnter={() => setFocusIndex(3)}
|
||||
onMouseLeave={() => setFocusIndex(-1)}
|
||||
onClick={() => {
|
||||
playSfx("close_click.wav");
|
||||
onClose();
|
||||
@@ -166,6 +167,7 @@ export default function CustomTUModal({
|
||||
</button>
|
||||
<button
|
||||
onMouseEnter={() => setFocusIndex(4)}
|
||||
onMouseLeave={() => setFocusIndex(-1)}
|
||||
onClick={() => {
|
||||
playSfx("save_click.wav");
|
||||
handleImport();
|
||||
|
||||
@@ -142,6 +142,7 @@ export default function ImportInstanceModal({
|
||||
<div className="flex flex-col gap-3 mt-8 w-full items-center">
|
||||
<button
|
||||
onMouseEnter={() => setFocusIndex(3)}
|
||||
onMouseLeave={() => setFocusIndex(-1)}
|
||||
onClick={handleImport}
|
||||
className={`w-full h-12 flex items-center justify-center text-xl mc-text-shadow transition-all outline-none border-none bg-transparent ${focusIndex === 3 ? "text-[#FFFF55]" : "text-white"}`}
|
||||
style={{
|
||||
@@ -156,6 +157,7 @@ export default function ImportInstanceModal({
|
||||
<div className="flex gap-4 w-full">
|
||||
<button
|
||||
onMouseEnter={() => setFocusIndex(4)}
|
||||
onMouseLeave={() => setFocusIndex(-1)}
|
||||
onClick={() => {
|
||||
playSfx("close_click.wav");
|
||||
onClose();
|
||||
|
||||
@@ -550,6 +550,7 @@ const SettingsView = memo(function SettingsView() {
|
||||
data-index={index}
|
||||
tabIndex={0}
|
||||
onMouseEnter={() => setFocusIndex(index)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
className="relative w-[360px] h-10 flex items-center justify-center cursor-pointer transition-all outline-none border-none hover:text-[#FFFF55] shrink-0"
|
||||
style={getSliderStyle(index)}
|
||||
>
|
||||
@@ -582,6 +583,7 @@ const SettingsView = memo(function SettingsView() {
|
||||
key={item.id}
|
||||
data-index={index}
|
||||
onMouseEnter={() => setFocusIndex(index)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={item.onClick}
|
||||
className={`w-[360px] h-10 flex items-center justify-center px-4 relative z-30 transition-colors outline-none border-none shrink-0 ${isRed
|
||||
? focusIndex === index
|
||||
@@ -612,6 +614,7 @@ const SettingsView = memo(function SettingsView() {
|
||||
<button
|
||||
data-index={backIndex}
|
||||
onMouseEnter={() => setFocusIndex(backIndex)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={backItem.onClick}
|
||||
className={`w-72 h-10 flex items-center justify-center transition-colors text-xl mc-text-shadow outline-none border-none hover:text-[#FFFF55] ${focusIndex === backIndex ? "text-[#FFFF55]" : "text-white"
|
||||
}`}
|
||||
|
||||
@@ -291,6 +291,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
<button
|
||||
data-index="0"
|
||||
onMouseEnter={() => setFocusIndex(0)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={handleImportClick}
|
||||
className={`w-40 h-10 flex items-center justify-center transition-colors text-2xl mc-text-shadow outline-none border-none hover:text-[#FFFF55] ${focusIndex === 0 ? 'text-[#FFFF55]' : 'text-white'}`}
|
||||
style={{ backgroundImage: focusIndex === 0 ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')", backgroundSize: '100% 100%', imageRendering: 'pixelated' }}
|
||||
@@ -301,6 +302,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
<button
|
||||
data-index="1"
|
||||
onMouseEnter={() => !isActiveDefault && setFocusIndex(1)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={handleDeleteActive}
|
||||
className={`w-40 h-10 flex items-center justify-center transition-colors text-2xl mc-text-shadow outline-none border-none ${isActiveDefault ? 'text-gray-400 opacity-80 cursor-not-allowed' : (focusIndex === 1 ? 'text-[#FFFF55]' : 'text-white')}`}
|
||||
style={{
|
||||
@@ -315,6 +317,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
<button
|
||||
data-index="2"
|
||||
onMouseEnter={() => setFocusIndex(2)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={() => handleToggleModel()}
|
||||
className={`w-40 h-10 flex items-center justify-center transition-colors text-2xl mc-text-shadow outline-none border-none hover:text-[#FFFF55] ${focusIndex === 2 ? 'text-[#FFFF55]' : 'text-white'}`}
|
||||
style={{ backgroundImage: focusIndex === 2 ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')", backgroundSize: '100% 100%', imageRendering: 'pixelated' }}
|
||||
@@ -328,6 +331,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
<button
|
||||
data-index="3"
|
||||
onMouseEnter={() => setFocusIndex(3)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={() => { playClickSound(); TauriService.openSkinsFolder().catch(() => { }); }}
|
||||
className={`mc-sq-btn w-10 h-10 flex items-center justify-center outline-none border-none transition-all`}
|
||||
style={{ backgroundImage: focusIndex === 3 ? "url('/images/Button_Square_Highlighted.png')" : "url('/images/Button_Square.png')", backgroundSize: '100% 100%', imageRendering: 'pixelated' }}
|
||||
@@ -345,7 +349,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
const isActive = activeSkinId ? activeSkinId === skin.id : skinUrl === skin.url;
|
||||
const isFocused = focusIndex === idx;
|
||||
return (
|
||||
<div key={skin.id} data-index={idx} tabIndex={0} onMouseEnter={() => setFocusIndex(idx)} className="flex flex-col items-center gap-1 w-32 outline-none">
|
||||
<div key={skin.id} data-index={idx} tabIndex={0} onMouseEnter={() => setFocusIndex(idx)} onMouseLeave={() => setFocusIndex(null)} className="flex flex-col items-center gap-1 w-32 outline-none">
|
||||
<div className="h-4">
|
||||
{isActive && <span className="text-[#FFFF55] text-xs mc-text-shadow uppercase tracking-widest">Active</span>}
|
||||
</div>
|
||||
@@ -371,6 +375,7 @@ const SkinsView = memo(function SkinsView() {
|
||||
<button
|
||||
data-index={BACK_BUTTON_INDEX}
|
||||
onMouseEnter={() => setFocusIndex(BACK_BUTTON_INDEX)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={() => { playBackSound(); setActiveView('main'); }}
|
||||
className={`w-72 h-14 flex items-center justify-center transition-colors text-2xl mc-text-shadow mt-2 outline-none border-none hover:text-[#FFFF55] ${focusIndex === BACK_BUTTON_INDEX ? 'text-[#FFFF55]' : 'text-white'}`}
|
||||
style={{ backgroundImage: focusIndex === BACK_BUTTON_INDEX ? "url('/images/button_highlighted.png')" : "url('/images/Button_Background.png')", backgroundSize: '100% 100%', imageRendering: 'pixelated' }}
|
||||
|
||||
@@ -111,6 +111,7 @@ const ThemesView = memo(function ThemesView() {
|
||||
<button
|
||||
data-index="0"
|
||||
onMouseEnter={() => setFocusIndex(0)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={() => {
|
||||
playClickSound();
|
||||
const currentIndex = totalPalettes.indexOf(currentTheme);
|
||||
@@ -128,6 +129,7 @@ const ThemesView = memo(function ThemesView() {
|
||||
<button
|
||||
data-index="1"
|
||||
onMouseEnter={() => setFocusIndex(1)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={handleImport}
|
||||
className={`w-72 h-12 flex items-center justify-center px-4 relative transition-colors outline-none border-none hover:text-[#FFFF55] ${focusIndex === 1 ? "text-[#FFFF55]" : "text-white"}`}
|
||||
style={getItemStyle(1)}
|
||||
@@ -141,6 +143,7 @@ const ThemesView = memo(function ThemesView() {
|
||||
<button
|
||||
data-index="2"
|
||||
onMouseEnter={() => setFocusIndex(2)}
|
||||
onMouseLeave={() => setFocusIndex(null)}
|
||||
onClick={() => {
|
||||
playBackSound();
|
||||
setActiveView("main");
|
||||
|
||||
@@ -66,7 +66,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusCol((prev) => (prev > 0 ? prev - 1 : prev));
|
||||
}
|
||||
} else if (e.key === "Enter") {
|
||||
if (focusRow < editions.length) {
|
||||
if (focusRow >= 0 && focusRow < editions.length) {
|
||||
const edition = editions[focusRow];
|
||||
const isInstalled = installedVersions.includes(edition.id);
|
||||
const isCustom = edition.id.startsWith("custom_") || edition.id.startsWith("instance_");
|
||||
@@ -192,6 +192,10 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusCol(0);
|
||||
}
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setFocusRow(-1);
|
||||
setFocusCol(-1);
|
||||
}}
|
||||
onClick={() => {
|
||||
if (!isPlaceholder && isInstalled) {
|
||||
playClickSound();
|
||||
@@ -273,6 +277,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(-1);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playClickSound();
|
||||
@@ -328,6 +333,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(1);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (!downloadingId) {
|
||||
@@ -375,6 +381,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(2);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playClickSound();
|
||||
@@ -407,6 +414,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(3);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playBackSound();
|
||||
@@ -454,6 +462,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(isInstalled ? 4 : 2);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (hasCustomImage) {
|
||||
@@ -520,6 +529,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(isInstalled ? 5 : 3);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playClickSound();
|
||||
@@ -559,6 +569,7 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(i);
|
||||
setFocusCol(isInstalled ? 6 : 4);
|
||||
}}
|
||||
onMouseLeave={() => setFocusCol(0)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
playBackSound();
|
||||
@@ -608,6 +619,10 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(editions.length);
|
||||
setFocusCol(0);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setFocusRow(-1);
|
||||
setFocusCol(-1);
|
||||
}}
|
||||
onClick={() => {
|
||||
playClickSound();
|
||||
setIsImportModalOpen(true);
|
||||
@@ -632,6 +647,10 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(editions.length + 1);
|
||||
setFocusCol(0);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setFocusRow(-1);
|
||||
setFocusCol(-1);
|
||||
}}
|
||||
onClick={() => {
|
||||
playClickSound();
|
||||
setIsInstanceModalOpen(true);
|
||||
@@ -656,6 +675,10 @@ const VersionsView = memo(function VersionsView() {
|
||||
setFocusRow(editions.length + 2);
|
||||
setFocusCol(0);
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setFocusRow(-1);
|
||||
setFocusCol(-1);
|
||||
}}
|
||||
onClick={() => {
|
||||
playBackSound();
|
||||
setActiveView("main");
|
||||
|
||||
Reference in New Issue
Block a user