Skip to content

Commit

Permalink
update sidecar menu
Browse files Browse the repository at this point in the history
Signed-off-by: tygao <[email protected]>
  • Loading branch information
raintygao committed Jan 22, 2024
1 parent 63ac8b3 commit 6c05b57
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 153 deletions.
10 changes: 2 additions & 8 deletions public/chat_flyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { EuiFlyout, EuiFlyoutHeader, EuiResizableContainer } from '@elastic/eui';
import { EuiFlyoutHeader, EuiResizableContainer } from '@elastic/eui';
import cs from 'classnames';
import React, { useRef } from 'react';
import { useChatContext } from './contexts/chat_context';
Expand All @@ -16,9 +16,7 @@ import { TAB_ID } from './utils/constants';
interface ChatFlyoutProps {
flyoutVisible: boolean;
overrideComponent: React.ReactNode | null;
flyoutProps: Partial<React.ComponentProps<typeof EuiFlyout>>;
flyoutFullScreen: boolean;
toggleFlyoutFullScreen: (direction: string) => void;
}

export const ChatFlyout = (props: ChatFlyoutProps) => {
Expand Down Expand Up @@ -85,14 +83,10 @@ export const ChatFlyout = (props: ChatFlyoutProps) => {
className={cs('llm-chat-flyout', {
'llm-chat-fullscreen': props.flyoutFullScreen,
})}
{...props.flyoutProps}
>
<>
<EuiFlyoutHeader className={cs('llm-chat-flyout-header')}>
<ChatWindowHeader
flyoutFullScreen={props.flyoutFullScreen}
toggleFlyoutFullScreen={props.toggleFlyoutFullScreen}
/>
<ChatWindowHeader />
</EuiFlyoutHeader>

{props.overrideComponent}
Expand Down
42 changes: 4 additions & 38 deletions public/chat_header_button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { EuiBadge, EuiFieldText, EuiIcon } from '@elastic/eui';
import classNames from 'classnames';
import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
import { useEffectOnce } from 'react-use';
import { useEffectOnce, useObservable } from 'react-use';
import { ApplicationStart } from '../../../src/core/public';
import { ChatFlyout } from './chat_flyout';
import { ChatContext, IChatContext } from './contexts/chat_context';
Expand Down Expand Up @@ -43,46 +43,20 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
const [selectedTabId, setSelectedTabId] = useState<TabId>(TAB_ID.CHAT);
const [preSelectedTabId, setPreSelectedTabId] = useState<TabId | undefined>(undefined);
const [interactionId, setInteractionId] = useState<string | undefined>(undefined);
const [dockedDirection, setDockedDirection] = useState<ISidecarConfig['dockedDirection']>(
'right'
);
const [query, setQuery] = useState('');
const [inputFocus, setInputFocus] = useState(false);
const flyoutFullScreen = dockedDirection === 'bottom';
const inputRef = useRef<HTMLInputElement>(null);
// const [flyoutLoaded, setFlyoutLoaded] = useState(false);
const core = useCore();
// if (!flyoutLoaded && flyoutVisible) flyoutLoaded = true;
const sidecarConfig = useObservable(core.overlays.sidecar().getSidecarConfig$());
const flyoutFullScreen = sidecarConfig?.dockedMode === 'takeover';

useEffectOnce(() => {
const subscription = props.application.currentAppId$.subscribe((id) => setAppId(id));
return () => subscription.unsubscribe();
});

const toggleFlyoutFullScreen = useCallback(
(direction: ISidecarConfig['dockedDirection']) => {
setDockedDirection((prevDirection) => {
if (prevDirection === direction) {
return prevDirection;
} else {
if (direction === 'bottom') {
core.overlays.sidecar().setSidecarConfig({
dockedDirection: 'bottom',
paddingSize: window.innerHeight - 136,
});
} else {
core.overlays.sidecar().setSidecarConfig({
dockedDirection: direction,
paddingSize: 460,
});
}
return direction;
}
});
},
[flyoutFullScreen]
);

const chatContextValue: IChatContext = useMemo(
() => ({
appId,
Expand Down Expand Up @@ -154,34 +128,26 @@ export const HeaderChatButton = (props: HeaderChatButtonProps) => {
<ChatFlyout
flyoutVisible={flyoutVisible}
overrideComponent={flyoutComponent}
flyoutProps={flyoutFullScreen ? { size: '100%' } : {}}
flyoutFullScreen={flyoutFullScreen}
toggleFlyoutFullScreen={toggleFlyoutFullScreen}
/>
</ChatStateProvider>
</ChatContext.Provider>
</props.coreContext.Provider>
);

useEffect(() => {
console.log('visa', flyoutVisible, flyoutLoaded);
if (!flyoutLoaded && flyoutVisible) {
console.log('mounut');
core.overlays.sidecar().open(toMountPoint(Chat), {
className: 'chatbot-sidecar',
config: {
dockedDirection: 'right',
dockedMode: 'right',
paddingSize: 460,
},
});
flyoutLoaded = true;
} else if (flyoutLoaded && flyoutVisible) {
console.log('show');

core.overlays.sidecar().show();
} else if (flyoutLoaded && !flyoutVisible) {
console.log('hide');

core.overlays.sidecar().hide();
}
}, [flyoutVisible, flyoutLoaded]);
Expand Down
152 changes: 80 additions & 72 deletions public/components/sidecar_icon_menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,38 @@
* SPDX-License-Identifier: Apache-2.0
*/

import {
EuiContextMenuItem,
EuiContextMenuPanel,
EuiFlexGroup,
EuiFlexItem,
EuiPopover,
EuiButtonIcon,
} from '@elastic/eui';
import React, { useCallback, useState } from 'react';
import { EuiContextMenuItem, EuiContextMenuPanel, EuiPopover, EuiButtonIcon } from '@elastic/eui';
import React, { useCallback, useMemo, useState } from 'react';
import { useObservable } from 'react-use';
import { useCore } from '../contexts/core_context';
import { ISidecarConfig } from '../../../../src/core/public';

const dockBottom = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path d="M3 1H13C14.1046 1 15 1.89543 15 3V13C15 14.1046 14.1046 15 13 15H3C1.89543 15 1 14.1046 1 13V3C1 1.89543 1.89543 1 3 1ZM3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2H3Z" />
<path d="M3 9.5C3 9.22386 3.22386 9 3.5 9H12.5C12.7761 9 13 9.22386 13 9.5V12.5C13 12.7761 12.7761 13 12.5 13H3.5C3.22386 13 3 12.7761 3 12.5V9.5Z" />
</g>
</svg>
);
const ALL_SIDECAR_DIRECTIONS: Array<{
mode: ISidecarConfig['dockedMode'];
name: string;
icon: string;
}> = [
{
mode: 'right',
name: 'Dock Right',
icon: 'dockedRight',
},
{
mode: 'left',
name: 'Dock Left',
icon: 'dockedLeft',
},
{
mode: 'takeover',
name: 'Dock Full Width',
icon: 'dockedTakeover',
},
];

const dockRight = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path d="M3 1H13C14.1046 1 15 1.89543 15 3V13C15 14.1046 14.1046 15 13 15H3C1.89543 15 1 14.1046 1 13V3C1 1.89543 1.89543 1 3 1ZM3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2H3Z" />
<path d="M9 3.5C9 3.22386 9.22386 3 9.5 3H12.5C12.7761 3 13 3.22386 13 3.5V12.5C13 12.7761 12.7761 13 12.5 13H9.5C9.22386 13 9 12.7761 9 12.5V3.5Z" />
</g>
</svg>
);

interface Props {
setDockedDirection: (direction: string) => void;
}

export const SidecarIconMenu = ({ setDockedDirection }) => {
export const SidecarIconMenu = () => {
const [isPopoverOpen, setPopoverOpen] = useState(false);
const core = useCore();
const sidecarConfig = useObservable(core.overlays.sidecar().getSidecarConfig$());

const onButtonClick = useCallback(() => {
setPopoverOpen((flag) => !flag);
Expand All @@ -46,58 +44,68 @@ export const SidecarIconMenu = ({ setDockedDirection }) => {
setPopoverOpen(false);
}, []);

const button = (
<EuiButtonIcon
aria-label="fullScreen"
color="text"
size="xs"
iconType={dockRight}
onClick={onButtonClick}
/>
const setDockedMode = useCallback(
(mode: ISidecarConfig['dockedMode']) => {
const currentMode = sidecarConfig?.dockedMode;
if (currentMode === mode) {
return;
} else {
if (mode === 'takeover') {
core.overlays.sidecar().setSidecarConfig({
dockedMode: 'takeover',
paddingSize: window.innerHeight - 136,
});
} else {
core.overlays.sidecar().setSidecarConfig({
dockedMode: mode,
paddingSize: 460,
});
}
}
},
[sidecarConfig]
);

const menuItems = useMemo(
() =>
ALL_SIDECAR_DIRECTIONS.map(({ name, icon, mode }) => (
<EuiContextMenuItem
key={mode}
onClick={() => {
closePopover();
setDockedMode(mode);
}}
icon={sidecarConfig?.dockedMode === mode ? 'check' : icon}
>
{name}
</EuiContextMenuItem>
)),
[sidecarConfig]
);

const items = [
<EuiContextMenuItem
key="rename-conversation"
prefix="a"
onClick={() => {
closePopover();
setDockedDirection('right');
}}
>
Dock Right
</EuiContextMenuItem>,
<EuiContextMenuItem
key="new-conversation"
onClick={() => {
closePopover();
setDockedDirection('left');
}}
>
Dock Left
</EuiContextMenuItem>,
<EuiContextMenuItem
key="save-as-notebook"
onClick={() => {
closePopover();
setDockedDirection('bottom');
}}
>
Dock Full Width
</EuiContextMenuItem>,
];
const selectMenuItemIndex = useMemo(() => {
const dockedMode = sidecarConfig?.dockedMode ?? 'right';
return ALL_SIDECAR_DIRECTIONS.findIndex((item) => item.mode === dockedMode);
}, [sidecarConfig]);

return (
<>
<EuiPopover
id="conversationTitle"
button={button}
id="sidecarModeIcon"
button={
<EuiButtonIcon
aria-label="fullScreen"
size="xs"
iconType={ALL_SIDECAR_DIRECTIONS[selectMenuItemIndex].icon}
onClick={onButtonClick}
/>
}
isOpen={isPopoverOpen}
closePopover={closePopover}
panelPaddingSize="none"
anchorPosition="downRight"
>
<EuiContextMenuPanel size="m" items={items} />
<EuiContextMenuPanel size="m" items={menuItems} />
</EuiPopover>
</>
);
Expand Down
37 changes: 2 additions & 35 deletions public/tabs/chat_window_header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,9 @@ import chatIcon from '../assets/chat.svg';
import { TAB_ID } from '../utils/constants';
import { SidecarIconMenu } from '../components/sidecar_icon_menu';

export interface ChatWindowHeaderProps {
flyoutFullScreen: boolean;
toggleFlyoutFullScreen: (direction: string) => void;
}

export const ChatWindowHeader = React.memo((props: ChatWindowHeaderProps) => {
export const ChatWindowHeader = React.memo(() => {
const chatContext = useChatContext();

const dockBottom = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path d="M3 1H13C14.1046 1 15 1.89543 15 3V13C15 14.1046 14.1046 15 13 15H3C1.89543 15 1 14.1046 1 13V3C1 1.89543 1.89543 1 3 1ZM3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2H3Z" />
<path d="M3 9.5C3 9.22386 3.22386 9 3.5 9H12.5C12.7761 9 13 9.22386 13 9.5V12.5C13 12.7761 12.7761 13 12.5 13H3.5C3.22386 13 3 12.7761 3 12.5V9.5Z" />
</g>
</svg>
);

const dockRight = () => (
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
<g fill="currentColor">
<path d="M3 1H13C14.1046 1 15 1.89543 15 3V13C15 14.1046 14.1046 15 13 15H3C1.89543 15 1 14.1046 1 13V3C1 1.89543 1.89543 1 3 1ZM3 2C2.44772 2 2 2.44772 2 3V13C2 13.5523 2.44772 14 3 14H13C13.5523 14 14 13.5523 14 13V3C14 2.44772 13.5523 2 13 2H3Z" />
<path d="M9 3.5C9 3.22386 9.22386 3 9.5 3H12.5C12.7761 3 13 3.22386 13 3.5V12.5C13 12.7761 12.7761 13 12.5 13H9.5C9.22386 13 9 12.7761 9 12.5V3.5Z" />
</g>
</svg>
);

return (
<>
<EuiFlexGroup
Expand Down Expand Up @@ -71,17 +48,7 @@ export const ChatWindowHeader = React.memo((props: ChatWindowHeaderProps) => {
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlexItem>
<SidecarIconMenu setDockedDirection={props.toggleFlyoutFullScreen} />
{/* <EuiFlexItem grow={false}>
<EuiButtonIcon
aria-label="fullScreen"
color="text"
size="xs"
// TODO replace svg with built-in icon
iconType={props.flyoutFullScreen ? dockRight : dockBottom}
onClick={props.toggleFlyoutFullScreen}
/>
</EuiFlexItem> */}
<SidecarIconMenu />
<EuiFlexItem grow={false}>
<EuiButtonIcon
aria-label="close"
Expand Down

0 comments on commit 6c05b57

Please sign in to comment.