@plone/volto
Version:
Volto
155 lines (144 loc) • 4.88 kB
JSX
import React, { createRef, useMemo } from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import cx from 'classnames';
import { Pagination, Dimmer, Loader } from 'semantic-ui-react';
import Slugger from 'github-slugger';
import Icon from '@plone/volto/components/theme/Icon/Icon';
import { renderLinkElement } from '@plone/volto-slate/editor/render';
import config from '@plone/volto/registry';
import withQuerystringResults from './withQuerystringResults';
import { normalizeString } from '@plone/volto/helpers/Utils/Utils';
import paginationLeftSVG from '@plone/volto/icons/left-key.svg';
import paginationRightSVG from '@plone/volto/icons/right-key.svg';
const Headline = ({ headlineTag, id, data = {}, listingItems, isEditMode }) => {
let attr = { id };
const slug = Slugger.slug(normalizeString(data.headline));
attr.id = slug || id;
const LinkedHeadline = useMemo(
() => renderLinkElement(headlineTag),
[headlineTag],
);
return (
<LinkedHeadline
mode={!isEditMode && 'view'}
children={data.headline}
attributes={attr}
className={cx('headline', {
emptyListing: !listingItems?.length > 0,
})}
/>
);
};
const ListingBody = withQuerystringResults((props) => {
const {
data = {},
isEditMode,
listingItems,
totalPages,
onPaginationChange,
variation,
currentPage,
prevBatch,
nextBatch,
isFolderContentsListing,
hasLoaded,
id,
} = props;
let ListingBodyTemplate;
// Legacy support if template is present
const variations = config.blocks?.blocksConfig['listing']?.variations || [];
const defaultVariation = variations.filter((item) => item.isDefault)?.[0];
if (data.template && !data.variation) {
const legacyTemplateConfig = variations.find(
(item) => item.id === data.template,
);
ListingBodyTemplate = legacyTemplateConfig.template;
} else {
ListingBodyTemplate =
variation?.template ?? defaultVariation?.template ?? null;
}
const listingRef = createRef();
const NoResults = variation?.noResultsComponent
? variation.noResultsComponent
: config.blocks?.blocksConfig['listing'].noResultsComponent;
const HeadlineTag = data.headlineTag || 'h2';
return (
<>
{data.headline && (
<Headline
headlineTag={HeadlineTag}
id={id}
listingItems={listingItems}
data={data}
isEditMode={isEditMode}
/>
)}
{listingItems?.length > 0 ? (
<div ref={listingRef}>
<ListingBodyTemplate
items={listingItems}
isEditMode={isEditMode}
{...data}
{...variation}
/>
{totalPages > 1 && (
<div className="pagination-wrapper">
<Pagination
activePage={currentPage}
totalPages={totalPages}
onPageChange={(e, { activePage }) => {
!isEditMode &&
listingRef.current.scrollIntoView({ behavior: 'smooth' });
onPaginationChange(e, { activePage });
}}
firstItem={null}
lastItem={null}
prevItem={{
content: <Icon name={paginationLeftSVG} size="18px" />,
icon: true,
'aria-disabled': !prevBatch,
className: !prevBatch ? 'disabled' : null,
}}
nextItem={{
content: <Icon name={paginationRightSVG} size="18px" />,
icon: true,
'aria-disabled': !nextBatch,
className: !nextBatch ? 'disabled' : null,
}}
/>
</div>
)}
</div>
) : isEditMode ? (
<div className="listing message" ref={listingRef}>
{isFolderContentsListing && (
<FormattedMessage
id="No items found in this container."
defaultMessage="No items found in this container."
/>
)}
{hasLoaded && NoResults && (
<NoResults isEditMode={isEditMode} {...data} />
)}
<Dimmer active={!hasLoaded} inverted>
<Loader indeterminate size="small">
<FormattedMessage id="loading" defaultMessage="Loading" />
</Loader>
</Dimmer>
</div>
) : (
<div className="emptyListing">
{hasLoaded && NoResults && (
<NoResults isEditMode={isEditMode} {...data} />
)}
<Dimmer active={!hasLoaded} inverted>
<Loader indeterminate size="small">
<FormattedMessage id="loading" defaultMessage="Loading" />
</Loader>
</Dimmer>
</div>
)}
</>
);
});
export default injectIntl(ListingBody);