Skip to content

Commit

Permalink
AI Chat conversations list item menu and edit improvements
Browse files Browse the repository at this point in the history
- ButtonMenu can open without being tiny and scrolling
- make sure edit isn't blocked by the link activation
- storybook get edit ability
  • Loading branch information
petemill committed Dec 13, 2024
1 parent a3df109 commit 0c5cf4b
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,46 +62,48 @@ interface DisplayTitleProps {
}

function DisplayTitle(props: DisplayTitleProps) {
const [isButtonMenuVisible, setIsButtonMenuVisible] = React.useState(false)
const [isOptionsMenuOpen, setIsOptionsMenuOpen] = React.useState(false)

const handleButtonMenuChange = React.useCallback((e: {isOpen: boolean}) => {
setIsOptionsMenuOpen(e.isOpen)
}, [])

return (
<div
className={styles.displayTitle}
onMouseEnter={() => setIsButtonMenuVisible(true)}
onMouseLeave={() => setIsButtonMenuVisible(false)}
className={classnames(styles.displayTitle, isOptionsMenuOpen && styles.isOptionsMenuOpen)}
>
<div className={styles.displayTitleContent}>
<div
className={styles.text}
onDoubleClick={props.onEditTitle}
title={props.title}
>
{props.title}
</div>
<div className={styles.description}>{props.description}</div>
</div>
{isButtonMenuVisible && (
<ButtonMenu className={styles.optionsMenu}>
<div
slot='anchor-content'
className={styles.optionsButton}
>
<Icon name='more-vertical' />
<ButtonMenu
className={styles.optionsMenu}
onChange={handleButtonMenuChange}
>
<div
slot='anchor-content'
className={styles.optionsButton}
>
<Icon name='more-vertical' />
</div>
<leo-menu-item onClick={props.onEditTitle}>
<div className={styles.optionsMenuItemWithIcon}>
<Icon name='edit-pencil' />
<div>{getLocale('menuRenameConversation')}</div>
</div>
<leo-menu-item onClick={props.onEditTitle}>
<div className={styles.optionsMenuItemWithIcon}>
<Icon name='edit-pencil' />
<div>{getLocale('menuRenameConversation')}</div>
</div>
</leo-menu-item>
<leo-menu-item onClick={props.onDelete}>
<div className={styles.optionsMenuItemWithIcon}>
<Icon name='trash' />
<div>{getLocale('menuDeleteConversation')}</div>
</div>
</leo-menu-item>
</ButtonMenu>
)}
</leo-menu-item>
<leo-menu-item onClick={props.onDelete}>
<div className={styles.optionsMenuItemWithIcon}>
<Icon name='trash' />
<div>{getLocale('menuDeleteConversation')}</div>
</div>
</leo-menu-item>
</ButtonMenu>
</div>
)
}
Expand Down Expand Up @@ -140,19 +142,30 @@ export default function ConversationsList(props: ConversationsListProps) {
{aiChatContext.visibleConversations.length > 0 &&
<ol>
{aiChatContext.visibleConversations.map(item => {
const isEditing = aiChatContext.editingConversationId === item.uuid
return (
<li key={item.uuid}>
<a
className={classnames({
[styles.navItem]: true,
[styles.navItemActive]: item.uuid === conversationContext.conversationUuid
})}
onClick={() => {
onClick={(e) => {
if (isEditing) {
e.preventDefault()
}
props.setIsConversationsListOpen?.(false)
}}
onDoubleClick={() => aiChatContext.setEditingConversationId(item.uuid)}
href={`/${item.uuid}`}
>
{item.uuid === aiChatContext.editingConversationId ? (
<DisplayTitle
title={item.title || getLocale('conversationListUntitled')}
description=''
onEditTitle={() => aiChatContext.setEditingConversationId(item.uuid)}
onDelete={() => getAPI().service.deleteConversation(item.uuid)}
/>
{item.uuid === aiChatContext.editingConversationId && (
<div className={styles.editibleTitle}>
<SimpleInput
text={item.title}
Expand All @@ -163,13 +176,6 @@ export default function ConversationsList(props: ConversationsListProps) {
}}
/>
</div>
) : (
<DisplayTitle
title={item.title || getLocale('conversationListUntitled')}
description=''
onEditTitle={() => aiChatContext.setEditingConversationId(item.uuid)}
onDelete={() => getAPI().service.deleteConversation(item.uuid)}
/>
)}
</a>
</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

.navItem {
all: unset;
position: relative;
border-radius: var(--leo-radius-m);
display: block;
font: var(--leo-font-default-regular);
Expand Down Expand Up @@ -65,6 +66,10 @@
transition: all 0.2s ease-out allow-discrete;
width: 100%;

&:not(.isOptionsMenuOpen,:hover) .optionsMenu {
visibility: hidden;
}

&:hover {
background: var(--leo-color-container-highlight);
box-shadow: var(--leo-effect-elevation-01);
Expand Down Expand Up @@ -92,9 +97,14 @@
}

.editibleTitle {
// Take up the full size of the parent
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
width: 100%;

form,
input {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
// Ensure navigation content always stays at full-width even when animation
// is decreasing the width of the parent
width: var(--navigation-width);
// Ensure fills the height so that there's enough space for the conversation
// item context menu
height: -webkit-fill-available;
}

.left {
Expand Down
15 changes: 10 additions & 5 deletions components/ai_chat/resources/page/stories/components_panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,8 @@ type CustomArgs = {
model: string
inputText: string
hasConversation: boolean
hasConversationListItems: boolean
editingConversationId: string | null
visibleConversationListCount: number
hasSuggestedQuestions: boolean
hasSiteInfo: boolean
isFeedbackFormVisible: boolean
Expand All @@ -422,9 +423,10 @@ const args: CustomArgs = {
initialized: true,
inputText: `Write a Star Trek poem about Data's life on board the Enterprise`,
hasConversation: true,
hasConversationListItems: true,
visibleConversationListCount: CONVERSATIONS.length,
hasSuggestedQuestions: true,
hasSiteInfo: true,
editingConversationId: null,
isFeedbackFormVisible: false,
isStorageNoticeDismissed: false,
canShowPremiumPrompt: false,
Expand Down Expand Up @@ -464,6 +466,9 @@ const preview: Meta<CustomArgs> = {
options: MODELS.map(model => model.displayName),
control: { type: 'select' }
},
visibleConversationListCount: {
control: { type: 'number', min: 0, max: CONVERSATIONS.length }
}
},
args,
decorators: [
Expand Down Expand Up @@ -494,8 +499,8 @@ const preview: Meta<CustomArgs> = {
const aiChatContext: AIChatContext = {
conversationEntriesComponent: StorybookConversationEntries,
initialized: options.args.initialized,
editingConversationId: null,
visibleConversations: options.args.hasConversationListItems ? CONVERSATIONS : [],
editingConversationId: options.args.editingConversationId,
visibleConversations: CONVERSATIONS.slice(0, options.args.visibleConversationListCount),
isStoragePrefEnabled: options.args.isStoragePrefEnabled,
hasAcceptedAgreement: options.args.hasAcceptedAgreement,
isPremiumStatusFetching: false,
Expand All @@ -515,7 +520,7 @@ const preview: Meta<CustomArgs> = {
dismissStorageNotice: () => {},
dismissPremiumPrompt: () => {},
userRefreshPremiumSession: () => {},
setEditingConversationId: () => {},
setEditingConversationId: (id: string | null) => setArgs({ editingConversationId: id }),
showSidebar: showSidebar,
toggleSidebar: () => setShowSidebar(s => !s)
}
Expand Down

0 comments on commit 0c5cf4b

Please sign in to comment.