Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions client/components/FilterByText.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ import { useTranslation } from '../contexts/TranslationContext';
type FilterByTextProps = {
placeholder?: string;
onChange: (filter: { text: string }) => void;
displayButton: boolean;
inputRef?: () => void;
};

type FilterByTextPropsWithButton = FilterByTextProps & {
displayButton: true;
textButton: string;
onButtonClick: () => void;
inputRef: () => void;
};
const isFilterByTextPropsWithButton = (props: any): props is FilterByTextPropsWithButton =>
'displayButton' in props && props.displayButton === true;

const FilterByText: FC<FilterByTextProps> = ({
placeholder,
onChange: setFilter,
displayButton: display = false,
textButton = '',
onButtonClick,
inputRef,
children: _,
...props
}) => {
const t = useTranslation();
Expand Down Expand Up @@ -53,9 +56,11 @@ const FilterByText: FC<FilterByTextProps> = ({
onChange={handleInputChange}
value={text}
/>
<Button onClick={onButtonClick} display={display ? 'block' : 'none'} mis='x8' primary>
{textButton}
</Button>
{isFilterByTextPropsWithButton(props) && (
<Button onClick={props.onButtonClick} mis='x8' primary>
{props.textButton}
</Button>
)}
</Box>
);
};
Expand Down
95 changes: 37 additions & 58 deletions client/components/GenericTable/GenericTable.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import { Box, Pagination, Table, Tile } from '@rocket.chat/fuselage';
import { Pagination, Tile } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import React, {
useState,
useEffect,
useCallback,
forwardRef,
ReactNode,
ReactElement,
Key,
RefAttributes,
useMemo,
Ref,
} from 'react';
import flattenChildren from 'react-keyed-flatten-children';

import { useTranslation } from '../../contexts/TranslationContext';
import ScrollableContentWrapper from '../ScrollableContentWrapper';
import LoadingRow from './LoadingRow';
import { GenericTable as GenericTableV2 } from './V2/GenericTable';
import { GenericTableBody } from './V2/GenericTableBody';
import { GenericTableHeader } from './V2/GenericTableHeader';
import { GenericTableLoadingTable } from './V2/GenericTableLoadingTable';
import { usePagination } from './hooks/usePagination';

const defaultParamsValue = { text: '', current: 0, itemsPerPage: 25 } as const;
const defaultSetParamsValue = (): void => undefined;
Expand All @@ -37,14 +40,10 @@ type GenericTableProps<
pagination?: boolean;
} & FilterProps;

const GenericTable: {
<
FilterProps extends { onChange?: (params: GenericTableParams) => void },
ResultProps extends { _id?: Key },
>(
props: GenericTableProps<FilterProps, ResultProps> & RefAttributes<HTMLElement>,
): ReactElement | null;
} = forwardRef(function GenericTable(
const GenericTable = forwardRef(function GenericTable<
FilterProps extends { onChange?: (params: GenericTableParams) => void },
ResultProps extends { _id?: Key },
>(
{
children,
fixed = true,
Expand All @@ -57,41 +56,31 @@ const GenericTable: {
total,
pagination = true,
...props
},
ref,
}: GenericTableProps<FilterProps, ResultProps>,
ref: Ref<HTMLElement>,
) {
const t = useTranslation();

const [filter, setFilter] = useState(paramsDefault);

const [itemsPerPage, setItemsPerPage] = useState<25 | 50 | 100>(25);

const [current, setCurrent] = useState(0);
const {
itemsPerPage,
setItemsPerPage,
current,
setCurrent,
itemsPerPageLabel,
showingResultsLabel,
} = usePagination();

const params = useDebouncedValue(filter, 500);

useEffect(() => {
setParams({ ...params, current, itemsPerPage });
setParams({ text: params.text || '', current, itemsPerPage });
}, [params, current, itemsPerPage, setParams]);

const Loading = useCallback(() => {
const headerCells = flattenChildren(header);
return (
<>
{Array.from({ length: 10 }, (_, i) => (
<LoadingRow key={i} cols={headerCells.length} />
))}
</>
);
}, [header]);

const showingResultsLabel = useCallback(
({ count, current, itemsPerPage }) =>
t('Showing_results_of', current + 1, Math.min(current + itemsPerPage, count), count),
[t],
);
const headerCells = useMemo(() => flattenChildren(header).length, [header]);

const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), [t]);
const isLoading = !results;

return (
<>
Expand All @@ -104,28 +93,18 @@ const GenericTable: {
</Tile>
) : (
<>
<Box mi='neg-x24' pi='x24' flexShrink={1} flexGrow={1} ref={ref} overflow='hidden'>
<ScrollableContentWrapper overflowX>
<Table fixed={fixed} sticky>
{header && (
<Table.Head>
<Table.Row>{header}</Table.Row>
</Table.Head>
)}
<Table.Body>
{RenderRow &&
(results ? (
results.map((props, index) => (
<RenderRow key={props._id || index} {...props} />
))
) : (
<Loading />
))}
{children && (results ? results.map(children) : <Loading />)}
</Table.Body>
</Table>
</ScrollableContentWrapper>
</Box>
<GenericTableV2 fixed={fixed} ref={ref}>
{header && <GenericTableHeader>{header}</GenericTableHeader>}
<GenericTableBody>
{isLoading && <GenericTableLoadingTable headerCells={headerCells} />}
{!isLoading &&
((RenderRow &&
results?.map((props, index: number) => (
<RenderRow key={props._id || index} {...props} />
))) ||
(children && results?.map(children)))}
</GenericTableBody>
</GenericTableV2>
{pagination && (
<Pagination
divider
Expand Down
24 changes: 24 additions & 0 deletions client/components/GenericTable/V2/GenericTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Box, Table } from '@rocket.chat/fuselage';
import React, { forwardRef, ReactNode } from 'react';

import ScrollableContentWrapper from '../../ScrollableContentWrapper';

type GenericTableProps = {
fixed?: boolean;
children: ReactNode;
};

export const GenericTable = forwardRef<HTMLElement, GenericTableProps>(function GenericTable(
{ fixed = true, children },
ref,
) {
return (
<Box mi='neg-x24' pi='x24' flexShrink={1} flexGrow={1} ref={ref} overflow='hidden'>
<ScrollableContentWrapper overflowX>
<Table fixed={fixed} sticky>
{children}
</Table>
</ScrollableContentWrapper>
</Box>
);
});
4 changes: 4 additions & 0 deletions client/components/GenericTable/V2/GenericTableBody.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { Table } from '@rocket.chat/fuselage';
import React, { FC } from 'react';

export const GenericTableBody: FC = (props) => <Table.Body {...props} />;
6 changes: 6 additions & 0 deletions client/components/GenericTable/V2/GenericTableCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Table } from '@rocket.chat/fuselage';
import React, { ComponentProps, FC } from 'react';

export const GenericTableCell: FC<ComponentProps<typeof Table.Cell>> = (props) => (
<Table.Cell {...props} />
);
10 changes: 10 additions & 0 deletions client/components/GenericTable/V2/GenericTableHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Table } from '@rocket.chat/fuselage';
import React, { FC } from 'react';

import { GenericTableRow } from './GenericTableRow';

export const GenericTableHeader: FC = ({ children, ...props }) => (
<Table.Head {...props}>
<GenericTableRow>{children}</GenericTableRow>
</Table.Head>
);
30 changes: 30 additions & 0 deletions client/components/GenericTable/V2/GenericTableHeaderCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Box, Table } from '@rocket.chat/fuselage';
import React, { ComponentProps, ReactElement, useCallback } from 'react';

import SortIcon from '../SortIcon';

type GenericTableHeaderCellProps<T extends string> = Omit<ComponentProps<typeof Box>, 'onClick'> & {
active?: boolean;
direction?: 'asc' | 'desc';
sort?: T;
onClick?: (sort: T) => void;
};

export const GenericTableHeaderCell = <T extends string = string>({
children,
active,
direction,
sort,
onClick,
...props
}: GenericTableHeaderCellProps<T>): ReactElement => {
const fn = useCallback(() => onClick && sort && onClick(sort), [sort, onClick]);
return (
<Table.Cell clickable={!!sort} onClick={fn} {...props}>
<Box display='flex' alignItems='center' wrap='no-wrap'>
{children}
{sort && <SortIcon direction={active ? direction : undefined} />}
</Box>
</Table.Cell>
);
};
25 changes: 25 additions & 0 deletions client/components/GenericTable/V2/GenericTableLoadingRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { Box, Skeleton, Table } from '@rocket.chat/fuselage';
import React, { ReactElement } from 'react';

type GenericTableLoadingRowRowProps = {
cols: number;
};

export const GenericTableLoadingRow = ({ cols }: GenericTableLoadingRowRowProps): ReactElement => (
<Table.Row>
<Table.Cell>
<Box display='flex'>
<Skeleton variant='rect' height={40} width={40} />
<Box mi='x8' flexGrow={1}>
<Skeleton width='100%' />
<Skeleton width='100%' />
</Box>
</Box>
</Table.Cell>
{Array.from({ length: cols - 1 }, (_, i) => (
<Table.Cell key={i}>
<Skeleton width='100%' />
</Table.Cell>
))}
</Table.Row>
);
15 changes: 15 additions & 0 deletions client/components/GenericTable/V2/GenericTableLoadingTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React, { ReactElement } from 'react';

import { GenericTableLoadingRow } from './GenericTableLoadingRow';

export const GenericTableLoadingTable = ({
headerCells,
}: {
headerCells: number;
}): ReactElement => (
<>
{Array.from({ length: 10 }, (_, i) => (
<GenericTableLoadingRow key={i} cols={headerCells} />
))}
</>
);
6 changes: 6 additions & 0 deletions client/components/GenericTable/V2/GenericTableRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Table } from '@rocket.chat/fuselage';
import React, { ComponentProps, FC } from 'react';

export const GenericTableRow: FC<ComponentProps<typeof Table.Row>> = (props) => (
<Table.Row {...props} />
);
9 changes: 9 additions & 0 deletions client/components/GenericTable/hooks/useCurrent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useState } from 'react';

export const useCurrent = (
currentInitialValue = 0,
): [number, React.Dispatch<React.SetStateAction<number>>] => {
const [current, setCurrent] = useState<number>(currentInitialValue);

return [current, setCurrent];
};
11 changes: 11 additions & 0 deletions client/components/GenericTable/hooks/useItemsPerPage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useState } from 'react';

type UseItemsPerPageValue = 25 | 50 | 100;

export const useItemsPerPage = (
itemsPerPageInitialValue: UseItemsPerPageValue = 25,
): [UseItemsPerPageValue, React.Dispatch<React.SetStateAction<UseItemsPerPageValue>>] => {
const [itemsPerPage, setItemsPerPage] = useState<UseItemsPerPageValue>(itemsPerPageInitialValue);

return [itemsPerPage, setItemsPerPage];
};
8 changes: 8 additions & 0 deletions client/components/GenericTable/hooks/useItemsPerPageLabel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { useCallback } from 'react';

import { useTranslation } from '../../../contexts/TranslationContext';

export const useItemsPerPageLabel = (): (() => string) => {
const t = useTranslation();
return useCallback(() => t('Items_per_page:'), [t]);
};
30 changes: 30 additions & 0 deletions client/components/GenericTable/hooks/usePagination.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useCurrent } from './useCurrent';
import { useItemsPerPage } from './useItemsPerPage';
import { useItemsPerPageLabel } from './useItemsPerPageLabel';
import { useShowingResultsLabel } from './useShowingResultsLabel';

export const usePagination = (): {
current: ReturnType<typeof useCurrent>[0];
setCurrent: ReturnType<typeof useCurrent>[1];
itemsPerPage: ReturnType<typeof useItemsPerPage>[0];
setItemsPerPage: ReturnType<typeof useItemsPerPage>[1];
itemsPerPageLabel: ReturnType<typeof useItemsPerPageLabel>;
showingResultsLabel: ReturnType<typeof useShowingResultsLabel>;
} => {
const [itemsPerPage, setItemsPerPage] = useItemsPerPage();

const [current, setCurrent] = useCurrent();

const itemsPerPageLabel = useItemsPerPageLabel();

const showingResultsLabel = useShowingResultsLabel();

return {
itemsPerPage,
setItemsPerPage,
current,
setCurrent,
itemsPerPageLabel,
showingResultsLabel,
};
};
19 changes: 19 additions & 0 deletions client/components/GenericTable/hooks/useShowingResultsLabel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Pagination } from '@rocket.chat/fuselage';
import { ComponentProps, useCallback } from 'react';

import { useTranslation } from '../../../contexts/TranslationContext';

type Props<
T extends ComponentProps<typeof Pagination>['showingResultsLabel'] = ComponentProps<
typeof Pagination
>['showingResultsLabel'],
> = T extends (...args: any[]) => any ? Parameters<T> : never;

export const useShowingResultsLabel = (): ((...params: Props) => string) => {
const t = useTranslation();
return useCallback(
({ count, current, itemsPerPage }) =>
t('Showing_results_of', current + 1, Math.min(current + itemsPerPage, count), count),
[t],
);
};
Loading