Co-authored-by: Jacob Fletcher <jacobsfletch@gmail.com> Co-authored-by: Alessio Gravili <alessio@gravili.de> Co-authored-by: PatrikKozak <patrik@trbl.design> Co-authored-by: Lucas Blancas <lablancas@gmail.com> Co-authored-by: Stef Gootzen <37367280+stefgootzen@users.noreply.github.com> Co-authored-by: Jarrod Flesch <30633324+JarrodMFlesch@users.noreply.github.com> Co-authored-by: Jessica Chowdhury <67977755+JessChowdhury@users.noreply.github.com> Co-authored-by: PatrikKozak <35232443+PatrikKozak@users.noreply.github.com> Co-authored-by: Greg Willard <Wickett06@gmail.com> Co-authored-by: James Mikrut <james@payloadcms.com> Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com> Co-authored-by: Elliot DeNolf <denolfe@gmail.com> fix: WhereBuilder component does not accept all valid Where queries (#3087) fix: passes in height to resizeOptions upload option to allow height resize (#3171)
243 lines
7.9 KiB
TypeScript
243 lines
7.9 KiB
TypeScript
import React, { Fragment } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { useWindowInfo } from '@faceless-ui/window-info';
|
|
import Eyebrow from '../../../elements/Eyebrow';
|
|
import Paginator from '../../../elements/Paginator';
|
|
import ListControls from '../../../elements/ListControls';
|
|
import ListSelection from '../../../elements/ListSelection';
|
|
import Pill from '../../../elements/Pill';
|
|
import Button from '../../../elements/Button';
|
|
import { Table } from '../../../elements/Table';
|
|
import Meta from '../../../utilities/Meta';
|
|
import { Props } from './types';
|
|
import ViewDescription from '../../../elements/ViewDescription';
|
|
import PerPage from '../../../elements/PerPage';
|
|
import { Gutter } from '../../../elements/Gutter';
|
|
import { RelationshipProvider } from './RelationshipProvider';
|
|
import { getTranslation } from '../../../../../utilities/getTranslation';
|
|
import { StaggeredShimmers } from '../../../elements/ShimmerEffect';
|
|
import { SelectionProvider } from './SelectionProvider';
|
|
import EditMany from '../../../elements/EditMany';
|
|
import DeleteMany from '../../../elements/DeleteMany';
|
|
import PublishMany from '../../../elements/PublishMany';
|
|
import UnpublishMany from '../../../elements/UnpublishMany';
|
|
import formatFilesize from '../../../../../uploads/formatFilesize';
|
|
|
|
import './index.scss';
|
|
|
|
const baseClass = 'collection-list';
|
|
|
|
const DefaultList: React.FC<Props> = (props) => {
|
|
const {
|
|
collection,
|
|
collection: {
|
|
labels: {
|
|
singular: singularLabel,
|
|
plural: pluralLabel,
|
|
},
|
|
admin: {
|
|
description,
|
|
components: {
|
|
BeforeList,
|
|
BeforeListTable,
|
|
AfterListTable,
|
|
AfterList,
|
|
} = {},
|
|
} = {},
|
|
},
|
|
data,
|
|
newDocumentURL,
|
|
limit,
|
|
hasCreatePermission,
|
|
disableEyebrow,
|
|
modifySearchParams,
|
|
handleSortChange,
|
|
handleWhereChange,
|
|
handlePageChange,
|
|
handlePerPageChange,
|
|
customHeader,
|
|
resetParams,
|
|
} = props;
|
|
|
|
const { breakpoints: { s: smallBreak } } = useWindowInfo();
|
|
const { t, i18n } = useTranslation('general');
|
|
let formattedDocs = data.docs || [];
|
|
|
|
if (collection.upload) {
|
|
formattedDocs = formattedDocs?.map((doc) => {
|
|
return {
|
|
...doc,
|
|
filesize: formatFilesize(doc.filesize),
|
|
};
|
|
});
|
|
}
|
|
|
|
return (
|
|
<div className={baseClass}>
|
|
{Array.isArray(BeforeList) && BeforeList.map((Component, i) => (
|
|
<Component
|
|
key={i}
|
|
{...props}
|
|
/>
|
|
))}
|
|
|
|
<Meta
|
|
title={getTranslation(collection.labels.plural, i18n)}
|
|
/>
|
|
<SelectionProvider
|
|
docs={data.docs}
|
|
totalDocs={data.totalDocs}
|
|
>
|
|
{!disableEyebrow && (
|
|
<Eyebrow />
|
|
)}
|
|
<Gutter className={`${baseClass}__wrap`}>
|
|
<header className={`${baseClass}__header`}>
|
|
{customHeader && customHeader}
|
|
{!customHeader && (
|
|
<Fragment>
|
|
<h1>
|
|
{getTranslation(pluralLabel, i18n)}
|
|
</h1>
|
|
{hasCreatePermission && (
|
|
<Pill
|
|
to={newDocumentURL}
|
|
aria-label={t('createNewLabel', { label: getTranslation(singularLabel, i18n) })}
|
|
>
|
|
{t('createNew')}
|
|
</Pill>
|
|
)}
|
|
{!smallBreak && (
|
|
<ListSelection
|
|
label={getTranslation(collection.labels.plural, i18n)}
|
|
/>
|
|
)}
|
|
{description && (
|
|
<div className={`${baseClass}__sub-header`}>
|
|
<ViewDescription description={description} />
|
|
</div>
|
|
)}
|
|
</Fragment>
|
|
)}
|
|
</header>
|
|
<ListControls
|
|
collection={collection}
|
|
modifySearchQuery={modifySearchParams}
|
|
handleSortChange={handleSortChange}
|
|
handleWhereChange={handleWhereChange}
|
|
resetParams={resetParams}
|
|
/>
|
|
{Array.isArray(BeforeListTable) && BeforeListTable.map((Component, i) => (
|
|
<Component
|
|
key={i}
|
|
{...props}
|
|
/>
|
|
))}
|
|
{!data.docs && (
|
|
<StaggeredShimmers
|
|
className={[`${baseClass}__shimmer`, `${baseClass}__shimmer--rows`].join(' ')}
|
|
count={6}
|
|
/>
|
|
)}
|
|
{(data.docs && data.docs.length > 0) && (
|
|
<RelationshipProvider>
|
|
<Table data={formattedDocs} />
|
|
</RelationshipProvider>
|
|
)}
|
|
{data.docs && data.docs.length === 0 && (
|
|
<div className={`${baseClass}__no-results`}>
|
|
<p>
|
|
{t('noResults', { label: getTranslation(pluralLabel, i18n) })}
|
|
</p>
|
|
{hasCreatePermission && newDocumentURL && (
|
|
<Button
|
|
el="link"
|
|
to={newDocumentURL}
|
|
>
|
|
{t('createNewLabel', { label: getTranslation(singularLabel, i18n) })}
|
|
</Button>
|
|
)}
|
|
</div>
|
|
)}
|
|
{Array.isArray(AfterListTable) && AfterListTable.map((Component, i) => (
|
|
<Component
|
|
key={i}
|
|
{...props}
|
|
/>
|
|
))}
|
|
|
|
<div className={`${baseClass}__page-controls`}>
|
|
<Paginator
|
|
limit={data.limit}
|
|
totalPages={data.totalPages}
|
|
page={data.page}
|
|
hasPrevPage={data.hasPrevPage}
|
|
hasNextPage={data.hasNextPage}
|
|
prevPage={data.prevPage}
|
|
nextPage={data.nextPage}
|
|
numberOfNeighbors={1}
|
|
disableHistoryChange={modifySearchParams === false}
|
|
onChange={handlePageChange}
|
|
/>
|
|
{data?.totalDocs > 0 && (
|
|
<Fragment>
|
|
<div className={`${baseClass}__page-info`}>
|
|
{(data.page * data.limit) - (data.limit - 1)}
|
|
-
|
|
{data.totalPages > 1 && data.totalPages !== data.page ? (data.limit * data.page) : data.totalDocs}
|
|
{' '}
|
|
{t('of')}
|
|
{' '}
|
|
{data.totalDocs}
|
|
</div>
|
|
<PerPage
|
|
limits={collection?.admin?.pagination?.limits}
|
|
limit={limit}
|
|
modifySearchParams={modifySearchParams}
|
|
handleChange={handlePerPageChange}
|
|
resetPage={data.totalDocs <= data.pagingCounter}
|
|
/>
|
|
<div className={`${baseClass}__list-selection`}>
|
|
{smallBreak && (
|
|
<Fragment>
|
|
<ListSelection
|
|
label={getTranslation(collection.labels.plural, i18n)}
|
|
/>
|
|
<div className={`${baseClass}__list-selection-actions`}>
|
|
<EditMany
|
|
collection={collection}
|
|
resetParams={resetParams}
|
|
/>
|
|
<PublishMany
|
|
collection={collection}
|
|
resetParams={resetParams}
|
|
/>
|
|
<UnpublishMany
|
|
collection={collection}
|
|
resetParams={resetParams}
|
|
/>
|
|
<DeleteMany
|
|
collection={collection}
|
|
resetParams={resetParams}
|
|
/>
|
|
</div>
|
|
</Fragment>
|
|
)}
|
|
</div>
|
|
</Fragment>
|
|
)}
|
|
</div>
|
|
</Gutter>
|
|
</SelectionProvider>
|
|
{Array.isArray(AfterList) && AfterList.map((Component, i) => (
|
|
<Component
|
|
key={i}
|
|
{...props}
|
|
/>
|
|
))}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default DefaultList;
|