replace react-resizable-panels with react-split-pane

- react-resizable-panels was causing an issue with browser navigation
This commit is contained in:
jeffvli
2025-12-27 00:55:38 -08:00
parent 796e511626
commit f0d22267c3
6 changed files with 90 additions and 45 deletions
+1 -1
View File
@@ -122,8 +122,8 @@
"react-image": "^4.1.0", "react-image": "^4.1.0",
"react-loading-skeleton": "^3.5.0", "react-loading-skeleton": "^3.5.0",
"react-player": "^2.16.0", "react-player": "^2.16.0",
"react-resizable-panels": "^4.0.15",
"react-router": "^7.9.6", "react-router": "^7.9.6",
"react-split-pane": "^3.0.4",
"react-virtualized-auto-sizer": "^1.0.26", "react-virtualized-auto-sizer": "^1.0.26",
"react-window": "1.8.11", "react-window": "1.8.11",
"react-window-v2": "npm:react-window@^2.2.3", "react-window-v2": "npm:react-window@^2.2.3",
+15 -14
View File
@@ -191,12 +191,12 @@ importers:
react-player: react-player:
specifier: ^2.16.0 specifier: ^2.16.0
version: 2.16.0(react@19.1.0) version: 2.16.0(react@19.1.0)
react-resizable-panels:
specifier: ^4.0.15
version: 4.0.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-router: react-router:
specifier: ^7.9.6 specifier: ^7.9.6
version: 7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 7.9.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-split-pane:
specifier: ^3.0.4
version: 3.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-virtualized-auto-sizer: react-virtualized-auto-sizer:
specifier: ^1.0.26 specifier: ^1.0.26
version: 1.0.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 1.0.26(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -4619,12 +4619,6 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
react-resizable-panels@4.0.15:
resolution: {integrity: sha512-+ygM/EI2h4Qc/cl2fasQ2qwOgNfpQwXLNTU5PqhhPerliX+wnbf7ejcqran7lz3BqABzjddf0pJ3j3G/+A0v9Q==}
peerDependencies:
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
react-router-dom@7.9.4: react-router-dom@7.9.4:
resolution: {integrity: sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==} resolution: {integrity: sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==}
engines: {node: '>=20.0.0'} engines: {node: '>=20.0.0'}
@@ -4652,6 +4646,13 @@ packages:
react-dom: react-dom:
optional: true optional: true
react-split-pane@3.0.4:
resolution: {integrity: sha512-+QNayN8lsYhT87z0bH5yAuUocoqHlc3AQnw/+pGXMH2kG2+mSfNAR4fHhEdmweHLFjIyX811hh9sgCkiHXCYag==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
react-style-singleton@2.2.3: react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -10618,11 +10619,6 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 19.2.5 '@types/react': 19.2.5
react-resizable-panels@4.0.15(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0): react-router-dom@7.9.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies: dependencies:
react: 19.1.0 react: 19.1.0
@@ -10647,6 +10643,11 @@ snapshots:
optionalDependencies: optionalDependencies:
react-dom: 19.1.0(react@19.1.0) react-dom: 19.1.0(react@19.1.0)
react-split-pane@3.0.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-style-singleton@2.2.3(@types/react@19.2.5)(react@19.1.0): react-style-singleton@2.2.3(@types/react@19.2.5)(react@19.1.0):
dependencies: dependencies:
get-nonce: 1.0.1 get-nonce: 1.0.1
@@ -45,6 +45,7 @@
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
min-height: 0; min-height: 0;
max-height: 100%;
overflow: hidden; overflow: hidden;
background: var(--theme-colors-background); background: var(--theme-colors-background);
background-color: var(--theme-colors-background-alternate); background-color: var(--theme-colors-background-alternate);
@@ -54,12 +55,19 @@
position: relative; position: relative;
flex-shrink: 0; flex-shrink: 0;
width: 100%; width: 100%;
height: 1px; height: 4px;
cursor: row-resize; cursor: row-resize;
background-color: var(--theme-colors-border);
transition: &:before {
background-color 0.2s ease, position: absolute;
height 0.2s ease; top: 0;
left: 0;
width: 100%;
height: 1px;
background: var(--theme-colors-border);
content: '';
transition: opacity 0.2s ease;
}
} }
.panel-reorder-controls { .panel-reorder-controls {
@@ -1,7 +1,8 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { lazy, Suspense, useCallback, useMemo, useRef, useState } from 'react'; import { lazy, Suspense, useCallback, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Group, Panel, Separator, useDefaultLayout } from 'react-resizable-panels'; // import { Group, Panel, Separator, useDefaultLayout } from 'react-resizable-panels';
import { Pane, SplitPane, usePersistence } from 'react-split-pane';
import styles from './sidebar-play-queue.module.css'; import styles from './sidebar-play-queue.module.css';
@@ -51,8 +52,8 @@ export const SidebarPlayQueue = () => {
const showPanel = showLyricsInSidebar || showVisualizer; const showPanel = showLyricsInSidebar || showVisualizer;
// Persist the layout of the sidebar play queue container // Persist the layout of the sidebar play queue container
const { defaultLayout, onLayoutChange } = useDefaultLayout({ const [defaultLayout, onLayoutChange] = usePersistence({
id: 'sidebar-play-queue-container', key: 'sidebar-play-queue-container',
storage: localStorage, storage: localStorage,
}); });
@@ -78,12 +79,21 @@ export const SidebarPlayQueue = () => {
return visiblePanels; return visiblePanels;
}, [combinedLyricsAndVisualizer, showLyricsInSidebar, showVisualizer, sidebarPanelOrder]); }, [combinedLyricsAndVisualizer, showLyricsInSidebar, showVisualizer, sidebarPanelOrder]);
const renderPanel = (panelType: SidebarPanelType, index: number, totalPanels: number) => { const renderPanel = (panelType: SidebarPanelType, _index: number, totalPanels: number) => {
if (panelType === 'queue') { if (panelType === 'queue') {
return ( return (
<> <>
{index > 0 && <Separator className={styles.resizeHandle} />} <Pane
<Panel defaultSize={50} id="queue" key="queue" minSize={20}> defaultSize={50}
key="queue"
minSize={20}
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
overflow: 'hidden',
}}
>
<div className={styles.playQueueSection}> <div className={styles.playQueueSection}>
<PlayQueue <PlayQueue
listKey={ItemListKey.SIDE_QUEUE} listKey={ItemListKey.SIDE_QUEUE}
@@ -91,7 +101,7 @@ export const SidebarPlayQueue = () => {
searchTerm={search} searchTerm={search}
/> />
</div> </div>
</Panel> </Pane>
</> </>
); );
} }
@@ -99,15 +109,20 @@ export const SidebarPlayQueue = () => {
if (combinedLyricsAndVisualizer && (panelType === 'lyrics' || panelType === 'visualizer')) { if (combinedLyricsAndVisualizer && (panelType === 'lyrics' || panelType === 'visualizer')) {
return ( return (
<> <>
{index > 0 && <Separator className={styles.resizeHandle} />} <Pane
<Panel
defaultSize={totalPanels > 2 ? 25 : 50} defaultSize={totalPanels > 2 ? 25 : 50}
id="combined"
key="combined" key="combined"
minSize={20} minSize={20}
size={defaultLayout[0]}
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
overflow: 'hidden',
}}
> >
<CombinedLyricsAndVisualizerPanel /> <CombinedLyricsAndVisualizerPanel />
</Panel> </Pane>
</> </>
); );
} }
@@ -115,15 +130,20 @@ export const SidebarPlayQueue = () => {
if (panelType === 'lyrics') { if (panelType === 'lyrics') {
return ( return (
<> <>
{index > 0 && <Separator className={styles.resizeHandle} />} <Pane
<Panel
defaultSize={totalPanels > 2 ? 25 : 50} defaultSize={totalPanels > 2 ? 25 : 50}
id="lyrics"
key="lyrics" key="lyrics"
minSize={15} minSize={15}
size={defaultLayout[1]}
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
overflow: 'hidden',
}}
> >
<LyricsPanel /> <LyricsPanel />
</Panel> </Pane>
</> </>
); );
} }
@@ -131,15 +151,20 @@ export const SidebarPlayQueue = () => {
if (panelType === 'visualizer') { if (panelType === 'visualizer') {
return ( return (
<> <>
{index > 0 && <Separator className={styles.resizeHandle} />} <Pane
<Panel
defaultSize={totalPanels > 2 ? 25 : 50} defaultSize={totalPanels > 2 ? 25 : 50}
id="visualizer"
key="visualizer" key="visualizer"
minSize={15} minSize={15}
size={defaultLayout[2]}
style={{
display: 'flex',
flexDirection: 'column',
height: '100%',
overflow: 'hidden',
}}
> >
<VisualizerPanel /> <VisualizerPanel />
</Panel> </Pane>
</> </>
); );
} }
@@ -155,16 +180,16 @@ export const SidebarPlayQueue = () => {
type={ItemListKey.SIDE_QUEUE} type={ItemListKey.SIDE_QUEUE}
/> />
{showPanel ? ( {showPanel ? (
<Group <SplitPane
defaultLayout={defaultLayout} direction="vertical"
onLayoutChange={onLayoutChange} dividerClassName={styles.resizeHandle}
orientation="vertical" onResize={onLayoutChange}
style={{ flex: 1, minHeight: 0 }} style={{ flex: 1, height: '100%', minHeight: 0 }}
> >
{orderedPanels.map((panel, index) => {orderedPanels.map((panel, index) =>
renderPanel(panel, index, orderedPanels.length), renderPanel(panel, index, orderedPanels.length),
)} )}
</Group> </SplitPane>
) : ( ) : (
<Flex direction="column" style={{ flex: 1, minHeight: 0 }}> <Flex direction="column" style={{ flex: 1, minHeight: 0 }}>
<div className={styles.playQueueSection}> <div className={styles.playQueueSection}>
@@ -3,10 +3,14 @@
z-index: 50; z-index: 50;
width: 100%; width: 100%;
height: 100%; height: 100%;
max-height: 100%;
margin: auto; margin: auto;
overflow: hidden;
canvas { canvas {
width: 100%; width: 100%;
max-width: 100%;
max-height: 100%;
margin: auto; margin: auto;
} }
@@ -26,4 +30,7 @@
.visualizer { .visualizer {
width: 100%; width: 100%;
height: 100%; height: 100%;
max-width: 100%;
max-height: 100%;
overflow: hidden;
} }
@@ -3,7 +3,9 @@
z-index: 50; z-index: 50;
width: 100%; width: 100%;
height: 100%; height: 100%;
max-height: 100%;
margin: auto; margin: auto;
overflow: hidden;
&:hover { &:hover {
.settings-icon { .settings-icon {
@@ -21,6 +23,8 @@
display: block; display: block;
width: 100%; width: 100%;
height: 100%; height: 100%;
max-width: 100%;
max-height: 100%;
} }
.preset-overlay { .preset-overlay {