New UI: transfer App Drawer lists into the Plugged

This commit is contained in:
Enrico Ros
2024-01-02 23:49:21 -08:00
parent 7d5ab95c20
commit 4d807ecf5c
6 changed files with 127 additions and 90 deletions
+4 -4
View File
@@ -23,7 +23,7 @@ import { useOptimaLayout, usePluggableOptimaLayout } from '~/common/layout/optim
import { useUXLabsStore } from '~/common/state/store-ux-labs';
import type { ComposerOutputMultiPart } from './components/composer/composer.types';
import { ChatDrawerItemsMemo } from './components/applayout/ChatDrawerItems';
import { ChatDrawerContentMemo } from './components/applayout/ChatDrawerItems';
import { ChatDropdowns } from './components/applayout/ChatDropdowns';
import { ChatMenuItems } from './components/applayout/ChatMenuItems';
import { ChatMessageList } from './components/ChatMessageList';
@@ -384,9 +384,9 @@ export function AppChat() {
[focusedConversationId, isSplitPane, toggleSplitPane],
);
const drawerItems = React.useMemo(
const drawerContent = React.useMemo(
() => (
<ChatDrawerItemsMemo
<ChatDrawerContentMemo
activeConversationId={focusedConversationId}
disableNewButton={isFocusedChatEmpty}
onConversationActivate={setFocusedConversationId}
@@ -422,7 +422,7 @@ export function AppChat() {
? useFolderStore.getState().folders.find(folder => folder.id === selectedFolderId)?.conversationIds.length || 0
: conversations.length;
usePluggableOptimaLayout(drawerItems, centerItems, menuItems, 'AppChat');
usePluggableOptimaLayout(drawerContent, centerItems, menuItems, 'AppChat');
return (
<>
@@ -1,24 +1,24 @@
import * as React from 'react';
import { shallow } from 'zustand/shallow';
import { Box, ListDivider, ListItemDecorator, MenuItem, Typography } from '@mui/joy';
import AddIcon from '@mui/icons-material/Add';
import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline';
import FileUploadIcon from '@mui/icons-material/FileUpload';
import { DConversationId, useChatStore, useConversationsByFolder } from '~/common/state/store-chats';
import { DConversationId, useConversationsByFolder } from '~/common/state/store-chats';
import { OpenAIIcon } from '~/common/components/icons/OpenAIIcon';
import { PageDrawerList, PageDrawerTallItemSx } from '~/common/layout/optima/components/PageDrawerList';
import { useFolderStore } from '~/common/state/store-folders';
import { useOptimaDrawers } from '~/common/layout/optima/useOptimaDrawers';
import { useUIPreferencesStore } from '~/common/state/store-ui';
import { useUXLabsStore } from '~/common/state/store-ux-labs';
import { useFolderStore } from '~/common/state/store-folders';
import { ChatNavigationItemMemo } from './ChatNavigationItem';
import { ChatFolderList } from './folder/ChatFolderList';
// type ListGrouping = 'off' | 'persona';
export const ChatDrawerItemsMemo = React.memo(ChatDrawerItems);
export const ChatDrawerContentMemo = React.memo(ChatDrawerItems);
function ChatDrawerItems(props: {
activeConversationId: DConversationId | null,
@@ -102,67 +102,72 @@ function ChatDrawerItems(props: {
{/*</ListItem>*/}
<ChatFolderList
onFolderSelect={handleFolderSelect}
folders={useFolderStore((state) => state.folders)}
selectedFolderId={selectedFolderId}
conversationsByFolder={conversations}
/>
<ListDivider sx={{ mb: 0 }} />
onFolderSelect={handleFolderSelect}
folders={useFolderStore((state) => state.folders)}
selectedFolderId={selectedFolderId}
conversationsByFolder={conversations}
/>
<MenuItem disabled={props.disableNewButton} onClick={handleButtonNew}>
<ListItemDecorator><AddIcon /></ListItemDecorator>
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'space-between', gap: 1 }}>
New
{/*<KeyStroke combo='Ctrl + Alt + N' />*/}
<ListDivider />
<PageDrawerList noTopPadding noBottomPadding tallRows>
<MenuItem disabled={props.disableNewButton} onClick={handleButtonNew} sx={PageDrawerTallItemSx}>
<ListItemDecorator><AddIcon /></ListItemDecorator>
<Box sx={{ flexGrow: 1, display: 'flex', justifyContent: 'space-between', gap: 1 }}>
New
{/*<KeyStroke combo='Ctrl + Alt + N' />*/}
</Box>
</MenuItem>
<ListDivider sx={{ mt: 0 }} />
<Box sx={{ flex: 1, overflowY: 'auto' }}>
{/*<ListItem sticky sx={{ justifyContent: 'space-between', boxShadow: 'sm' }}>*/}
{/* <Typography level='body-sm'>*/}
{/* Conversations*/}
{/* </Typography>*/}
{/* <ToggleButtonGroup variant='soft' size='sm' value={grouping} onChange={(_event, newValue) => newValue && setGrouping(newValue)}>*/}
{/* <IconButton value='off'>*/}
{/* <AccessTimeIcon />*/}
{/* </IconButton>*/}
{/* <IconButton value='persona'>*/}
{/* <PersonIcon />*/}
{/* </IconButton>*/}
{/* </ToggleButtonGroup>*/}
{/*</ListItem>*/}
{conversations.map(conversation =>
<ChatNavigationItemMemo
key={'nav-' + conversation.id}
conversation={conversation}
isActive={conversation.id === props.activeConversationId}
isLonely={singleChat}
maxChatMessages={(labsEnhancedUI || softMaxReached) ? maxChatMessages : 0}
showSymbols={showSymbols}
onConversationActivate={handleConversationActivate}
onConversationDelete={handleConversationDelete}
/>)}
</Box>
</MenuItem>
<ListDivider sx={{ mb: 0 }} />
<ListDivider sx={{ mt: 0 }} />
<Box sx={{ flex: 1, overflowY: 'auto' }}>
{/*<ListItem sticky sx={{ justifyContent: 'space-between', boxShadow: 'sm' }}>*/}
{/* <Typography level='body-sm'>*/}
{/* Conversations*/}
{/* </Typography>*/}
{/* <ToggleButtonGroup variant='soft' size='sm' value={grouping} onChange={(_event, newValue) => newValue && setGrouping(newValue)}>*/}
{/* <IconButton value='off'>*/}
{/* <AccessTimeIcon />*/}
{/* </IconButton>*/}
{/* <IconButton value='persona'>*/}
{/* <PersonIcon />*/}
{/* </IconButton>*/}
{/* </ToggleButtonGroup>*/}
{/*</ListItem>*/}
<MenuItem onClick={props.onConversationImportDialog}>
<ListItemDecorator>
<FileUploadIcon />
</ListItemDecorator>
Import chats
<OpenAIIcon sx={{ fontSize: 'xl', ml: 'auto' }} />
</MenuItem>
{conversations.map(conversation =>
<ChatNavigationItemMemo
key={'nav-' + conversation.id}
conversation={conversation}
isActive={conversation.id === props.activeConversationId}
isLonely={singleChat}
maxChatMessages={(labsEnhancedUI || softMaxReached) ? maxChatMessages : 0}
showSymbols={showSymbols}
onConversationActivate={handleConversationActivate}
onConversationDelete={handleConversationDelete}
/>)}
</Box>
<MenuItem disabled={!hasChats} onClick={() => props.onConversationsDeleteAll(selectedFolderId)}>
<ListItemDecorator><DeleteOutlineIcon /></ListItemDecorator>
<Typography>
Delete {totalConversations >= 2 ? `all ${totalConversations} chats` : 'chat'}
</Typography>
</MenuItem>
<ListDivider sx={{ mt: 0 }} />
<MenuItem onClick={props.onConversationImportDialog}>
<ListItemDecorator>
<FileUploadIcon />
</ListItemDecorator>
Import chats
<OpenAIIcon sx={{ fontSize: 'xl', ml: 'auto' }} />
</MenuItem>
<MenuItem disabled={!hasChats} onClick={() => props.onConversationsDeleteAll(selectedFolderId)}>
<ListItemDecorator><DeleteOutlineIcon /></ListItemDecorator>
<Typography>
Delete {totalConversations >= 2 ? `all ${totalConversations} chats` : 'chat'}
</Typography>
</MenuItem>
</PageDrawerList>
</>;
}
+3 -3
View File
@@ -16,7 +16,7 @@ import { conversationTitle } from '~/common/state/store-chats';
import { themeBgAppDarker } from '~/common/app.theme';
import { usePluggableOptimaLayout } from '~/common/layout/optima/useOptimaLayout';
import { AppChatLinkDrawerItems } from './AppChatLinkDrawerItems';
import { AppChatLinkDrawerContent } from './AppChatLinkDrawerContent';
import { AppChatLinkMenuItems } from './AppChatLinkMenuItems';
import { ViewChatLink } from './ViewChatLink';
@@ -84,9 +84,9 @@ export function AppChatLink(props: { linkId: string }) {
// pluggable UI
const drawerItems = React.useMemo(() => <AppChatLinkDrawerItems />, []);
const drawerContent = React.useMemo(() => <AppChatLinkDrawerContent />, []);
const menuItems = React.useMemo(() => <AppChatLinkMenuItems />, []);
usePluggableOptimaLayout(hasLinkItems ? drawerItems : null, null, menuItems, 'AppChatLink');
usePluggableOptimaLayout(hasLinkItems ? drawerContent : null, null, menuItems, 'AppChatLink');
const pageTitle = (data?.conversation && conversationTitle(data.conversation)) || 'Chat Link';
@@ -10,13 +10,14 @@ import { Brand } from '~/common/app.config';
import { Link } from '~/common/components/Link';
import { getChatLinkRelativePath, ROUTE_INDEX } from '~/common/app.routes';
import { useOptimaDrawers } from '~/common/layout/optima/useOptimaDrawers';
import { PageDrawerList } from '~/common/layout/optima/components/PageDrawerList';
/**
* Drawer Items are all the links already shared, for quick access.
* This is stores in the Trade Store (local storage).
*/
export function AppChatLinkDrawerItems() {
export function AppChatLinkDrawerContent() {
// external state
const { closeDrawerOnMobile } = useOptimaDrawers();
@@ -25,7 +26,7 @@ export function AppChatLinkDrawerItems() {
.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
const notEmpty = chatLinkItems.length > 0;
return <>
return <PageDrawerList>
<MenuItem
onClick={closeDrawerOnMobile}
@@ -64,6 +65,6 @@ export function AppChatLinkDrawerItems() {
))}
</Box>}
</>;
</PageDrawerList>;
}
+4 -21
View File
@@ -1,6 +1,6 @@
import * as React from 'react';
import { IconButton, MenuList, Sheet, Typography } from '@mui/joy';
import { IconButton, Sheet, Typography } from '@mui/joy';
import CloseIcon from '@mui/icons-material/Close';
import type { NavItemApp } from '~/common/app.nav';
@@ -39,25 +39,8 @@ export function PageDrawer(props: {
</IconButton>
</Sheet>
{/* Pluggable content (Pane) */}
<MenuList
variant='plain'
// variant={props.variant} color={props.color}
// onKeyDown={handleListKeyDown}
sx={{
'--Icon-fontSize': 'var(--joy-fontSize-xl2)',
'--ListItem-minHeight': /*props.dense*/ false ? '2.5rem' : '3rem',
'--ListItemDecorator-size': '2.75rem', // icon width
backgroundColor: 'background.popup',
boxShadow: 'md',
border: 'none',
// ...(props.maxHeightGapPx !== undefined ? { maxHeight: `calc(100dvh - ${props.maxHeightGapPx}px)`, overflowY: 'auto' } : {}),
// ...(props.noTopPadding ? { pt: 0 } : {}),
// ...(props.noBottomPadding ? { pb: 0 } : {}),
// ...(props.sx || {}),
}}
>
{props.children}
</MenuList>
{/* Pluggable Drawer Content */}
{props.children}
</>;
}
@@ -0,0 +1,48 @@
import * as React from 'react';
import { ColorPaletteProp, MenuList, VariantProp } from '@mui/joy';
import { SxProps } from '@mui/joy/styles/types';
export const PageDrawerTallItemSx: SxProps = {
'--ListItem-minHeight': '3rem',
};
/**
* Used by pluggable layouts to have a standardized list appearance
*/
export function PageDrawerList(props: {
variant?: VariantProp,
color?: ColorPaletteProp,
largeIcons?: boolean,
tallRows?: boolean,
noTopPadding?: boolean,
noBottomPadding?: boolean,
children: React.ReactNode
}) {
return (
<MenuList
variant={props.variant}
color={props.color}
sx={{
// size of the list items
...props.largeIcons && {
'--Icon-fontSize': 'var(--joy-fontSize-xl2)',
'--ListItemDecorator-size': '2.75rem', // icon width
},
...props.tallRows && PageDrawerTallItemSx,
// style
backgroundColor: 'background.popup',
border: 'none',
boxShadow: 'md',
...(!!props.noTopPadding && { pt: 0 }),
...(!!props.noBottomPadding && { pb: 0 }),
}}
>
{props.children}
</MenuList>
);
}