diff --git a/apps/meteor/.storybook/mocks/meteor.ts b/apps/meteor/.storybook/mocks/meteor.ts index e3f0e0a979e70..7fdd0161454fe 100644 --- a/apps/meteor/.storybook/mocks/meteor.ts +++ b/apps/meteor/.storybook/mocks/meteor.ts @@ -29,6 +29,11 @@ export const Meteor = { users: {}, }; +export const DDPCommon = { + parseDDP: () => undefined, + stringifyDDP: () => '', +}; + export const Tracker = { autorun: () => ({ stop: () => {}, @@ -94,5 +99,3 @@ export const Session = { get: () => {}, set: () => {}, }; - -export const DDPCommon = {}; diff --git a/apps/meteor/client/components/HorizontalDivider/HorizontalDivider.tsx b/apps/meteor/client/components/HorizontalDivider/HorizontalDivider.tsx new file mode 100644 index 0000000000000..8e44ec39a1287 --- /dev/null +++ b/apps/meteor/client/components/HorizontalDivider/HorizontalDivider.tsx @@ -0,0 +1,8 @@ +import { Divider } from '@rocket.chat/fuselage'; +import type { ComponentProps } from 'react'; + +type HorizontalDividerProps = Omit, 'vertical'>; + +const HorizontalDivider = (props: HorizontalDividerProps) => ; + +export default HorizontalDivider; diff --git a/apps/meteor/client/components/HorizontalDivider/index.ts b/apps/meteor/client/components/HorizontalDivider/index.ts new file mode 100644 index 0000000000000..dc7f5fe8b5e2d --- /dev/null +++ b/apps/meteor/client/components/HorizontalDivider/index.ts @@ -0,0 +1 @@ +export { default } from './HorizontalDivider'; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRail.stories.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRail.stories.tsx new file mode 100644 index 0000000000000..0db3df42492ae --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRail.stories.tsx @@ -0,0 +1,64 @@ +import { Box } from '@rocket.chat/fuselage'; +import { mockAppRoot } from '@rocket.chat/mock-providers'; +import { SessionContext } from '@rocket.chat/ui-contexts'; +import type { SessionContextValue } from '@rocket.chat/ui-contexts'; +import type { Meta, StoryObj } from '@storybook/react'; +import type { ReactNode } from 'react'; + +import SidebarRail from './SidebarRail'; + +const sessionMock = (state: Record): SessionContextValue => ({ + query: (name) => [() => () => undefined, () => state[name]], + dispatch: () => undefined, +}); + +const baseRoot = () => + mockAppRoot().withSetting('Layout_Show_Home_Button', true).withTranslations('en', 'core', { + Sidebar: 'Sidebar', + Home: 'Home', + Create_new: 'Create new', + Voice_Call: 'Voice Call', + Pages_and_actions: 'Pages and actions', + Workspace_and_user_preferences: 'Workspace and user preferences', + }); + +export default { + title: 'Sidebar/SidebarRail', + + component: SidebarRail, + parameters: { + layout: 'fullscreen', + }, + decorators: [ + (Story) => ( + + + + ), + ], +} satisfies Meta; + +type Story = StoryObj; + +export const Anonymous: Story = { + decorators: [baseRoot().buildStoryDecorator()], +}; + +export const LoggedIn: Story = { + decorators: [baseRoot().withJohnDoe().buildStoryDecorator()], +}; + +export const WithUnreadBadge: Story = { + decorators: [ + baseRoot() + .withJohnDoe() + .wrap((children: ReactNode) => {children}) + .buildStoryDecorator(), + ], +}; + +export const WithCreatePermissions: Story = { + decorators: [ + baseRoot().withJohnDoe().withPermission('create-c').withPermission('create-p').withPermission('create-d').buildStoryDecorator(), + ], +}; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRail.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRail.tsx new file mode 100644 index 0000000000000..2c59b72f61681 --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRail.tsx @@ -0,0 +1,58 @@ +import { Box, NavBarGroup } from '@rocket.chat/fuselage'; +import { useUser } from '@rocket.chat/ui-contexts'; +import { memo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import SidebarRailCreateNew from './SidebarRailCreateNew'; +import SidebarRailDivider from './SidebarRailDivider'; +import SidebarRailLoginPage from './SidebarRailLoginPage'; +import SidebarRailPhone from './SidebarRailPhone'; +import SidebarRailSort from './SidebarRailSort'; +import NavBarItemDirectoryPage from '../../navbar/NavBarPagesGroup/NavBarItemDirectoryPage'; +import NavBarItemHomePage from '../../navbar/NavBarPagesGroup/NavBarItemHomePage'; +import NavBarItemMarketPlaceMenu from '../../navbar/NavBarPagesGroup/NavBarItemMarketPlaceMenu'; +import { NavBarItemAdministrationMenu, UserMenu } from '../../navbar/NavBarSettingsToolbar'; + +const SidebarRail = () => { + const { t } = useTranslation(); + const user = useUser(); + + return ( + + + + + + + + + + + + + + + + + + {user ? : } + + + + ); +}; + +export default memo(SidebarRail); diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailCallPanel.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailCallPanel.tsx new file mode 100644 index 0000000000000..3949043cd58bb --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailCallPanel.tsx @@ -0,0 +1,40 @@ +import { Box, Sidepanel } from '@rocket.chat/fuselage'; +import { MediaCallWidgetSlot, useWidgetExternalControls } from '@rocket.chat/ui-voip'; +import { useLayoutEffect } from 'react'; +import { useTranslation } from 'react-i18next'; + +const SidebarRailCallPanel = () => { + const { t } = useTranslation(); + const { openDialer, closeDialer } = useWidgetExternalControls(); + + // The call panel hosts the dialer: while it is mounted and the session is idle + // (initial open, or right after a call ends) show the dialer instead of an empty + // panel. `openDialer` is idempotent (only acts on the "closed" state), so React + // StrictMode double-invoking this layout effect on mount is harmless. + useLayoutEffect(() => { + openDialer(); + }, [openDialer]); + + // Leaving the telephony screen with only the idle dialer open must drop it, so it + // does not pop out as a floating widget. The `state === 'new'` guard scopes this to + // the idle dialer (never an ongoing call) and skips StrictMode's fake unmount, where + // the just-issued open has not re-rendered yet. + useLayoutEffect( + () => () => { + closeDialer(); + }, + [closeDialer], + ); + + return ( + + + + + + + + ); +}; + +export default SidebarRailCallPanel; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailCreateNew.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailCreateNew.tsx new file mode 100644 index 0000000000000..5299cfc77adc3 --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailCreateNew.tsx @@ -0,0 +1,22 @@ +import { NavBarItem } from '@rocket.chat/fuselage'; +import { GenericMenu } from '@rocket.chat/ui-client'; +import type { HTMLAttributes } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useCreateNewMenu } from '../../navbar/NavBarPagesGroup/hooks/useCreateNewMenu'; + +type SidebarRailCreateNewProps = Omit, 'is'>; + +const SidebarRailCreateNew = (props: SidebarRailCreateNewProps) => { + const { t } = useTranslation(); + + const sections = useCreateNewMenu(); + + if (sections.length === 0) { + return null; + } + + return ; +}; + +export default SidebarRailCreateNew; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailDivider.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailDivider.tsx new file mode 100644 index 0000000000000..60a4aa630bcfe --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailDivider.tsx @@ -0,0 +1,11 @@ +import type { ComponentProps } from 'react'; + +import HorizontalDivider from '../../components/HorizontalDivider'; + +type SidebarRailDividerProps = ComponentProps; + +const SidebarRailDivider = (props: SidebarRailDividerProps) => ( + +); + +export default SidebarRailDivider; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailHeader.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailHeader.tsx new file mode 100644 index 0000000000000..27c15578474f5 --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailHeader.tsx @@ -0,0 +1,15 @@ +import { Box, NavBar as NavBarComponent, NavBarSection } from '@rocket.chat/fuselage'; + +import NavBarNavigation from '../../navbar/NavBarNavigation'; + +const SidebarRailHeader = () => ( + + + + + + + +); + +export default SidebarRailHeader; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailLoginPage.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailLoginPage.tsx new file mode 100644 index 0000000000000..6eae2924116e4 --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailLoginPage.tsx @@ -0,0 +1,15 @@ +import { NavBarItem } from '@rocket.chat/fuselage'; +import { useSessionDispatch } from '@rocket.chat/ui-contexts'; +import type { HTMLAttributes } from 'react'; +import { useTranslation } from 'react-i18next'; + +type SidebarRailLoginPageProps = Omit, 'is'>; + +const SidebarRailLoginPage = (props: SidebarRailLoginPageProps) => { + const setForceLogin = useSessionDispatch('forceLogin'); + const { t } = useTranslation(); + + return setForceLogin(true)} />; +}; + +export default SidebarRailLoginPage; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailPhone.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailPhone.tsx new file mode 100644 index 0000000000000..4860c09674cba --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailPhone.tsx @@ -0,0 +1,38 @@ +import { NavBarItem } from '@rocket.chat/fuselage'; +import { useStableCallback } from '@rocket.chat/fuselage-hooks'; +import { useCurrentRoutePath, useRouter } from '@rocket.chat/ui-contexts'; +import { useMediaCallAction } from '@rocket.chat/ui-voip'; +import type { HTMLAttributes } from 'react'; +import { useTranslation } from 'react-i18next'; + +type SidebarRailPhoneProps = Omit, 'is'>; + +const SidebarRailPhone = (props: SidebarRailPhoneProps) => { + const { t } = useTranslation(); + const callAction = useMediaCallAction(); + const router = useRouter(); + const currentRoute = useCurrentRoutePath(); + + const isActive = currentRoute?.includes('/call-history') ?? false; + + const handleClick = useStableCallback(() => { + router.navigate('/call-history'); + }); + + if (!callAction) { + return null; + } + + return ( + + ); +}; + +export default SidebarRailPhone; diff --git a/apps/meteor/client/sidebar/SidebarRail/SidebarRailSort.tsx b/apps/meteor/client/sidebar/SidebarRail/SidebarRailSort.tsx new file mode 100644 index 0000000000000..9f5ac72bf00da --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/SidebarRailSort.tsx @@ -0,0 +1,28 @@ +import { NavBarItem } from '@rocket.chat/fuselage'; +import { GenericMenu } from '@rocket.chat/ui-client'; +import type { HTMLAttributes } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { useSortMenu } from '../../navbar/NavBarPagesGroup/hooks/useSortMenu'; + +type SidebarRailSortProps = Omit, 'is'>; + +const SidebarRailSort = (props: SidebarRailSortProps) => { + const { t } = useTranslation(); + + const sections = useSortMenu(); + + return ( + + ); +}; + +export default SidebarRailSort; diff --git a/apps/meteor/client/sidebar/SidebarRail/index.ts b/apps/meteor/client/sidebar/SidebarRail/index.ts new file mode 100644 index 0000000000000..d7869b1253c9b --- /dev/null +++ b/apps/meteor/client/sidebar/SidebarRail/index.ts @@ -0,0 +1 @@ +export { default } from './SidebarRail'; diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.spec.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.spec.tsx index f87e63732ae51..a292504aa4f13 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.spec.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.spec.tsx @@ -1,7 +1,6 @@ import { mockAppRoot } from '@rocket.chat/mock-providers'; import { useCurrentRoutePath, useRouter } from '@rocket.chat/ui-contexts'; import { render } from '@testing-library/react'; -import type { ReactNode } from 'react'; import LayoutWithSidebar from './LayoutWithSidebar'; @@ -12,20 +11,10 @@ jest.mock('@rocket.chat/ui-contexts', () => ({ })); jest.mock('../../../navbar', () => () =>
NavBar
); -jest.mock('../../../sidebar', () => () =>
Sidebar
); -jest.mock('../../navigation', () => () =>
NavigationRegion
); +jest.mock('../../../sidebar/SidebarRail', () => () =>
SidebarRail
); +jest.mock('../../../sidebar/SidebarRail/SidebarRailHeader', () => () =>
SidebarRailHeader
); +jest.mock('./SecondaryPanel', () => () =>
SecondaryPanel
); jest.mock('./AccessibilityShortcut', () => () =>
AccessibilityShortcut
); -jest.mock('../../navigation/providers/RoomsNavigationProvider', () => ({ - __esModule: true, - default: ({ children }: { children: ReactNode }) => <>{children}, -})); - -jest.mock('@rocket.chat/ui-client', () => ({ - ...jest.requireActual('@rocket.chat/ui-client'), - FeaturePreview: ({ children }: { children: ReactNode }) => <>{children}, - FeaturePreviewOn: ({ children }: { children: ReactNode }) => <>{children}, - FeaturePreviewOff: ({ children }: { children: ReactNode }) => <>{children}, -})); const mockedUseCurrentRoutePath = useCurrentRoutePath as jest.MockedFunction; const mockedUseRouter = useRouter as jest.MockedFunction; diff --git a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx index a2ed06e110e50..d2c3fad3002b2 100644 --- a/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx +++ b/apps/meteor/client/views/root/MainLayout/LayoutWithSidebar.tsx @@ -1,5 +1,4 @@ import { Box } from '@rocket.chat/fuselage'; -import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client'; import type { IRouterPaths } from '@rocket.chat/ui-contexts'; import { useLayout, useSetting, useCurrentRoutePath, useRouter } from '@rocket.chat/ui-contexts'; import type { ReactNode } from 'react'; @@ -8,17 +7,19 @@ import { useEffect, useRef } from 'react'; import AccessibilityShortcut from './AccessibilityShortcut'; import MainContent from './MainContent'; import { MainLayoutStyleTags } from './MainLayoutStyleTags'; +import SecondaryPanel from './SecondaryPanel'; +import { isSidebarRailEnabled } from './sidebarRailFlag'; import NavBar from '../../../navbar'; -import Sidebar from '../../../sidebar'; -import NavigationRegion from '../../navigation'; -import RoomsNavigationProvider from '../../navigation/providers/RoomsNavigationProvider'; +import SidebarRail from '../../../sidebar/SidebarRail'; +import SidebarRailHeader from '../../../sidebar/SidebarRail/SidebarRailHeader'; const INVALID_ROOM_NAME_PREFIXES = ['#', '?'] as const; export type LayoutWithSidebarProps = { children: ReactNode }; const LayoutWithSidebar = ({ children }: LayoutWithSidebarProps) => { - const { isEmbedded: embeddedLayout } = useLayout(); + const { isEmbedded: embeddedLayout, isMobile } = useLayout(); + const showSidebarRail = isSidebarRailEnabled() && !embeddedLayout && !isMobile; const currentRoutePath = useCurrentRoutePath(); const router = useRouter(); @@ -56,25 +57,15 @@ const LayoutWithSidebar = ({ children }: LayoutWithSidebarProps) => { return ( <> - {!embeddedLayout && } + {!embeddedLayout && (showSidebarRail ? : )} - {!removeSidenav && ( - - - - - - - - - - - )} + {showSidebarRail && } + {!removeSidenav && } {children} diff --git a/apps/meteor/client/views/root/MainLayout/MainLayoutStyleTags.tsx b/apps/meteor/client/views/root/MainLayout/MainLayoutStyleTags.tsx index 4b36a7502302d..18ebca25429b7 100644 --- a/apps/meteor/client/views/root/MainLayout/MainLayoutStyleTags.tsx +++ b/apps/meteor/client/views/root/MainLayout/MainLayoutStyleTags.tsx @@ -9,7 +9,7 @@ export const MainLayoutStyleTags = () => { return ( <> - + {theme === 'dark' && } ); diff --git a/apps/meteor/client/views/root/MainLayout/SecondaryPanel.tsx b/apps/meteor/client/views/root/MainLayout/SecondaryPanel.tsx new file mode 100644 index 0000000000000..aab7112c670e4 --- /dev/null +++ b/apps/meteor/client/views/root/MainLayout/SecondaryPanel.tsx @@ -0,0 +1,32 @@ +import { FeaturePreview, FeaturePreviewOff, FeaturePreviewOn } from '@rocket.chat/ui-client'; + +import Sidebar from '../../../sidebar'; +import SidebarRailCallPanel from '../../../sidebar/SidebarRail/SidebarRailCallPanel'; +import NavigationRegion from '../../navigation'; +import RoomsNavigationProvider from '../../navigation/providers/RoomsNavigationProvider'; + +type SecondaryPanelProps = { + showSidebarRail: boolean; + currentRoutePath?: string; +}; + +const SecondaryPanel = ({ showSidebarRail, currentRoutePath }: SecondaryPanelProps) => { + if (showSidebarRail && currentRoutePath?.includes('/call-history')) { + return ; + } + + return ( + + + + + + + + + + + ); +}; + +export default SecondaryPanel; diff --git a/apps/meteor/client/views/root/MainLayout/sidebarRailFlag.ts b/apps/meteor/client/views/root/MainLayout/sidebarRailFlag.ts new file mode 100644 index 0000000000000..ede40e22772ba --- /dev/null +++ b/apps/meteor/client/views/root/MainLayout/sidebarRailFlag.ts @@ -0,0 +1,19 @@ +declare global { + // eslint-disable-next-line @typescript-eslint/naming-convention + interface Window { + USE_SIDEBAR_RAIL?: boolean; + } +} + +/** + * Feature flag for the collapsed SidebarRail navigation. + * + * Exposed on `window` so it can be toggled at runtime without a rebuild — e.g. from an + * admin custom script: + * + * window.USE_SIDEBAR_RAIL = true; + * + * Read on every render, so the value applies on the next page load after being set. + * Defaults to `false`. + */ +export const isSidebarRailEnabled = (): boolean => window.USE_SIDEBAR_RAIL ?? false; diff --git a/packages/ui-voip/src/components/Keypad/Keypad.tsx b/packages/ui-voip/src/components/Keypad/Keypad.tsx index cdceeed6229ed..98f6610fac3e3 100644 --- a/packages/ui-voip/src/components/Keypad/Keypad.tsx +++ b/packages/ui-voip/src/components/Keypad/Keypad.tsx @@ -5,6 +5,7 @@ import Key from './Key'; export type KeypadProps = { onKeyPress(key: string): void; + autoFocus?: boolean; }; const DIGITS = [ @@ -22,8 +23,8 @@ const DIGITS = [ ['#', ''], ]; -const Keypad = ({ onKeyPress }: KeypadProps) => ( - +const Keypad = ({ onKeyPress, autoFocus = true }: KeypadProps) => ( + {DIGITS.map(([primaryDigit, alternativeDigit, longPressDigit]) => ( void; + }; +}; + +type UseKeypadOptions = { + // When `true` the keypad is always rendered (expanded) and does not steal focus on mount. + // Used by the inline (sidebar rail) layout where the dialpad is permanently visible. + alwaysOpen?: boolean; +}; + +export const useKeypad = (onPress: (tone: string) => void, { alwaysOpen = false }: UseKeypadOptions = {}): UseKeypad => { + const [open, setOpen] = useState(false); + const [inputValue, setInputValue] = useState(''); + const { t } = useTranslation(); + + const element = ( + + + + + + + { + setInputValue((inputValue) => inputValue + args[0]); + onPress(...args); + }} + /> + + + ); + + return { + element: alwaysOpen || open ? element : null, + buttonProps: { + title: open ? t('Close_dialpad') : t('Open_dialpad'), + onClick: () => setOpen((open) => !open), + }, + }; +}; diff --git a/packages/ui-voip/src/components/MediaCallWidgetSlot.tsx b/packages/ui-voip/src/components/MediaCallWidgetSlot.tsx new file mode 100644 index 0000000000000..6a00996d002d3 --- /dev/null +++ b/packages/ui-voip/src/components/MediaCallWidgetSlot.tsx @@ -0,0 +1,19 @@ +import { useLayoutEffect, useRef } from 'react'; + +import { useMediaCallWidgetSlot } from '../context/MediaCallWidgetSlotContext'; + +const slotStyle = { display: 'flex', alignItems: 'flex-start', flex: '0 0 auto', width: '100%' } as const; + +const MediaCallWidgetSlot = () => { + const ref = useRef(null); + const { setSlot } = useMediaCallWidgetSlot(); + + useLayoutEffect(() => { + setSlot(ref.current); + return () => setSlot(null); + }, [setSlot]); + + return
; +}; + +export default MediaCallWidgetSlot; diff --git a/packages/ui-voip/src/components/Widget/Widget.tsx b/packages/ui-voip/src/components/Widget/Widget.tsx index 06e040949cd1b..f3f4bb9cd7db8 100644 --- a/packages/ui-voip/src/components/Widget/Widget.tsx +++ b/packages/ui-voip/src/components/Widget/Widget.tsx @@ -5,29 +5,36 @@ import type { ComponentProps, ReactNode } from 'react'; import { useLayoutEffect } from 'react'; import { DragContext } from './WidgetDraggableContext'; +import { useMediaCallWidgetSlot } from '../../context/MediaCallWidgetSlotContext'; import { useDraggable } from '../../hooks'; import { type LastKnownPosition } from '../../providers/useWidgetPositionTracker'; // TODO: Initial position from the draggable api instead of style props // TODO: A11Y -const WidgetBase = styled('div')` - position: fixed; +const WidgetBase = styled('div', ({ inline: _inline, ...props }: { inline?: boolean }) => props)` padding: 0; - right: 2em; - top: 11em; display: flex; flex-direction: column; width: 248px; min-height: 128px; border-radius: 4px; - border: 1px solid ${Palette.stroke['stroke-dark'].toString()}; - box-shadow: - 0px 0px 1px 0px ${Palette.shadow['shadow-elevation-2x'].toString()}, - 0px 0px 12px 0px ${Palette.shadow['shadow-elevation-2y'].toString()}; background-color: ${Palette.surface['surface-tint'].toString()}; color: ${Palette.text['font-default'].toString()}; - z-index: 100; overflow: hidden; + + ${({ inline }) => + inline + ? '' + : ` + position: fixed; + right: 2em; + top: 11em; + border: 1px solid ${Palette.stroke['stroke-dark'].toString()}; + box-shadow: + 0px 0px 1px 0px ${Palette.shadow['shadow-elevation-2x'].toString()}, + 0px 0px 12px 0px ${Palette.shadow['shadow-elevation-2y'].toString()}; + z-index: 100; + `} `; export type WidgetProps = { @@ -37,18 +44,23 @@ export type WidgetProps = { } & ComponentProps; const Widget = ({ children, onChangePosition, restorePosition, ...props }: WidgetProps) => { + const { inline } = useMediaCallWidgetSlot(); const [draggableRef, boundingRef, handleRef] = useDraggable({ onChangePosition, restorePosition }); useLayoutEffect(() => { + if (inline) { + return; + } boundingRef(document.body); - }, [boundingRef]); + }, [boundingRef, inline]); return ( diff --git a/packages/ui-voip/src/components/Widget/WidgetHandle.tsx b/packages/ui-voip/src/components/Widget/WidgetHandle.tsx index 059f063423df0..ff0288e3fabaf 100644 --- a/packages/ui-voip/src/components/Widget/WidgetHandle.tsx +++ b/packages/ui-voip/src/components/Widget/WidgetHandle.tsx @@ -3,6 +3,7 @@ import { Box, Icon, Palette } from '@rocket.chat/fuselage'; import type { ComponentProps } from 'react'; import { useDraggableWidget } from './WidgetDraggableContext'; +import { useMediaCallWidgetSlot } from '../../context/MediaCallWidgetSlotContext'; const dragHandle = css` cursor: grab; @@ -21,6 +22,12 @@ const dragHandle = css` const WidgetHandle = (props: ComponentProps) => { const { handleRef } = useDraggableWidget(); + const { inline } = useMediaCallWidgetSlot(); + + if (inline) { + return null; + } + return ( diff --git a/packages/ui-voip/src/components/Widget/__snapshots__/Widget.spec.tsx.snap b/packages/ui-voip/src/components/Widget/__snapshots__/Widget.spec.tsx.snap index 041b73b7e6363..22c3c847638bf 100644 --- a/packages/ui-voip/src/components/Widget/__snapshots__/Widget.spec.tsx.snap +++ b/packages/ui-voip/src/components/Widget/__snapshots__/Widget.spec.tsx.snap @@ -9,7 +9,7 @@ exports[`renders FullWidget without crashing 1`] = ` />