Web: Shared: Adapt tabs to selector for people/groups

This commit is contained in:
Aleksandr Lushkin 2024-02-13 16:20:36 +01:00
parent 1fe703a0c9
commit 16cc8962ee
5 changed files with 91 additions and 5 deletions

View File

@ -6,6 +6,7 @@ import { Base } from "../../themes";
import { ComboBox } from "../combobox"; import { ComboBox } from "../combobox";
import { Text } from "../text"; import { Text } from "../text";
import { Submenu } from "../submenu";
const StyledSelector = styled.div` const StyledSelector = styled.div`
width: 100%; width: 100%;
@ -53,6 +54,7 @@ const StyledBody = styled.div<{
withHeader?: boolean; withHeader?: boolean;
footerHeight: number; footerHeight: number;
headerHeight: number; headerHeight: number;
withTabs?: boolean;
}>` }>`
width: 100%; width: 100%;
@ -65,7 +67,7 @@ const StyledBody = styled.div<{
? `calc(100% - 16px - ${props.headerHeight}px)` ? `calc(100% - 16px - ${props.headerHeight}px)`
: `calc(100% - 16px)`}; : `calc(100% - 16px)`};
padding: 16px 0 0 0; padding: ${({ withTabs }) => (withTabs ? "8px 0 0 0" : "16px 0 0 0")};
.search-input, .search-input,
.search-loader { .search-loader {
@ -360,6 +362,15 @@ const StyledComboBox = styled(ComboBox)`
} }
`; `;
const StyledTabs = styled(Submenu)`
padding: 0 16px;
margin-bottom: 16px;
.sticky-indent {
height: 0;
}
`;
StyledSelector.defaultProps = { theme: Base }; StyledSelector.defaultProps = { theme: Base };
StyledHeader.defaultProps = { theme: Base }; StyledHeader.defaultProps = { theme: Base };
StyledBody.defaultProps = { theme: Base }; StyledBody.defaultProps = { theme: Base };
@ -384,4 +395,5 @@ export {
StyledNewNameHeader, StyledNewNameHeader,
StyledButtonContainer, StyledButtonContainer,
StyledComboBox, StyledComboBox,
StyledTabs,
}; };

View File

@ -83,7 +83,12 @@ const Selector = ({
cancelButtonId, cancelButtonId,
isChecked, isChecked,
setIsChecked, setIsChecked,
withTabs,
tabsData,
activeTabId,
}: SelectorProps) => { }: SelectorProps) => {
const [areItemsUpdated, setAreItemsUpdated] = React.useState(false);
const [footerVisible, setFooterVisible] = React.useState<boolean>(false); const [footerVisible, setFooterVisible] = React.useState<boolean>(false);
const [isSearch, setIsSearch] = React.useState<boolean>(false); const [isSearch, setIsSearch] = React.useState<boolean>(false);
@ -304,6 +309,37 @@ const Selector = ({
compareSelectedItems(cloneSelectedItems); compareSelectedItems(cloneSelectedItems);
} }
}, [items, selectedItems, isMultiSelect, compareSelectedItems]); }, [items, selectedItems, isMultiSelect, compareSelectedItems]);
React.useEffect(() => {
if (!areItemsUpdated) return;
if (!newSelectedItems.length || !isMultiSelect || !items) {
setAreItemsUpdated(false);
return;
}
let hasConflict = false;
const cloneItems = items.map((x) => {
if (x.isSelected) return { ...x };
const isSelected = newSelectedItems.some(
(selectedItem) => selectedItem.id === x.id,
);
if (isSelected) hasConflict = true;
return { ...x, isSelected };
});
if (hasConflict) {
setRenderedItems(cloneItems);
}
setAreItemsUpdated(false);
}, [areItemsUpdated, isMultiSelect, items, newSelectedItems]);
React.useEffect(() => {
setAreItemsUpdated(true);
}, [items]);
return ( return (
<StyledSelector <StyledSelector
id={id} id={id}
@ -366,6 +402,9 @@ const Selector = ({
withFooterInput={withFooterInput} withFooterInput={withFooterInput}
withFooterCheckbox={withFooterCheckbox} withFooterCheckbox={withFooterCheckbox}
descriptionText={descriptionText} descriptionText={descriptionText}
withTabs={withTabs}
tabsData={tabsData}
activeTabId={activeTabId}
/> />
{(footerVisible || alwaysShowFooter) && ( {(footerVisible || alwaysShowFooter) && (
@ -415,6 +454,7 @@ Selector.defaultProps = {
alwaysShowFooter: false, alwaysShowFooter: false,
disableAcceptButton: false, disableAcceptButton: false,
withHeader: true, withHeader: true,
withTabs: false,
selectedItems: [], selectedItems: [],
}; };

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { RoomsType } from "../../enums"; import { RoomsType } from "../../enums";
import { AvatarRole } from "../avatar"; import { AvatarRole } from "../avatar";
import { TSubmenuItem } from "../submenu";
export type AccessRight = { export type AccessRight = {
key: string; key: string;
@ -84,6 +85,10 @@ export interface SelectorProps {
cancelButtonId?: string; cancelButtonId?: string;
isChecked?: boolean; isChecked?: boolean;
setIsChecked?: React.Dispatch<React.SetStateAction<boolean>>; setIsChecked?: React.Dispatch<React.SetStateAction<boolean>>;
withTabs?: boolean;
tabsData?: TSubmenuItem[];
activeTabId?: number;
} }
export interface HeaderProps { export interface HeaderProps {
@ -135,6 +140,10 @@ export interface BodyProps {
withFooterCheckbox?: boolean; withFooterCheckbox?: boolean;
descriptionText?: string; descriptionText?: string;
withTabs?: boolean;
tabsData?: TSubmenuItem[];
activeTabId?: number;
} }
export interface FooterProps { export interface FooterProps {
@ -177,6 +186,7 @@ export type TSelectorItem = {
isDisabled?: boolean; isDisabled?: boolean;
color?: string; color?: string;
fileExst?: string; fileExst?: string;
isGroup?: boolean;
roomType?: RoomsType; roomType?: RoomsType;
shared: boolean; shared: boolean;
}; };

View File

@ -11,7 +11,7 @@ import { SelectAll } from "./SelectAll";
import { EmptyScreen } from "./EmptyScreen"; import { EmptyScreen } from "./EmptyScreen";
import { BreadCrumbs } from "./BreadCrumbs"; import { BreadCrumbs } from "./BreadCrumbs";
import { StyledBody } from "../Selector.styled"; import { StyledBody, StyledTabs } from "../Selector.styled";
import { BodyProps } from "../Selector.types"; import { BodyProps } from "../Selector.types";
import { Item } from "./Item"; import { Item } from "./Item";
@ -65,6 +65,10 @@ const Body = ({
withFooterCheckbox, withFooterCheckbox,
descriptionText, descriptionText,
withHeader, withHeader,
withTabs,
tabsData,
activeTabId,
}: BodyProps) => { }: BodyProps) => {
const [bodyHeight, setBodyHeight] = React.useState(0); const [bodyHeight, setBodyHeight] = React.useState(0);
@ -152,6 +156,7 @@ const Body = ({
headerHeight={HEADER_HEIGHT} headerHeight={HEADER_HEIGHT}
footerVisible={footerVisible} footerVisible={footerVisible}
withHeader={withHeader} withHeader={withHeader}
withTabs={withTabs}
> >
{withBreadCrumbs ? ( {withBreadCrumbs ? (
isBreadCrumbsLoading ? ( isBreadCrumbsLoading ? (
@ -165,6 +170,14 @@ const Body = ({
) )
) : null} ) : null}
{withTabs && tabsData && (
<StyledTabs
startSelect={0}
data={tabsData}
forsedActiveItemId={activeTabId}
/>
)}
{isSearchLoading || isBreadCrumbsLoading ? ( {isSearchLoading || isBreadCrumbsLoading ? (
searchLoader searchLoader
) : withSearch || isSearch || (itemsCount > 0 && withSearch) ? ( ) : withSearch || isSearch || (itemsCount > 0 && withSearch) ? (

View File

@ -47,8 +47,17 @@ const Item = React.memo(({ index, style, data }: ItemProps) => {
if (!item || (item && !item.id)) if (!item || (item && !item.id))
return <div style={style}>{rowLoader}</div>; return <div style={style}>{rowLoader}</div>;
const { label, avatar, icon, role, isSelected, isDisabled, color, email } = const {
item; label,
avatar,
icon,
role,
isSelected,
isDisabled,
color,
email,
isGroup,
} = item;
const currentRole = role || AvatarRole.user; const currentRole = role || AvatarRole.user;
@ -87,6 +96,8 @@ const Item = React.memo(({ index, style, data }: ItemProps) => {
source={avatar || ""} source={avatar || ""}
role={currentRole} role={currentRole}
size={AvatarSize.min} size={AvatarSize.min}
isGroup={isGroup}
userName={isGroup ? label : ""}
/> />
) : ( ) : (
<RoomIcon <RoomIcon
@ -98,7 +109,7 @@ const Item = React.memo(({ index, style, data }: ItemProps) => {
/> />
)} )}
{renderCustomItem ? ( {renderCustomItem ? (
renderCustomItem(label, typeLabel, email) renderCustomItem(label, typeLabel, email, isGroup)
) : ( ) : (
<Text <Text
className="label" className="label"