@apify/ui-library 1.98.2 → 1.98.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/components/link.d.ts +7 -0
- package/dist/src/components/link.d.ts.map +1 -1
- package/dist/src/components/link.js +40 -2
- package/dist/src/components/link.js.map +1 -1
- package/dist/src/components/to_consolidate/markdown.d.ts +1 -3
- package/dist/src/components/to_consolidate/markdown.d.ts.map +1 -1
- package/dist/src/components/to_consolidate/markdown.js +7 -10
- package/dist/src/components/to_consolidate/markdown.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/components/link.tsx +39 -4
- package/src/components/to_consolidate/markdown.tsx +7 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apify/ui-library",
|
|
3
|
-
"version": "1.98.
|
|
3
|
+
"version": "1.98.3",
|
|
4
4
|
"description": "React UI library used by apify.com",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"src",
|
|
65
65
|
"style"
|
|
66
66
|
],
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "11a60e90663e75d2de31c28a6e6b01c98d0b9d57"
|
|
68
68
|
}
|
package/src/components/link.tsx
CHANGED
|
@@ -37,6 +37,40 @@ export const isUrlEmail = (url: To) => {
|
|
|
37
37
|
if (!url || typeof (url) !== 'string') return false;
|
|
38
38
|
return url.startsWith('mailto:');
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Omits the domain and protocol from a given URL string.
|
|
42
|
+
* @param rawUrlString The input URL string which may include protocol and domain.
|
|
43
|
+
* @returns The URL string without the domain and protocol, or if it's invalid URL then it returns the input param — `rawUrlString`.
|
|
44
|
+
*/
|
|
45
|
+
export function omitDomainAndProtocol(rawUrlString: string): string {
|
|
46
|
+
try {
|
|
47
|
+
const { pathname, search, hash } = new URL(rawUrlString);
|
|
48
|
+
return pathname + search + hash;
|
|
49
|
+
} catch {
|
|
50
|
+
return rawUrlString;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function hasUrlHttpProtocol(rawUrlString: string): boolean {
|
|
55
|
+
const httpProtocolRegex = /^https?:\/\//i;
|
|
56
|
+
return httpProtocolRegex.test(rawUrlString);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns pathname if the input param `to` is string and internal link or if it's a `To` object
|
|
60
|
+
*/
|
|
61
|
+
function getHref(to: To, isExternal?: boolean): string {
|
|
62
|
+
if (typeof to === 'string') {
|
|
63
|
+
// React Router ignores all urls contains origins and append it to current window url.
|
|
64
|
+
// This extracts the pathname from the URL if it's same as the current window url
|
|
65
|
+
if (!isExternal && hasUrlHttpProtocol(to)) {
|
|
66
|
+
return omitDomainAndProtocol(to);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return to;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return createPath(to);
|
|
73
|
+
}
|
|
40
74
|
|
|
41
75
|
const StyledLink = styled(Box)`
|
|
42
76
|
/* Basic positioning */
|
|
@@ -78,8 +112,8 @@ export const Link = forwardRef<HTMLElement, LinkProps>(({
|
|
|
78
112
|
trackClick,
|
|
79
113
|
InternalLink,
|
|
80
114
|
} = useSharedUiDependencies();
|
|
81
|
-
const href = typeof (to) === 'string' ? to : createPath(to);
|
|
82
115
|
const isExternal = isUrlExternal(to, windowLocationHost);
|
|
116
|
+
const href = getHref(to, isExternal);
|
|
83
117
|
const isEmail = isUrlEmail(to);
|
|
84
118
|
const isTrusted = isHrefTrusted(href);
|
|
85
119
|
|
|
@@ -88,12 +122,13 @@ export const Link = forwardRef<HTMLElement, LinkProps>(({
|
|
|
88
122
|
if (onClick) onClick(e);
|
|
89
123
|
};
|
|
90
124
|
|
|
91
|
-
const
|
|
92
|
-
rel,
|
|
125
|
+
const uniqRel = new Set([
|
|
126
|
+
...(rel?.split(' ') || []),
|
|
93
127
|
isExternal && 'external',
|
|
94
128
|
(isExternal || target === '_blank') && 'noopener',
|
|
95
129
|
(isExternal && !isTrusted) && 'nofollow',
|
|
96
|
-
);
|
|
130
|
+
]);
|
|
131
|
+
const effectiveRel = clsx(Array.from(uniqRel));
|
|
97
132
|
|
|
98
133
|
return (
|
|
99
134
|
<StyledLink
|
|
@@ -15,6 +15,7 @@ import type { UiThemeOption } from '../../design_system/theme.js';
|
|
|
15
15
|
import { theme } from '../../design_system/theme.js';
|
|
16
16
|
import { useCopyToClipboard } from '../../utils/index.js';
|
|
17
17
|
import { CodeBlock, inlineCodeStyles, OneLineCode } from '../code/index.js';
|
|
18
|
+
import { Link as SharedLink, type LinkProps } from '../link.js';
|
|
18
19
|
import { cleanMarkdown, slugifyHeadingChildren } from '../readme_renderer/utils.js';
|
|
19
20
|
|
|
20
21
|
interface StyledReadmeProps {
|
|
@@ -422,13 +423,12 @@ interface LinkRendererProps extends React.AnchorHTMLAttributes<HTMLAnchorElement
|
|
|
422
423
|
|
|
423
424
|
interface LinkRendererOptions {
|
|
424
425
|
hostname?: string,
|
|
425
|
-
Link?: React.ElementType;
|
|
426
426
|
}
|
|
427
427
|
|
|
428
428
|
// We want no-follow for external links
|
|
429
429
|
// Also if link is a video from youtube or vimeo, we want to render it as iframe
|
|
430
430
|
// Allowing to pass hostname to check if the link is an Apify link to the same hostname (is needed for SSR on the web)
|
|
431
|
-
const DefaultLinkRenderer = ({ node, href, ...props }: LinkRendererProps, { hostname
|
|
431
|
+
const DefaultLinkRenderer = ({ node, href, ...props }: LinkRendererProps, { hostname }: LinkRendererOptions, isUserGeneratedContent?: boolean) => {
|
|
432
432
|
const videoSrc = node.properties.enableEmbeddedVideo && getVideoSrc(href);
|
|
433
433
|
if (videoSrc) return <Video src={videoSrc} />;
|
|
434
434
|
|
|
@@ -454,12 +454,7 @@ const DefaultLinkRenderer = ({ node, href, ...props }: LinkRendererProps, { host
|
|
|
454
454
|
&& APIFY_HOSTNAMES.includes(urlParsed.hostname.toLowerCase())
|
|
455
455
|
&& urlParsed.protocol === 'https:'; // we want to disqualify links that have http: protocol. It's a mistake on users' side that we are penalized for.
|
|
456
456
|
|
|
457
|
-
|
|
458
|
-
if (!hasDifferentHostname && Link) {
|
|
459
|
-
return <Link to={urlParsed} {...props} />;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
let linkProps = {};
|
|
457
|
+
let linkProps: Pick<LinkProps, 'rel' | 'target'> = {};
|
|
463
458
|
|
|
464
459
|
// If false, we want to open the link in the same tab (linkProps won't have any props)
|
|
465
460
|
if (hasDifferentHostname) {
|
|
@@ -472,11 +467,11 @@ const DefaultLinkRenderer = ({ node, href, ...props }: LinkRendererProps, { host
|
|
|
472
467
|
// Google says:
|
|
473
468
|
// It’s valid to use nofollow with the new attributes — such as rel=”nofollow ugc”
|
|
474
469
|
// — if you wish to be backwards-compatible with services that don’t support the new attributes.
|
|
475
|
-
linkProps = {
|
|
470
|
+
linkProps = { rel: clsx(isUserGeneratedContent && 'ugc') };
|
|
476
471
|
}
|
|
477
472
|
}
|
|
478
473
|
|
|
479
|
-
return <
|
|
474
|
+
return <SharedLink {...props as LinkProps} {...linkProps} to={href}/>;
|
|
480
475
|
};
|
|
481
476
|
|
|
482
477
|
// node is just to omit from exported props
|
|
@@ -548,7 +543,6 @@ export interface MarkdownProps {
|
|
|
548
543
|
isUserGeneratedContent?: boolean;
|
|
549
544
|
currentPathHostname?: string;
|
|
550
545
|
addHeadingAnchors?: boolean;
|
|
551
|
-
Link?: React.ElementType;
|
|
552
546
|
LinkRenderer?: (props: LinkRendererProps, options: LinkRendererOptions, isUserGeneratedContent?: boolean) => React.ReactElement;
|
|
553
547
|
lazyLoadImages?: boolean;
|
|
554
548
|
}
|
|
@@ -563,7 +557,6 @@ const Markdown = ({
|
|
|
563
557
|
currentPathHostname,
|
|
564
558
|
addHeadingAnchors,
|
|
565
559
|
isUserGeneratedContent,
|
|
566
|
-
Link,
|
|
567
560
|
LinkRenderer,
|
|
568
561
|
lazyLoadImages,
|
|
569
562
|
}: MarkdownProps) => {
|
|
@@ -617,8 +610,8 @@ const Markdown = ({
|
|
|
617
610
|
h5: headingRenderer,
|
|
618
611
|
a: (linkProps: LinkRendererProps) => (
|
|
619
612
|
LinkRenderer
|
|
620
|
-
? LinkRenderer(linkProps, { hostname: currentPathHostname
|
|
621
|
-
: DefaultLinkRenderer(linkProps, { hostname: currentPathHostname
|
|
613
|
+
? LinkRenderer(linkProps, { hostname: currentPathHostname }, isUserGeneratedContent)
|
|
614
|
+
: DefaultLinkRenderer(linkProps, { hostname: currentPathHostname }, isUserGeneratedContent)
|
|
622
615
|
),
|
|
623
616
|
code: (codeProps: CodeRendererComponentProps) => CodeRenderer(codeProps),
|
|
624
617
|
p: ParagraphRenderer,
|