mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-18 01:10:14 +01:00
feature: ChatGPT style show/hide panel button, panel state saving in local storage (#575)
* Add nav bar state saving to local storage * Add ChatGPT style hide side panel button
This commit is contained in:
parent
88683b9cc5
commit
8e1473c3d8
5 changed files with 61 additions and 44 deletions
|
|
@ -13,7 +13,7 @@ export default function NewChat() {
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
onClick={clickHandler}
|
onClick={clickHandler}
|
||||||
className="mb-2 flex flex-shrink-0 cursor-pointer items-center gap-3 rounded-md border border-white/20 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
className="mb-2 flex flex-grow flex-shrink-0 cursor-pointer items-center gap-3 rounded-md border border-white/20 px-3 py-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,19 @@
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useState, useEffect, useRef, useContext, useCallback } from 'react';
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import NewChat from './NewChat';
|
import { useGetConversationsQuery, useSearchQuery } from '~/data-provider';
|
||||||
import Panel from '../svg/Panel';
|
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||||
import Spinner from '../svg/Spinner';
|
|
||||||
import Pages from '../Conversations/Pages';
|
|
||||||
import Conversations from '../Conversations';
|
import Conversations from '../Conversations';
|
||||||
import NavLinks from './NavLinks';
|
import NavLinks from './NavLinks';
|
||||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
import NewChat from './NewChat';
|
||||||
import { useGetConversationsQuery, useSearchQuery } from '~/data-provider';
|
import Pages from '../Conversations/Pages';
|
||||||
import useDebounce from '~/hooks/useDebounce';
|
import Panel from '../svg/Panel';
|
||||||
|
import Spinner from '../svg/Spinner';
|
||||||
|
// import { ThemeContext } from '~/hooks/ThemeContext';
|
||||||
|
import { cn } from '~/utils/';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
import { useAuthContext } from '~/hooks/AuthContext';
|
import { useAuthContext } from '~/hooks/AuthContext';
|
||||||
import { ThemeContext } from '~/hooks/ThemeContext';
|
import useDebounce from '~/hooks/useDebounce';
|
||||||
import { cn } from '~/utils/';
|
|
||||||
|
|
||||||
// import resolveConfig from 'tailwindcss/resolveConfig';
|
// import resolveConfig from 'tailwindcss/resolveConfig';
|
||||||
// const tailwindConfig = import('../../../tailwind.config.cjs');
|
// const tailwindConfig = import('../../../tailwind.config.cjs');
|
||||||
|
|
@ -37,7 +38,7 @@ import { cn } from '~/utils/';
|
||||||
export default function Nav({ navVisible, setNavVisible }) {
|
export default function Nav({ navVisible, setNavVisible }) {
|
||||||
const [isHovering, setIsHovering] = useState(false);
|
const [isHovering, setIsHovering] = useState(false);
|
||||||
const { isAuthenticated } = useAuthContext();
|
const { isAuthenticated } = useAuthContext();
|
||||||
const { theme, } = useContext(ThemeContext);
|
// const { theme, } = useContext(ThemeContext);
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
const scrollPositionRef = useRef(null);
|
const scrollPositionRef = useRef(null);
|
||||||
|
|
||||||
|
|
@ -165,8 +166,6 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isMobile()) {
|
if (isMobile()) {
|
||||||
setNavVisible(false);
|
setNavVisible(false);
|
||||||
} else {
|
|
||||||
setNavVisible(true);
|
|
||||||
}
|
}
|
||||||
}, [conversationId, setNavVisible]);
|
}, [conversationId, setNavVisible]);
|
||||||
|
|
||||||
|
|
@ -181,10 +180,19 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||||
<div className="flex h-full min-h-0 flex-col ">
|
<div className="flex h-full min-h-0 flex-col ">
|
||||||
<div className="scrollbar-trigger relative flex h-full w-full flex-1 items-start border-white/20">
|
<div className="scrollbar-trigger relative flex h-full w-full flex-1 items-start border-white/20">
|
||||||
<nav className="relative flex h-full flex-1 flex-col space-y-1 p-2">
|
<nav className="relative flex h-full flex-1 flex-col space-y-1 p-2">
|
||||||
|
<div className='flex flex-row'>
|
||||||
<NewChat />
|
<NewChat />
|
||||||
|
<button
|
||||||
|
type='button'
|
||||||
|
className={cn('nav-close-button inline-flex h-11 w-11 border border-white/20 items-center justify-center rounded-md text-white hover:bg-gray-500/10')}
|
||||||
|
onClick={toggleNavVisible}
|
||||||
|
>
|
||||||
|
<span className='sr-only'>Close sidebar</span>
|
||||||
|
<Panel open={false} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
className={`flex-1 flex-col overflow-y-auto ${
|
className={`flex-1 flex-col overflow-y-auto ${isHovering ? '' : 'scrollbar-transparent'
|
||||||
isHovering ? '' : 'scrollbar-transparent'
|
|
||||||
} border-b border-white/20`}
|
} border-b border-white/20`}
|
||||||
onMouseEnter={() => setIsHovering(true)}
|
onMouseEnter={() => setIsHovering(true)}
|
||||||
onMouseLeave={() => setIsHovering(false)}
|
onMouseLeave={() => setIsHovering(false)}
|
||||||
|
|
@ -212,23 +220,17 @@ export default function Nav({ navVisible, setNavVisible }) {
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={cn('nav-close-button -ml-0.5 -mt-2.5 inline-flex h-10 w-10 items-center justify-center rounded-md focus:outline-none focus:ring-white md:-ml-1 md:-mt-2.5', theme === 'dark' ? 'text-gray-500 hover:text-gray-400' : 'text-gray-400 hover:text-gray-500')}
|
|
||||||
onClick={toggleNavVisible}
|
|
||||||
>
|
|
||||||
<span className="sr-only">Close sidebar</span>
|
|
||||||
<Panel/>
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
{!navVisible && (
|
{!navVisible && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="nav-open-button fixed left-2 top-0.5 z-10 inline-flex h-10 w-10 items-center justify-center rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-white dark:text-gray-500 dark:hover:text-gray-400"
|
className="nav-open-button mt-1 fixed left-2 top-0.5 z-10 inline-flex h-10 w-10 items-center justify-center rounded-md border text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-white dark:text-gray-500 dark:hover:text-gray-400"
|
||||||
onClick={toggleNavVisible}
|
onClick={toggleNavVisible}
|
||||||
>
|
>
|
||||||
|
<div className="flex items-center justify-center">
|
||||||
<span className="sr-only">Open sidebar</span>
|
<span className="sr-only">Open sidebar</span>
|
||||||
<Panel open={true}/>
|
<Panel open={true} />
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ export default function Panel({ open = false, className }) {
|
||||||
const openPanel = (
|
const openPanel = (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="20"
|
||||||
height="24"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
@ -23,8 +23,8 @@ export default function Panel({ open = false, className }) {
|
||||||
const closePanel = (
|
const closePanel = (
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="24"
|
width="20"
|
||||||
height="24"
|
height="20"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,13 @@
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-close-button {
|
.nav-open-button {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 100%;
|
}
|
||||||
top: 12px;
|
|
||||||
margin-left: 20px;
|
.nav-close-button {
|
||||||
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
|
|
@ -107,4 +108,9 @@
|
||||||
|
|
||||||
.input-panel {
|
.input-panel {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-open-button
|
||||||
|
{
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,24 @@
|
||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
|
||||||
import MessageHandler from '../components/MessageHandler';
|
|
||||||
import Nav from '../components/Nav';
|
|
||||||
import MobileNav from '../components/Nav/MobileNav';
|
|
||||||
import {
|
import {
|
||||||
useGetSearchEnabledQuery,
|
|
||||||
useGetEndpointsQuery,
|
useGetEndpointsQuery,
|
||||||
useGetPresetsQuery
|
useGetPresetsQuery,
|
||||||
|
useGetSearchEnabledQuery
|
||||||
} from '~/data-provider';
|
} from '~/data-provider';
|
||||||
|
|
||||||
|
import MessageHandler from '../components/MessageHandler';
|
||||||
|
import MobileNav from '../components/Nav/MobileNav';
|
||||||
|
import Nav from '../components/Nav';
|
||||||
|
import { Outlet } from 'react-router-dom';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
import { useSetRecoilState } from 'recoil';
|
|
||||||
import { useAuthContext } from '~/hooks/AuthContext';
|
import { useAuthContext } from '~/hooks/AuthContext';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
export default function Root() {
|
export default function Root() {
|
||||||
const [navVisible, setNavVisible] = useState(false);
|
const [navVisible, setNavVisible] = useState(() => {
|
||||||
|
const savedNavVisible = localStorage.getItem('navVisible');
|
||||||
|
return savedNavVisible !== null ? JSON.parse(savedNavVisible) : false;
|
||||||
|
});
|
||||||
|
|
||||||
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
const setIsSearchEnabled = useSetRecoilState(store.isSearchEnabled);
|
||||||
const setEndpointsConfig = useSetRecoilState(store.endpointsConfig);
|
const setEndpointsConfig = useSetRecoilState(store.endpointsConfig);
|
||||||
|
|
@ -24,6 +29,10 @@ export default function Root() {
|
||||||
const endpointsQuery = useGetEndpointsQuery();
|
const endpointsQuery = useGetEndpointsQuery();
|
||||||
const presetsQuery = useGetPresetsQuery({ enabled: !!user });
|
const presetsQuery = useGetPresetsQuery({ enabled: !!user });
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem('navVisible', JSON.stringify(navVisible));
|
||||||
|
}, [navVisible]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (endpointsQuery.data) {
|
if (endpointsQuery.data) {
|
||||||
setEndpointsConfig(endpointsQuery.data);
|
setEndpointsConfig(endpointsQuery.data);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue