feat: `Sidebar` new components (#32821)

pull/33155/head^2
Júlia Jaeger Foresti 1 year ago committed by GitHub
parent d27cc363a5
commit cd0d50016e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 13
      .changeset/many-balloons-scream.md
  2. 38
      apps/meteor/client/sidebarv2/Item/Condensed.tsx
  3. 9
      apps/meteor/client/sidebarv2/Item/Extended.stories.tsx
  4. 70
      apps/meteor/client/sidebarv2/Item/Extended.tsx
  5. 41
      apps/meteor/client/sidebarv2/Item/Medium.tsx
  6. 121
      apps/meteor/client/sidebarv2/RoomList/RoomList.tsx
  7. 19
      apps/meteor/client/sidebarv2/RoomList/RoomListRow.tsx
  8. 3
      apps/meteor/client/sidebarv2/RoomList/RoomListRowWrapper.tsx
  9. 42
      apps/meteor/client/sidebarv2/RoomList/SidebarItemTemplateWithData.tsx
  10. 4
      apps/meteor/client/sidebarv2/RoomList/useSidebarListNavigation.ts
  11. 26
      apps/meteor/client/sidebarv2/Sidebar.tsx
  12. 2
      apps/meteor/client/sidebarv2/header/SearchList.tsx
  13. 15
      apps/meteor/client/sidebarv2/header/SearchSection.tsx
  14. 4
      apps/meteor/client/sidebarv2/header/actions/CreateRoom.tsx
  15. 50
      apps/meteor/client/sidebarv2/header/actions/Search.tsx
  16. 6
      apps/meteor/client/sidebarv2/header/actions/Sort.tsx
  17. 4
      apps/meteor/client/sidebarv2/hooks/useAvatarTemplate.tsx
  18. 10
      apps/meteor/client/sidebarv2/search/Row.tsx
  19. 382
      apps/meteor/client/sidebarv2/search/SearchList.tsx
  20. 14
      apps/meteor/client/sidebarv2/search/UserItem.tsx
  21. 10
      apps/meteor/client/sidebarv2/sections/StatusDisabledSection.tsx
  22. 2
      apps/meteor/package.json
  23. 2
      ee/packages/ui-theming/package.json
  24. 2
      packages/fuselage-ui-kit/package.json
  25. 2
      packages/gazzodown/package.json
  26. 2
      packages/ui-avatar/package.json
  27. 2
      packages/ui-client/package.json
  28. 2
      packages/ui-composer/package.json
  29. 2
      packages/ui-video-conf/package.json
  30. 2
      packages/uikit-playground/package.json
  31. 26
      yarn.lock

@ -0,0 +1,13 @@
---
'@rocket.chat/uikit-playground': minor
'@rocket.chat/fuselage-ui-kit': minor
'@rocket.chat/ui-theming': minor
'@rocket.chat/ui-video-conf': minor
'@rocket.chat/ui-composer': minor
'@rocket.chat/gazzodown': minor
'@rocket.chat/ui-avatar': minor
'@rocket.chat/ui-client': minor
'@rocket.chat/meteor': minor
---
Replaced new `SidebarV2` components under feature preview

@ -1,11 +1,11 @@
import { IconButton, Sidebar } from '@rocket.chat/fuselage';
import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage';
import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons';
import type { ReactElement } from 'react';
import React, { memo, useState } from 'react';
type CondensedProps = {
title: ReactElement | string;
title: string;
titleIcon?: ReactElement;
avatar: ReactElement | boolean;
icon?: IconName;
@ -19,7 +19,7 @@ type CondensedProps = {
clickable?: boolean;
};
const Condensed = ({ icon, title = '', avatar, actions, href, unread, menu, badges, ...props }: CondensedProps) => {
const Condensed = ({ icon, title, avatar, actions, href, unread, menu, badges, selected }: CondensedProps) => {
const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION);
const isReduceMotionEnabled = usePrefersReducedMotion();
@ -32,28 +32,18 @@ const Condensed = ({ icon, title = '', avatar, actions, href, unread, menu, badg
};
return (
<Sidebar.Item {...props} {...({ href } as any)} clickable={!!href}>
{avatar && <Sidebar.Item.Avatar>{avatar}</Sidebar.Item.Avatar>}
<Sidebar.Item.Content>
<Sidebar.Item.Wrapper>
{icon}
<Sidebar.Item.Title data-qa='sidebar-item-title' className={(unread && 'rcx-sidebar-item--highlighted') as string}>
{title}
</Sidebar.Item.Title>
</Sidebar.Item.Wrapper>
{badges && <Sidebar.Item.Badge>{badges}</Sidebar.Item.Badge>}
{menu && (
<Sidebar.Item.Menu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-item__menu icon='kebab' />}
</Sidebar.Item.Menu>
)}
</Sidebar.Item.Content>
{actions && (
<Sidebar.Item.Container>
<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>
</Sidebar.Item.Container>
<SidebarV2Item href={href} selected={selected}>
{avatar && <SidebarV2ItemAvatarWrapper>{avatar}</SidebarV2ItemAvatarWrapper>}
{icon && icon}
<SidebarV2ItemTitle unread={unread}>{title}</SidebarV2ItemTitle>
{badges && badges}
{actions && actions}
{menu && (
<SidebarV2ItemMenu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-v2-item__menu icon='kebab' />}
</SidebarV2ItemMenu>
)}
</Sidebar.Item>
</SidebarV2Item>
);
};

@ -25,14 +25,7 @@ export default {
const Template: ComponentStory<typeof Extended> = (args) => (
<Extended
{...args}
title={
<Box display='flex' flexDirection='row' w='full' alignItems='center'>
<Box flexGrow='1' withTruncatedText>
John Doe
</Box>
<Box fontScale='micro'>15:38</Box>
</Box>
}
title='John Doe'
subtitle={
<Box display='flex' flexDirection='row' w='full' alignItems='center'>
<Box flexGrow='1' withTruncatedText>

@ -1,4 +1,14 @@
import { Sidebar, IconButton } from '@rocket.chat/fuselage';
import {
SidebarV2Item,
SidebarV2ItemAvatarWrapper,
SidebarV2ItemCol,
SidebarV2ItemRow,
SidebarV2ItemTitle,
SidebarV2ItemTimestamp,
SidebarV2ItemContent,
SidebarV2ItemMenu,
IconButton,
} from '@rocket.chat/fuselage';
import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons';
import React, { memo, useState } from 'react';
@ -7,7 +17,7 @@ import { useShortTimeAgo } from '../../hooks/useTimeAgo';
type ExtendedProps = {
icon?: IconName;
title?: React.ReactNode;
title: string;
avatar?: React.ReactNode | boolean;
actions?: React.ReactNode;
href?: string;
@ -24,7 +34,7 @@ type ExtendedProps = {
const Extended = ({
icon,
title = '',
title,
avatar,
actions,
href,
@ -37,7 +47,6 @@ const Extended = ({
threadUnread: _threadUnread,
unread,
selected,
...props
}: ExtendedProps) => {
const formatDate = useShortTimeAgo();
const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION);
@ -47,42 +56,33 @@ const Extended = ({
const handleMenu = useEffectEvent((e) => {
setMenuVisibility(e.target.offsetWidth > 0 && Boolean(menu));
});
const handleMenuEvent = {
[isReduceMotionEnabled ? 'onMouseEnter' : 'onTransitionEnd']: handleMenu,
};
return (
<Sidebar.Item selected={selected} highlighted={unread} {...props} {...({ href } as any)} clickable={!!href}>
{avatar && <Sidebar.Item.Avatar>{avatar}</Sidebar.Item.Avatar>}
<Sidebar.Item.Content>
<Sidebar.Item.Content>
<Sidebar.Item.Wrapper>
{icon}
<Sidebar.Item.Title data-qa='sidebar-item-title' className={(unread && 'rcx-sidebar-item--highlighted') as string}>
{title}
</Sidebar.Item.Title>
{time && <Sidebar.Item.Time>{formatDate(time)}</Sidebar.Item.Time>}
</Sidebar.Item.Wrapper>
</Sidebar.Item.Content>
<Sidebar.Item.Content>
<Sidebar.Item.Wrapper>
<Sidebar.Item.Subtitle className={(unread && 'rcx-sidebar-item--highlighted') as string}>{subtitle}</Sidebar.Item.Subtitle>
<Sidebar.Item.Badge>{badges}</Sidebar.Item.Badge>
{menu && (
<Sidebar.Item.Menu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-item__menu icon='kebab' />}
</Sidebar.Item.Menu>
)}
</Sidebar.Item.Wrapper>
</Sidebar.Item.Content>
</Sidebar.Item.Content>
{actions && (
<Sidebar.Item.Container>
<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>
</Sidebar.Item.Container>
)}
</Sidebar.Item>
<SidebarV2Item href={href} selected={selected}>
{avatar && <SidebarV2ItemAvatarWrapper>{avatar}</SidebarV2ItemAvatarWrapper>}
<SidebarV2ItemCol>
<SidebarV2ItemRow>
{icon && icon}
<SidebarV2ItemTitle unread={unread}>{title}</SidebarV2ItemTitle>
{time && <SidebarV2ItemTimestamp>{formatDate(time)}</SidebarV2ItemTimestamp>}
</SidebarV2ItemRow>
<SidebarV2ItemRow>
<SidebarV2ItemContent unread={unread}>{subtitle}</SidebarV2ItemContent>
{badges && badges}
{actions && actions}
{menu && (
<SidebarV2ItemMenu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-v2-item__menu icon='kebab' />}
</SidebarV2ItemMenu>
)}
</SidebarV2ItemRow>
</SidebarV2ItemCol>
</SidebarV2Item>
);
};

@ -1,12 +1,13 @@
import { Sidebar, IconButton } from '@rocket.chat/fuselage';
import { IconButton, SidebarV2Item, SidebarV2ItemAvatarWrapper, SidebarV2ItemMenu, SidebarV2ItemTitle } from '@rocket.chat/fuselage';
import { useEffectEvent, usePrefersReducedMotion } from '@rocket.chat/fuselage-hooks';
import type { Keys as IconName } from '@rocket.chat/icons';
import React, { memo, useState } from 'react';
type MediumProps = {
title: React.ReactNode;
title: string;
titleIcon?: React.ReactNode;
avatar: React.ReactNode | boolean;
icon?: string;
icon?: IconName;
actions?: React.ReactNode;
href?: string;
unread?: boolean;
@ -16,7 +17,7 @@ type MediumProps = {
menuOptions?: any;
};
const Medium = ({ icon, title = '', avatar, actions, href, badges, unread, menu, ...props }: MediumProps) => {
const Medium = ({ icon, title, avatar, actions, href, badges, unread, menu, selected }: MediumProps) => {
const [menuVisibility, setMenuVisibility] = useState(!!window.DISABLE_ANIMATION);
const isReduceMotionEnabled = usePrefersReducedMotion();
@ -29,28 +30,18 @@ const Medium = ({ icon, title = '', avatar, actions, href, badges, unread, menu,
};
return (
<Sidebar.Item {...props} href={href} clickable={!!href}>
{avatar && <Sidebar.Item.Avatar>{avatar}</Sidebar.Item.Avatar>}
<Sidebar.Item.Content>
<Sidebar.Item.Wrapper>
{icon}
<Sidebar.Item.Title data-qa='sidebar-item-title' className={unread ? 'rcx-sidebar-item--highlighted' : undefined}>
{title}
</Sidebar.Item.Title>
</Sidebar.Item.Wrapper>
{badges && <Sidebar.Item.Badge>{badges}</Sidebar.Item.Badge>}
{menu && (
<Sidebar.Item.Menu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-item__menu icon='kebab' />}
</Sidebar.Item.Menu>
)}
</Sidebar.Item.Content>
{actions && (
<Sidebar.Item.Container>
<Sidebar.Item.Actions>{actions}</Sidebar.Item.Actions>
</Sidebar.Item.Container>
<SidebarV2Item href={href} selected={selected}>
<SidebarV2ItemAvatarWrapper>{avatar}</SidebarV2ItemAvatarWrapper>
{icon && icon}
<SidebarV2ItemTitle unread={unread}>{title}</SidebarV2ItemTitle>
{badges && badges}
{actions && actions}
{menu && (
<SidebarV2ItemMenu {...handleMenuEvent}>
{menuVisibility ? menu() : <IconButton tabIndex={-1} aria-hidden mini rcx-sidebar-v2-item__menu icon='kebab' />}
</SidebarV2ItemMenu>
)}
</Sidebar.Item>
</SidebarV2Item>
);
};

@ -1,11 +1,11 @@
import type { IRoom } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Box } from '@rocket.chat/fuselage';
/* eslint-disable react/no-multi-comp */
import type { ISubscription, IRoom } from '@rocket.chat/core-typings';
import { Box, SidebarV2GroupTitle } from '@rocket.chat/fuselage';
import { useResizeObserver } from '@rocket.chat/fuselage-hooks';
import type { TranslationKey } from '@rocket.chat/ui-contexts';
import { useUserPreference, useUserId, useTranslation } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
import React, { useMemo } from 'react';
import { Virtuoso } from 'react-virtuoso';
import { GroupedVirtuoso } from 'react-virtuoso';
import { VirtuosoScrollbars } from '../../components/CustomScrollbars';
import { useOpenedRoom } from '../../lib/RoomManager';
@ -18,7 +18,25 @@ import RoomListRow from './RoomListRow';
import RoomListRowWrapper from './RoomListRowWrapper';
import RoomListWrapper from './RoomListWrapper';
const computeItemKey = (index: number, room: IRoom): IRoom['_id'] | number => room._id || index;
const getRoomsByGroup = (rooms: (ISubscription & IRoom)[]) => {
const groupCounts = rooms
.reduce((acc, item, index) => {
if (typeof item === 'string') {
acc.push(index);
}
return acc;
}, [] as number[])
.map((item, index, arr) => (arr[index + 1] ? arr[index + 1] : rooms.length) - item - 1);
const groupList = rooms.filter((item) => typeof item === 'string') as unknown as TranslationKey[];
const roomList = rooms.filter((item) => typeof item !== 'string');
return {
groupCounts,
groupList,
roomList,
};
};
const RoomList = () => {
const t = useTranslation();
@ -26,7 +44,7 @@ const RoomList = () => {
const roomsList = useRoomList();
const avatarTemplate = useAvatarTemplate();
const sideBarItemTemplate = useTemplateByViewMode();
const { ref } = useResizeObserver({ debounceDelay: 100 });
const { ref } = useResizeObserver<HTMLElement>({ debounceDelay: 100 });
const openedRoom = useOpenedRoom() ?? '';
const sidebarViewMode = useUserPreference<'extended' | 'medium' | 'condensed'>('sidebarViewMode') || 'extended';
@ -35,7 +53,7 @@ const RoomList = () => {
() => ({
extended,
t,
SideBarItemTemplate: sideBarItemTemplate,
SidebarItemTemplate: sideBarItemTemplate,
AvatarTemplate: avatarTemplate,
openedRoom,
sidebarViewMode,
@ -47,87 +65,16 @@ const RoomList = () => {
usePreventDefault(ref);
useShortcutOpenMenu(ref);
const roomsListStyle = css`
position: relative;
display: flex;
overflow-x: hidden;
overflow-y: hidden;
flex: 1 1 auto;
height: 100%;
&--embedded {
margin-top: 2rem;
}
&__list:not(:last-child) {
margin-bottom: 22px;
}
&__type {
display: flex;
flex-direction: row;
padding: 0 var(--sidebar-default-padding) 1rem var(--sidebar-default-padding);
color: var(--rooms-list-title-color);
font-size: var(--rooms-list-title-text-size);
align-items: center;
justify-content: space-between;
&-text--livechat {
flex: 1;
}
}
&__empty-room {
padding: 0 var(--sidebar-default-padding);
color: var(--rooms-list-empty-text-color);
font-size: var(--rooms-list-empty-text-size);
}
&__toolbar-search {
position: absolute;
z-index: 10;
left: 0;
overflow-y: scroll;
height: 100%;
background-color: var(--sidebar-background);
padding-block-start: 12px;
}
@media (max-width: 400px) {
padding: 0 calc(var(--sidebar-small-default-padding) - 4px);
&__type,
&__empty-room {
padding: 0 calc(var(--sidebar-small-default-padding) - 4px) 0.5rem calc(var(--sidebar-small-default-padding) - 4px);
}
}
`;
const { groupCounts, groupList, roomList } = getRoomsByGroup(roomsList);
return (
<Box className={[roomsListStyle, 'sidebar--custom-colors'].filter(Boolean)}>
<Box h='full' w='full' ref={ref}>
<Virtuoso
totalCount={roomsList.length}
data={roomsList}
components={{ Item: RoomListRowWrapper, List: RoomListWrapper, Scroller: VirtuosoScrollbars }}
computeItemKey={computeItemKey}
itemContent={(_, data): ReactElement => <RoomListRow data={itemData} item={data} />}
/>
</Box>
<Box position='relative' display='flex' overflow='hidden' height='full' flexGrow={1} flexShrink={1} flexBasis='auto' ref={ref}>
<GroupedVirtuoso
groupCounts={groupCounts}
groupContent={(index) => <SidebarV2GroupTitle title={t(groupList[index])} />}
itemContent={(index) => <RoomListRow data={itemData} item={roomList[index]} />}
components={{ Item: RoomListRowWrapper, List: RoomListWrapper, Scroller: VirtuosoScrollbars }}
/>
</Box>
);
};

@ -1,18 +1,17 @@
import type { IRoom, ISubscription } from '@rocket.chat/core-typings';
import { SidebarSection } from '@rocket.chat/fuselage';
import type { useTranslation } from '@rocket.chat/ui-contexts';
import React, { memo, useMemo } from 'react';
import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '../../contexts/VideoConfContext';
import type { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import type { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import SideBarItemTemplateWithData from './SideBarItemTemplateWithData';
import SidebarItemTemplateWithData from './SidebarItemTemplateWithData';
type RoomListRowProps = {
data: {
extended: boolean;
t: ReturnType<typeof useTranslation>;
SideBarItemTemplate: ReturnType<typeof useTemplateByViewMode>;
SidebarItemTemplate: ReturnType<typeof useTemplateByViewMode>;
AvatarTemplate: ReturnType<typeof useAvatarTemplate>;
openedRoom: string;
sidebarViewMode: 'extended' | 'condensed' | 'medium';
@ -22,7 +21,7 @@ type RoomListRowProps = {
};
const RoomListRow = ({ data, item }: RoomListRowProps) => {
const { extended, t, SideBarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data;
const { extended, t, SidebarItemTemplate, AvatarTemplate, openedRoom, sidebarViewMode } = data;
const acceptCall = useVideoConfAcceptCall();
const rejectCall = useVideoConfRejectIncomingCall();
@ -38,22 +37,14 @@ const RoomListRow = ({ data, item }: RoomListRowProps) => {
[acceptCall, rejectCall, currentCall],
);
if (typeof item === 'string') {
return (
<SidebarSection>
<SidebarSection.Title>{t(item)}</SidebarSection.Title>
</SidebarSection>
);
}
return (
<SideBarItemTemplateWithData
<SidebarItemTemplateWithData
sidebarViewMode={sidebarViewMode}
selected={item.rid === openedRoom}
t={t}
room={item}
extended={extended}
SideBarItemTemplate={SideBarItemTemplate}
SidebarItemTemplate={SidebarItemTemplate}
AvatarTemplate={AvatarTemplate}
videoConfActions={videoConfActions}
/>

@ -1,10 +1,11 @@
import { SidebarV2ListItem } from '@rocket.chat/fuselage';
import type { ForwardedRef, HTMLAttributes } from 'react';
import React, { forwardRef } from 'react';
type RoomListRoomWrapperProps = HTMLAttributes<HTMLDivElement>;
const RoomListRoomWrapper = forwardRef(function RoomListRoomWrapper(props: RoomListRoomWrapperProps, ref: ForwardedRef<HTMLDivElement>) {
return <div role='listitem' ref={ref} {...props} />;
return <SidebarV2ListItem ref={ref} {...props} />;
});
export default RoomListRoomWrapper;

@ -1,6 +1,6 @@
import type { IMessage, IRoom, ISubscription } from '@rocket.chat/core-typings';
import { isDirectMessageRoom, isMultipleDirectMessageRoom, isOmnichannelRoom, isVideoConfMessage } from '@rocket.chat/core-typings';
import { Badge, Sidebar, SidebarItemAction, SidebarItemActions, Margins } from '@rocket.chat/fuselage';
import { SidebarV2Action, SidebarV2Actions, SidebarV2ItemBadge, SidebarV2ItemIcon } from '@rocket.chat/fuselage';
import type { useTranslation } from '@rocket.chat/ui-contexts';
import { useLayout } from '@rocket.chat/ui-contexts';
import type { AllHTMLAttributes, ComponentType, ReactElement, ReactNode } from 'react';
@ -15,7 +15,7 @@ import { OmnichannelBadges } from '../badges/OmnichannelBadges';
import type { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import { normalizeSidebarMessage } from './normalizeSidebarMessage';
const getMessage = (room: IRoom, lastMessage: IMessage | undefined, t: ReturnType<typeof useTranslation>): string | undefined => {
export const getMessage = (room: IRoom, lastMessage: IMessage | undefined, t: ReturnType<typeof useTranslation>): string | undefined => {
if (!lastMessage) {
return t('No_messages_yet');
}
@ -34,7 +34,7 @@ const getMessage = (room: IRoom, lastMessage: IMessage | undefined, t: ReturnTyp
return `${lastMessage.u.name || lastMessage.u.username}: ${normalizeSidebarMessage(lastMessage, t)}`;
};
const getBadgeTitle = (
export const getBadgeTitle = (
userMentions: number,
threadUnread: number,
groupMentions: number,
@ -61,7 +61,7 @@ const getBadgeTitle = (
type RoomListRowProps = {
extended: boolean;
t: ReturnType<typeof useTranslation>;
SideBarItemTemplate: ComponentType<
SidebarItemTemplate: ComponentType<
{
icon: ReactNode;
title: ReactNode;
@ -98,13 +98,13 @@ type RoomListRowProps = {
};
};
const SideBarItemTemplateWithData = ({
const SidebarItemTemplateWithData = ({
room,
id,
selected,
style,
extended,
SideBarItemTemplate,
SidebarItemTemplate,
AvatarTemplate,
t,
isAnonymous,
@ -132,19 +132,19 @@ const SideBarItemTemplateWithData = ({
const highlighted = Boolean(!hideUnreadStatus && (alert || unread));
const icon = (
// TODO: Remove icon='at'
<Sidebar.Item.Icon highlighted={highlighted} icon='at'>
<RoomIcon room={room} placement='sidebar' isIncomingCall={Boolean(videoConfActions)} />
</Sidebar.Item.Icon>
<SidebarV2ItemIcon
highlighted={highlighted}
icon={<RoomIcon room={room} placement='sidebar' size='x20' isIncomingCall={Boolean(videoConfActions)} />}
/>
);
const actions = useMemo(
() =>
videoConfActions && (
<SidebarItemActions>
<SidebarItemAction onClick={videoConfActions.acceptCall} secondary success icon='phone' />
<SidebarItemAction onClick={videoConfActions.rejectCall} secondary danger icon='phone-off' />
</SidebarItemActions>
<SidebarV2Actions>
<SidebarV2Action onClick={videoConfActions.acceptCall} mini secondary success icon='phone' />
<SidebarV2Action onClick={videoConfActions.rejectCall} mini secondary danger icon='phone-off' />
</SidebarV2Actions>
),
[videoConfActions],
);
@ -165,18 +165,18 @@ const SideBarItemTemplateWithData = ({
const badgeTitle = getBadgeTitle(userMentions, tunread.length, groupMentions, unread, t);
const badges = (
<Margins inlineStart={8}>
<>
{showBadge && isUnread && (
<Badge {...({ style: { display: 'inline-flex', flexShrink: 0 } } as any)} variant={variant} title={badgeTitle}>
<SidebarV2ItemBadge variant={variant} title={badgeTitle}>
{unread + tunread?.length}
</Badge>
</SidebarV2ItemBadge>
)}
{isOmnichannelRoom(room) && <OmnichannelBadges room={room} />}
</Margins>
</>
);
return (
<SideBarItemTemplate
<SidebarItemTemplate
is='a'
id={id}
data-qa='sidebar-item'
@ -229,7 +229,7 @@ const keys: (keyof RoomListRowProps)[] = [
'style',
'extended',
'selected',
'SideBarItemTemplate',
'SidebarItemTemplate',
'AvatarTemplate',
't',
'sidebarViewMode',
@ -237,7 +237,7 @@ const keys: (keyof RoomListRowProps)[] = [
];
// eslint-disable-next-line react/no-multi-comp
export default memo(SideBarItemTemplateWithData, (prevProps, nextProps) => {
export default memo(SidebarItemTemplateWithData, (prevProps, nextProps) => {
if (keys.some((key) => prevProps[key] !== nextProps[key])) {
return false;
}

@ -1,8 +1,8 @@
import { useFocusManager } from '@react-aria/focus';
import { useCallback } from 'react';
const isListItem = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item');
const isListItemMenu = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-item__menu');
const isListItem = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-v2-item');
const isListItemMenu = (node: EventTarget) => (node as HTMLElement).classList.contains('rcx-sidebar-v2-item__menu');
/**
* Custom hook to provide the sidebar navigation by keyboard.

@ -1,5 +1,4 @@
import { css } from '@rocket.chat/css-in-js';
import { Box } from '@rocket.chat/fuselage';
import { SidebarV2 } from '@rocket.chat/fuselage';
import { useSessionStorage } from '@rocket.chat/fuselage-hooks';
import { useSetting, useUserPreference } from '@rocket.chat/ui-contexts';
import React, { memo } from 'react';
@ -15,31 +14,18 @@ const Sidebar = () => {
const [bannerDismissed, setBannerDismissed] = useSessionStorage('presence_cap_notifier', false);
const presenceDisabled = useSetting<boolean>('Presence_broadcast_disabled');
const sidebarLink = css`
a {
text-decoration: none;
}
`;
return (
<Box
display='flex'
flexDirection='column'
height='100%'
is='nav'
className={[
'rcx-sidebar--main',
`rcx-sidebar rcx-sidebar--${sidebarViewMode}`,
sidebarHideAvatar && 'rcx-sidebar--hide-avatar',
sidebarLink,
].filter(Boolean)}
<SidebarV2
aria-label='sidebar'
className={['rcx-sidebar--main', `rcx-sidebar rcx-sidebar--${sidebarViewMode}`, sidebarHideAvatar && 'rcx-sidebar--hide-avatar']
.filter(Boolean)
.join(' ')}
>
<SearchSection />
{presenceDisabled && !bannerDismissed && <StatusDisabledSection onDismiss={() => setBannerDismissed(true)} />}
<SidebarRoomList />
<SidebarFooter />
</Box>
</SidebarV2>
);
};

@ -34,7 +34,7 @@ const SearchList = ({ filterText, onEscSearch }: SearchListProps) => {
() => ({
items,
t,
SideBarItemTemplate: sideBarItemTemplate,
SidebarItemTemplate: sideBarItemTemplate,
avatarTemplate,
useRealName,
extended,

@ -1,5 +1,5 @@
import { css } from '@rocket.chat/css-in-js';
import { Box, Icon, TextInput, Palette, Sidebar } from '@rocket.chat/fuselage';
import { Box, Icon, TextInput, Palette, SidebarV2Section } from '@rocket.chat/fuselage';
import { useMergedRefs, useOutsideClick } from '@rocket.chat/fuselage-hooks';
import { useTranslation, useUser } from '@rocket.chat/ui-contexts';
import React, { useCallback, useEffect, useRef } from 'react';
@ -70,15 +70,7 @@ const SearchSection = () => {
return (
<Box className={['rcx-sidebar', isDirty && wrapperStyle]} ref={wrapperRef} role='search'>
<Box
pi={16}
h='x44'
display='flex'
alignItems='center'
className={css`
gap: 8px;
`}
>
<SidebarV2Section>
<TextInput
placeholder={t('Search')}
{...rest}
@ -94,8 +86,7 @@ const SearchSection = () => {
<CreateRoom />
</>
)}
</Box>
<Sidebar.Divider />
</SidebarV2Section>
{isDirty && <SearchList filterText={filterText} onEscSearch={handleEscSearch} />}
</Box>
);

@ -1,4 +1,4 @@
import { Sidebar } from '@rocket.chat/fuselage';
import { SidebarV2Action } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { HTMLAttributes } from 'react';
import React from 'react';
@ -13,7 +13,7 @@ const CreateRoom = (props: CreateRoomProps) => {
const sections = useCreateRoom();
return <GenericMenu icon='edit-rounded' sections={sections} title={t('Create_new')} is={Sidebar.TopBar.Action} {...props} />;
return <GenericMenu icon='edit-rounded' sections={sections} title={t('Create_new')} is={SidebarV2Action} {...props} />;
};
export default CreateRoom;

@ -1,50 +0,0 @@
import { Sidebar } from '@rocket.chat/fuselage';
import { useEffectEvent, useOutsideClick } from '@rocket.chat/fuselage-hooks';
import type { HTMLAttributes } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import tinykeys from 'tinykeys';
import SearchList from '../../search/SearchList';
type SearchProps = Omit<HTMLAttributes<HTMLElement>, 'is'>;
const Search = (props: SearchProps) => {
const [searchOpen, setSearchOpen] = useState(false);
const ref = useRef<HTMLElement>(null);
const handleCloseSearch = useEffectEvent(() => {
setSearchOpen(false);
});
useOutsideClick([ref], handleCloseSearch);
const openSearch = useEffectEvent(() => {
setSearchOpen(true);
});
useEffect(() => {
const unsubscribe = tinykeys(window, {
'$mod+K': (event) => {
event.preventDefault();
openSearch();
},
'$mod+P': (event) => {
event.preventDefault();
openSearch();
},
});
return (): void => {
unsubscribe();
};
}, [openSearch]);
return (
<>
<Sidebar.TopBar.Action icon='magnifier' onClick={openSearch} {...props} />
{searchOpen && <SearchList ref={ref} onClose={handleCloseSearch} />}
</>
);
};
export default Search;

@ -1,4 +1,4 @@
import { Sidebar } from '@rocket.chat/fuselage';
import { SidebarV2Action } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import type { HTMLAttributes } from 'react';
import React from 'react';
@ -13,9 +13,7 @@ const Sort = (props: SortProps) => {
const sections = useSortMenu();
return (
<GenericMenu icon='sort' sections={sections} title={t('Display')} selectionMode='multiple' is={Sidebar.TopBar.Action} {...props} />
);
return <GenericMenu icon='sort' sections={sections} title={t('Display')} selectionMode='multiple' is={SidebarV2Action} {...props} />;
};
export default Sort;

@ -18,7 +18,7 @@ export const useAvatarTemplate = (
return null;
}
const size = ((): 'x36' | 'x28' | 'x16' => {
const size = ((): 'x36' | 'x28' | 'x20' => {
switch (viewMode) {
case 'extended':
return 'x36';
@ -26,7 +26,7 @@ export const useAvatarTemplate = (
return 'x28';
case 'condensed':
default:
return 'x16';
return 'x20';
}
})();

@ -2,7 +2,7 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings';
import type { ReactElement } from 'react';
import React, { memo } from 'react';
import SideBarItemTemplateWithData from '../RoomList/SideBarItemTemplateWithData';
import SidebarItemTemplateWithData from '../RoomList/SidebarItemTemplateWithData';
import UserItem from './UserItem';
type RowProps = {
@ -11,7 +11,7 @@ type RowProps = {
};
const Row = ({ item, data }: RowProps): ReactElement => {
const { t, SideBarItemTemplate, avatarTemplate: AvatarTemplate, useRealName, extended } = data;
const { t, SidebarItemTemplate, avatarTemplate: AvatarTemplate, useRealName, extended } = data;
if (item.t === 'd' && !item.u) {
return (
@ -20,18 +20,18 @@ const Row = ({ item, data }: RowProps): ReactElement => {
useRealName={useRealName}
t={t}
item={item}
SideBarItemTemplate={SideBarItemTemplate}
SidebarItemTemplate={SidebarItemTemplate}
AvatarTemplate={AvatarTemplate}
/>
);
}
return (
<SideBarItemTemplateWithData
<SidebarItemTemplateWithData
id={`search-${item._id}`}
extended={extended}
t={t}
room={item}
SideBarItemTemplate={SideBarItemTemplate}
SidebarItemTemplate={SidebarItemTemplate}
AvatarTemplate={AvatarTemplate}
/>
);

@ -1,382 +0,0 @@
import type { IRoom, ISubscription } from '@rocket.chat/core-typings';
import { css } from '@rocket.chat/css-in-js';
import { Sidebar, TextInput, Box, Icon } from '@rocket.chat/fuselage';
import { useMutableCallback, useDebouncedValue, useAutoFocus, useUniqueId, useMergedRefs } from '@rocket.chat/fuselage-hooks';
import { escapeRegExp } from '@rocket.chat/string-helpers';
import { useUserPreference, useUserSubscriptions, useSetting, useTranslation, useMethod } from '@rocket.chat/ui-contexts';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import type {
ReactElement,
MutableRefObject,
SetStateAction,
Dispatch,
FormEventHandler,
Ref,
MouseEventHandler,
ForwardedRef,
} from 'react';
import React, { forwardRef, useState, useMemo, useEffect, useRef } from 'react';
import type { VirtuosoHandle } from 'react-virtuoso';
import { Virtuoso } from 'react-virtuoso';
import tinykeys from 'tinykeys';
import { VirtuosoScrollbars } from '../../components/CustomScrollbars';
import { getConfig } from '../../lib/utils/getConfig';
import { useAvatarTemplate } from '../hooks/useAvatarTemplate';
import { usePreventDefault } from '../hooks/usePreventDefault';
import { useTemplateByViewMode } from '../hooks/useTemplateByViewMode';
import Row from './Row';
const mobileCheck = function () {
let check = false;
(function (a: string) {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
a,
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
a.substr(0, 4),
)
)
check = true;
})(navigator.userAgent || navigator.vendor || window.opera || '');
return check;
};
declare global {
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Window {
opera?: string;
}
// eslint-disable-next-line @typescript-eslint/naming-convention
interface Navigator {
userAgentData?: {
mobile: boolean;
};
}
}
const shortcut = ((): string => {
if (navigator.userAgentData?.mobile || mobileCheck()) {
return '';
}
if (window.navigator.platform.toLowerCase().includes('mac')) {
return '(\u2318+K)';
}
return '(Ctrl+K)';
})();
const LIMIT = parseInt(String(getConfig('Sidebar_Search_Spotlight_LIMIT', 20)));
const options = {
sort: {
lm: -1,
name: 1,
},
limit: LIMIT,
} as const;
const useSearchItems = (filterText: string): UseQueryResult<(ISubscription & IRoom)[] | undefined, Error> => {
const [, mention, name] = useMemo(() => filterText.match(/(@|#)?(.*)/i) || [], [filterText]);
const query = useMemo(() => {
const filterRegex = new RegExp(escapeRegExp(name), 'i');
return {
$or: [{ name: filterRegex }, { fname: filterRegex }],
...(mention && {
t: mention === '@' ? 'd' : { $ne: 'd' },
}),
};
}, [name, mention]);
const localRooms = useUserSubscriptions(query, options);
const usernamesFromClient = [...localRooms?.map(({ t, name }) => (t === 'd' ? name : null))].filter(Boolean) as string[];
const searchForChannels = mention === '#';
const searchForDMs = mention === '@';
const type = useMemo(() => {
if (searchForChannels) {
return { users: false, rooms: true, includeFederatedRooms: true };
}
if (searchForDMs) {
return { users: true, rooms: false };
}
return { users: true, rooms: true, includeFederatedRooms: true };
}, [searchForChannels, searchForDMs]);
const getSpotlight = useMethod('spotlight');
return useQuery(
['sidebar/search/spotlight', name, usernamesFromClient, type, localRooms.map(({ _id, name }) => _id + name)],
async () => {
if (localRooms.length === LIMIT) {
return localRooms;
}
const spotlight = await getSpotlight(name, usernamesFromClient, type);
const filterUsersUnique = ({ _id }: { _id: string }, index: number, arr: { _id: string }[]): boolean =>
index === arr.findIndex((user) => _id === user._id);
const roomFilter = (room: { t: string; uids?: string[]; _id: string; name?: string }): boolean =>
!localRooms.find(
(item) =>
(room.t === 'd' && room.uids && room.uids.length > 1 && room.uids?.includes(item._id)) ||
[item.rid, item._id].includes(room._id),
);
const usersFilter = (user: { _id: string }): boolean =>
!localRooms.find((room) => room.t === 'd' && room.uids && room.uids?.length === 2 && room.uids.includes(user._id));
const userMap = (user: {
_id: string;
name: string;
username: string;
avatarETag?: string;
}): {
_id: string;
t: string;
name: string;
fname: string;
avatarETag?: string;
} => ({
_id: user._id,
t: 'd',
name: user.username,
fname: user.name,
avatarETag: user.avatarETag,
});
type resultsFromServerType = {
_id: string;
t: string;
name: string;
teamMain?: boolean;
fname?: string;
avatarETag?: string | undefined;
uids?: string[] | undefined;
}[];
const resultsFromServer: resultsFromServerType = [];
resultsFromServer.push(...spotlight.users.filter(filterUsersUnique).filter(usersFilter).map(userMap));
resultsFromServer.push(...spotlight.rooms.filter(roomFilter));
const exact = resultsFromServer?.filter((item) => [item.name, item.fname].includes(name));
return Array.from(new Set([...exact, ...localRooms, ...resultsFromServer]));
},
{
staleTime: 60_000,
keepPreviousData: true,
placeholderData: localRooms,
},
);
};
const useInput = (initial: string): { value: string; onChange: FormEventHandler; setValue: Dispatch<SetStateAction<string>> } => {
const [value, setValue] = useState(initial);
const onChange = useMutableCallback((e) => {
setValue(e.currentTarget.value);
});
return { value, onChange, setValue };
};
const toggleSelectionState = (next: HTMLElement, current: HTMLElement | undefined, input: HTMLElement | undefined): void => {
input?.setAttribute('aria-activedescendant', next.id);
next.setAttribute('aria-selected', 'true');
next.classList.add('rcx-sidebar-item--selected');
if (current) {
current.removeAttribute('aria-selected');
current.classList.remove('rcx-sidebar-item--selected');
}
};
type SearchListProps = {
onClose: () => void;
};
const SearchList = forwardRef(function SearchList({ onClose }: SearchListProps, ref: ForwardedRef<any>) {
const listId = useUniqueId();
const t = useTranslation();
const { setValue: setFilterValue, ...filter } = useInput('');
const cursorRef = useRef<HTMLInputElement>(null);
const autofocus: Ref<HTMLInputElement> = useMergedRefs(useAutoFocus<HTMLInputElement>(), cursorRef);
const listRef = useRef<VirtuosoHandle>(null);
const boxRef = useRef<HTMLDivElement>(null);
const selectedElement: MutableRefObject<HTMLElement | null | undefined> = useRef(null);
const itemIndexRef = useRef(0);
const sidebarViewMode = useUserPreference('sidebarViewMode');
const useRealName = useSetting('UI_Use_Real_Name');
const sideBarItemTemplate = useTemplateByViewMode();
const avatarTemplate = useAvatarTemplate();
const extended = sidebarViewMode === 'extended';
const filterText = useDebouncedValue(filter.value, 100);
const placeholder = [t('Search'), shortcut].filter(Boolean).join(' ');
const { data: items = [], isLoading } = useSearchItems(filterText);
const itemData = useMemo(
() => ({
items,
t,
SideBarItemTemplate: sideBarItemTemplate,
avatarTemplate,
useRealName,
extended,
sidebarViewMode,
}),
[avatarTemplate, extended, items, useRealName, sideBarItemTemplate, sidebarViewMode, t],
);
const changeSelection = useMutableCallback((dir) => {
let nextSelectedElement = null;
if (dir === 'up') {
const potentialElement = selectedElement.current?.parentElement?.previousSibling as HTMLElement;
if (potentialElement) {
nextSelectedElement = potentialElement.querySelector('a');
}
} else {
const potentialElement = selectedElement.current?.parentElement?.nextSibling as HTMLElement;
if (potentialElement) {
nextSelectedElement = potentialElement.querySelector('a');
}
}
if (nextSelectedElement) {
toggleSelectionState(nextSelectedElement, selectedElement.current || undefined, cursorRef?.current || undefined);
return nextSelectedElement;
}
return selectedElement.current;
});
const resetCursor = useMutableCallback(() => {
setTimeout(() => {
itemIndexRef.current = 0;
listRef.current?.scrollToIndex({ index: itemIndexRef.current });
selectedElement.current = boxRef.current?.querySelector('a.rcx-sidebar-item');
if (selectedElement.current) {
toggleSelectionState(selectedElement.current, undefined, cursorRef?.current || undefined);
}
}, 0);
});
usePreventDefault(boxRef);
useEffect(() => {
resetCursor();
});
useEffect(() => {
resetCursor();
}, [filterText, resetCursor]);
useEffect(() => {
if (!cursorRef?.current) {
return;
}
return tinykeys(cursorRef?.current, {
Escape: (event) => {
event.preventDefault();
setFilterValue((value) => {
if (!value) {
onClose();
}
resetCursor();
return '';
});
},
Tab: onClose,
ArrowUp: () => {
const currentElement = changeSelection('up');
itemIndexRef.current = Math.max(itemIndexRef.current - 1, 0);
listRef.current?.scrollToIndex({ index: itemIndexRef.current });
selectedElement.current = currentElement;
},
ArrowDown: () => {
const currentElement = changeSelection('down');
itemIndexRef.current = Math.min(itemIndexRef.current + 1, items.length + 1);
listRef.current?.scrollToIndex({ index: itemIndexRef.current });
selectedElement.current = currentElement;
},
Enter: (event) => {
event.preventDefault();
if (selectedElement.current && items.length > 0) {
selectedElement.current.click();
} else {
onClose();
}
},
});
}, [cursorRef, changeSelection, items.length, onClose, resetCursor, setFilterValue]);
const handleClick: MouseEventHandler<HTMLElement> = (e): void => {
if (e.target instanceof Element && [e.target.tagName, e.target.parentElement?.tagName].includes('BUTTON')) {
return;
}
return onClose();
};
return (
<Box
position='absolute'
rcx-sidebar
h='full'
display='flex'
flexDirection='column'
zIndex={99}
w='full'
className={css`
left: 0;
top: 0;
`}
ref={ref}
role='search'
>
<Sidebar.TopBar.Section {...({ flexShrink: 0 } as any)} is='form'>
<TextInput
aria-owns={listId}
ref={autofocus}
{...filter}
placeholder={placeholder}
role='searchbox'
addon={<Icon name='cross' size='x20' onClick={onClose} />}
/>
</Sidebar.TopBar.Section>
<Box
ref={boxRef}
role='listbox'
id={listId}
tabIndex={-1}
flexShrink={1}
h='full'
w='full'
aria-live='polite'
aria-atomic='true'
aria-busy={isLoading}
onClick={handleClick}
>
<Virtuoso
style={{ height: '100%', width: '100%' }}
totalCount={items.length}
data={items}
components={{ Scroller: VirtuosoScrollbars }}
computeItemKey={(_, room) => room._id}
itemContent={(_, data): ReactElement => <Row data={itemData} item={data} />}
ref={listRef}
/>
</Box>
</Box>
);
});
export default SearchList;

@ -1,5 +1,5 @@
import type { IUser } from '@rocket.chat/core-typings';
import { Sidebar } from '@rocket.chat/fuselage';
import { SidebarV2ItemIcon } from '@rocket.chat/fuselage';
import React, { memo } from 'react';
import { ReactiveUserStatus } from '../../components/UserStatus';
@ -13,24 +13,20 @@ type UserItemProps = {
t: string;
};
t: (value: string) => string;
SideBarItemTemplate: any;
SidebarItemTemplate: any;
AvatarTemplate: any;
id: string;
style?: CSSStyleRule;
useRealName?: boolean;
};
const UserItem = ({ item, id, style, t, SideBarItemTemplate, AvatarTemplate, useRealName }: UserItemProps) => {
const UserItem = ({ item, id, style, t, SidebarItemTemplate, AvatarTemplate, useRealName }: UserItemProps) => {
const title = useRealName ? item.fname || item.name : item.name || item.fname;
const icon = (
<Sidebar.Item.Icon icon={'' as any}>
<ReactiveUserStatus uid={item._id} />
</Sidebar.Item.Icon>
);
const icon = <SidebarV2ItemIcon icon={<ReactiveUserStatus uid={item._id} />} />;
const href = roomCoordinator.getRouteLink(item.t, { name: item.name });
return (
<SideBarItemTemplate
<SidebarItemTemplate
is='a'
style={{ height: '100%', ...style }}
id={id}

@ -1,4 +1,4 @@
import { SidebarBanner } from '@rocket.chat/fuselage';
import { SidebarV2Banner } from '@rocket.chat/fuselage';
import { useTranslation } from '@rocket.chat/ui-contexts';
import React from 'react';
@ -11,11 +11,11 @@ const StatusDisabledSection = ({ onDismiss }: StatusDisabledSectionProps) => {
const handleStatusDisabledModal = useStatusDisabledModal();
return (
<SidebarBanner
text={t('User_status_temporarily_disabled')}
description={t('Learn_more')}
<SidebarV2Banner
title={t('User_status_temporarily_disabled')}
linkText={t('Learn_more')}
onClose={onDismiss}
onClick={handleStatusDisabledModal}
linkProps={{ onClick: handleStatusDisabledModal, tabIndex: 0 }}
/>
);
};

@ -241,7 +241,7 @@
"@rocket.chat/favicon": "workspace:^",
"@rocket.chat/forked-matrix-appservice-bridge": "^4.0.2",
"@rocket.chat/forked-matrix-bot-sdk": "^0.6.0-beta.3",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.33.0",

@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/ui-contexts": "workspace:~",

@ -66,7 +66,7 @@
"@rocket.chat/apps-engine": "1.45.0-alpha.868",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/icons": "~0.38.0",

@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@rocket.chat/core-typings": "workspace:^",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-tokens": "^0.33.1",
"@rocket.chat/jest-presets": "workspace:~",
"@rocket.chat/message-parser": "workspace:^",

@ -4,7 +4,7 @@
"private": true,
"devDependencies": {
"@babel/core": "~7.22.20",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/ui-contexts": "workspace:^",
"@types/babel__core": "~7.20.3",
"@types/react": "~17.0.69",

@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/jest-presets": "workspace:~",

@ -19,7 +19,7 @@
"@babel/core": "~7.22.20",
"@react-aria/toolbar": "^3.0.0-beta.1",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/icons": "~0.38.0",
"@storybook/addon-actions": "~6.5.16",
"@storybook/addon-docs": "~6.5.16",

@ -6,7 +6,7 @@
"@babel/core": "~7.22.20",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/eslint-config": "workspace:^",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/icons": "~0.38.0",
"@rocket.chat/jest-presets": "workspace:~",

@ -15,7 +15,7 @@
"@codemirror/tooltip": "^0.19.16",
"@lezer/highlight": "^1.1.6",
"@rocket.chat/css-in-js": "~0.31.25",
"@rocket.chat/fuselage": "^0.57.1",
"@rocket.chat/fuselage": "^0.59.0",
"@rocket.chat/fuselage-hooks": "^0.33.1",
"@rocket.chat/fuselage-polyfills": "~0.31.25",
"@rocket.chat/fuselage-toastbar": "^0.33.0",

@ -8883,7 +8883,7 @@ __metadata:
"@rocket.chat/apps-engine": 1.45.0-alpha.868
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/fuselage-polyfills": ~0.31.25
"@rocket.chat/gazzodown": "workspace:^"
@ -8944,9 +8944,9 @@ __metadata:
languageName: unknown
linkType: soft
"@rocket.chat/fuselage@npm:^0.57.1":
version: 0.57.1
resolution: "@rocket.chat/fuselage@npm:0.57.1"
"@rocket.chat/fuselage@npm:^0.59.0":
version: 0.59.0
resolution: "@rocket.chat/fuselage@npm:0.59.0"
dependencies:
"@rocket.chat/css-in-js": ^0.31.25
"@rocket.chat/css-supports": ^0.31.25
@ -8964,7 +8964,7 @@ __metadata:
react: ^17.0.2
react-dom: ^17.0.2
react-virtuoso: 1.2.4
checksum: ed40c4e9ec6f6294e0e7c7a3912ae7c9eca026455506f3f1983483010d3d0c41169f9e38d173e5e63ed0e9824979edd607dda3c881202bf797a97b5b76e83a34
checksum: 259dce5381a3c3e0d7c7f3dc7ab51346cb65a9f4906a5ca5d6a976627d05e01e7f8a3a940604d0ad1b2b4ed89c250a871ef3fb253f6bbb69d35bc931e193898d
languageName: node
linkType: hard
@ -8975,7 +8975,7 @@ __metadata:
"@babel/core": ~7.22.20
"@rocket.chat/core-typings": "workspace:^"
"@rocket.chat/css-in-js": ~0.31.25
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-tokens": ^0.33.1
"@rocket.chat/jest-presets": "workspace:~"
"@rocket.chat/message-parser": "workspace:^"
@ -9342,7 +9342,7 @@ __metadata:
"@rocket.chat/favicon": "workspace:^"
"@rocket.chat/forked-matrix-appservice-bridge": ^4.0.2
"@rocket.chat/forked-matrix-bot-sdk": ^0.6.0-beta.3
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/fuselage-polyfills": ~0.31.25
"@rocket.chat/fuselage-toastbar": ^0.33.0
@ -10214,7 +10214,7 @@ __metadata:
resolution: "@rocket.chat/ui-avatar@workspace:packages/ui-avatar"
dependencies:
"@babel/core": ~7.22.20
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/ui-contexts": "workspace:^"
"@types/babel__core": ~7.20.3
"@types/react": ~17.0.69
@ -10240,7 +10240,7 @@ __metadata:
"@babel/core": ~7.22.20
"@react-aria/toolbar": ^3.0.0-beta.1
"@rocket.chat/css-in-js": ~0.31.25
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/icons": ~0.38.0
"@rocket.chat/jest-presets": "workspace:~"
@ -10290,7 +10290,7 @@ __metadata:
"@babel/core": ~7.22.20
"@react-aria/toolbar": ^3.0.0-beta.1
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/icons": ~0.38.0
"@storybook/addon-actions": ~6.5.16
"@storybook/addon-docs": ~6.5.16
@ -10385,7 +10385,7 @@ __metadata:
resolution: "@rocket.chat/ui-theming@workspace:ee/packages/ui-theming"
dependencies:
"@rocket.chat/css-in-js": ~0.31.25
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/icons": ~0.38.0
"@rocket.chat/ui-contexts": "workspace:~"
@ -10415,7 +10415,7 @@ __metadata:
"@rocket.chat/css-in-js": ~0.31.25
"@rocket.chat/emitter": ~0.31.25
"@rocket.chat/eslint-config": "workspace:^"
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/icons": ~0.38.0
"@rocket.chat/jest-presets": "workspace:~"
@ -10464,7 +10464,7 @@ __metadata:
"@codemirror/tooltip": ^0.19.16
"@lezer/highlight": ^1.1.6
"@rocket.chat/css-in-js": ~0.31.25
"@rocket.chat/fuselage": ^0.57.1
"@rocket.chat/fuselage": ^0.59.0
"@rocket.chat/fuselage-hooks": ^0.33.1
"@rocket.chat/fuselage-polyfills": ~0.31.25
"@rocket.chat/fuselage-toastbar": ^0.33.0

Loading…
Cancel
Save