design-comuni-plone-theme
Version:
Volto Theme for Italia design guidelines
246 lines (229 loc) • 6.73 kB
JSX
/*
* UniversalLink
* @module components/UniversalLink
*
* CUSTOMIZATIONS:
* - aggiunto icona per link esterni
* - aggiunto title informativo per link esterni
* - aggiunta la dimensione del file se il link punta a un file (enhanced_link_infos)
* - aggiunto il parametro hideFileFormat per nascondere il formato del file dall'enhance link
* - aggiunta la condizione su @@display-file e @download-file per gestire i casi in cui questi parametri vengono imposti a monte
*/
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl, defineMessages } from 'react-intl';
import { HashLink as Link } from 'react-router-hash-link';
import { useSelector } from 'react-redux';
import {
flattenToAppURL,
isInternalURL,
URLUtils,
} from '@plone/volto/helpers/Url/Url';
import { matchPath } from 'react-router';
import { Icon } from 'design-comuni-plone-theme/components/ItaliaTheme';
import { EnhanceLink } from 'design-comuni-plone-theme/helpers';
import cx from 'classnames';
import config from '@plone/volto/registry';
const messages = defineMessages({
opensInNewTab: {
id: 'opensInNewTab',
defaultMessage: 'Apre in un nuovo tab',
},
});
const UniversalLink = ({
href,
item = null,
openLinkInNewTab,
download = false,
children,
className = null,
title = null,
overrideMarkSpecialLinks = false,
hideFileFormat = false,
...props
}) => {
let translations = {
opensInNewTab: {
defaultMessage: messages.opensInNewTab.defaultMessage,
},
};
//questo perchè il provider di intl non è sempre definito, ad esempio in slate_wysiwyg_box (Slate RichTextWidget)
let intl = null;
try {
intl = useIntl();
Object.keys(translations).forEach(
(k) => (translations[k].message = intl.formatMessage(messages[k])),
);
} catch (e) {
// eslint-disable-next-line no-console
console.log('Cannot use intl here. View default messages.', e);
}
const token = useSelector((state) => state.userSession?.token);
const { openExternalLinkInNewTab } = config.settings;
let url = href;
let enhanced_link_infos = null;
if (!href && item) {
if (!item['@id']) {
// eslint-disable-next-line no-console
console.error(
'Invalid item passed to UniversalLink',
item,
props,
children,
);
url = '#';
} else {
//case: generic item
url = flattenToAppURL(item['@id']);
//case: item like a Link
let remoteUrl = item.remoteUrl || item.getRemoteUrl;
if (!token && remoteUrl) {
url = remoteUrl;
}
//case: item of type 'File'
if (!url.includes('@@download') && !url.includes('@@display-file')) {
//se non è gia stato aggiunto il suffisso per il download nell'@id dell'item
if (
!token &&
config.settings.downloadableObjects.includes(item['@type'])
) {
url = `${url}/@@download/file`;
}
if (
!token &&
config.settings.viewableInBrowserObjects.includes(item['@type'])
) {
url = `${url}/@@display-file/file`;
}
}
}
}
if (item && item['@id']) {
/*enhance link*/
if (item && item.enhanced_links_enabled) {
enhanced_link_infos = { ...item };
if (!enhanced_link_infos.filename) {
enhanced_link_infos.filename = item['@id'];
}
}
}
const isBlacklisted =
(config.settings.externalRoutes ?? []).find((route) =>
matchPath(flattenToAppURL(url), route.match),
)?.length > 0;
const isExternal = !isInternalURL(url) || isBlacklisted;
const isDownload = (!isExternal && url.includes('@@download')) || download;
const isDisplayFile =
(!isExternal && url.includes('@@display-file')) || false;
const checkedURL = URLUtils.checkAndNormalizeUrl(url);
url = checkedURL.url;
let aria_label = props['aria-label'] ?? item?.title ?? null;
let enhanced_link = null;
let extended_children = <></>;
if (enhanced_link_infos) {
enhanced_link = EnhanceLink({ enhanced_link_infos, aria_label });
extended_children = enhanced_link.children;
aria_label = enhanced_link.aria_label;
}
const showExternalIcon =
!overrideMarkSpecialLinks &&
config.settings.siteProperties.markSpecialLinks;
let tag = (
<Link
to={flattenToAppURL(url)}
target={openLinkInNewTab ?? false ? '_blank' : null}
title={title}
className={className}
smooth={config.settings.hashLinkSmoothScroll}
{...props}
aria-label={aria_label}
>
{children}
{extended_children}
</Link>
);
if (isExternal) {
const openInNewTab =
openLinkInNewTab === null || openLinkInNewTab === undefined
? openExternalLinkInNewTab
: openLinkInNewTab;
tag = (
<a
href={url}
title={`${title ? title + ' - ' : ''}${
translations.opensInNewTab.message ??
translations.opensInNewTab.defaultMessage
}`}
target={
!checkedURL.isMail && !checkedURL.isTelephone && openInNewTab
? '_blank'
: null
}
rel="noopener noreferrer"
className={cx(className, {
'with-external-link-icon': showExternalIcon,
})}
{...props}
aria-label={aria_label}
>
{children}
{showExternalIcon && (
<Icon
icon="it-external-link"
title={`${title ? title + ' - ' : ''}${intl?.formatMessage({
id: 'opensInNewTab',
})}`}
size="xs"
className="ms-1 align-sub external-link"
/>
)}
</a>
);
} else if (isDownload) {
tag = (
<a
href={flattenToAppURL(url)}
download
title={title}
className={className}
{...props}
aria-label={aria_label}
>
{children}
{extended_children}
</a>
);
} else if (isDisplayFile) {
tag = (
<a
href={flattenToAppURL(url)}
title={title}
target="_blank"
rel="noopener noreferrer"
className={className}
{...props}
aria-label={aria_label}
>
{children}
{extended_children}
</a>
);
}
return tag;
};
UniversalLink.propTypes = {
href: PropTypes.string,
openLinkInNewTab: PropTypes.bool,
download: PropTypes.bool,
className: PropTypes.string,
title: PropTypes.string,
item: PropTypes.shape({
'@id': PropTypes.string.isRequired,
remoteUrl: PropTypes.string, //of plone @type 'Link'
}),
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
]),
};
export default UniversalLink;