@apify/ui-library 0.71.1-featcolortokens-178953.58 → 0.71.1-featcolortokens-178953.67
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/design_system/colors/generated/{dark.d.ts → css_variables.dark.d.ts} +1 -1
- package/dist/src/design_system/colors/generated/css_variables.dark.d.ts.map +1 -0
- package/dist/src/design_system/colors/generated/css_variables.dark.js +147 -0
- package/dist/src/design_system/colors/generated/css_variables.dark.js.map +1 -0
- package/dist/src/design_system/colors/generated/{light.d.ts → css_variables.light.d.ts} +1 -1
- package/dist/src/design_system/colors/generated/css_variables.light.d.ts.map +1 -0
- package/dist/src/design_system/colors/generated/css_variables.light.js +147 -0
- package/dist/src/design_system/colors/generated/css_variables.light.js.map +1 -0
- package/dist/src/design_system/colors/generated/{palette.dark.d.ts → css_variables_palette.dark.d.ts} +1 -1
- package/dist/src/design_system/colors/generated/css_variables_palette.dark.d.ts.map +1 -0
- package/dist/src/design_system/colors/generated/css_variables_palette.dark.js +74 -0
- package/dist/src/design_system/colors/generated/css_variables_palette.dark.js.map +1 -0
- package/dist/src/design_system/colors/generated/{palette.light.d.ts → css_variables_palette.light.d.ts} +1 -1
- package/dist/src/design_system/colors/generated/css_variables_palette.light.d.ts.map +1 -0
- package/dist/src/design_system/colors/generated/css_variables_palette.light.js +74 -0
- package/dist/src/design_system/colors/generated/css_variables_palette.light.js.map +1 -0
- package/dist/src/design_system/colors/index.d.ts +4 -4
- package/dist/src/design_system/colors/index.d.ts.map +1 -1
- package/dist/src/design_system/colors/index.js +4 -4
- package/dist/src/design_system/colors/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/codemods/generate_typograpy_tokens_files.mjs +137 -0
- package/src/components/action_link.tsx +60 -0
- package/src/components/actor_template_card.tsx +116 -0
- package/src/components/badge.tsx +148 -0
- package/src/components/banner.tsx +94 -0
- package/src/components/blog_article.tsx +85 -0
- package/src/components/box.tsx +127 -0
- package/src/components/button.tsx +305 -0
- package/src/components/chip.tsx +128 -0
- package/src/components/code/action_button.tsx +96 -0
- package/src/components/code/code_block/code_block.styled.tsx +180 -0
- package/src/components/code/code_block/code_block.tsx +224 -0
- package/src/components/code/code_block/code_block_with_tabs.tsx +257 -0
- package/src/components/code/code_block/utils.tsx +67 -0
- package/src/components/code/index.ts +5 -0
- package/src/components/code/inline_code/inline_code.tsx +62 -0
- package/src/components/code/one_line_code/one_line_code.tsx +228 -0
- package/src/components/code/prism_highlighter.tsx +180 -0
- package/src/components/color_wheel_gradient.tsx +31 -0
- package/src/components/floating/index.ts +3 -0
- package/src/components/floating/menu.tsx +189 -0
- package/src/components/floating/menu_common.tsx +31 -0
- package/src/components/floating/menu_components.tsx +99 -0
- package/src/components/image.tsx +24 -0
- package/src/components/index.ts +22 -0
- package/src/components/link.tsx +114 -0
- package/src/components/message.tsx +153 -0
- package/src/components/rating.tsx +106 -0
- package/src/components/readme_renderer/index.ts +3 -0
- package/src/components/readme_renderer/pythonize_value.ts +76 -0
- package/src/components/readme_renderer/table_of_contents.tsx +272 -0
- package/src/components/readme_renderer/utils.tsx +46 -0
- package/src/components/simple_markdown/index.ts +2 -0
- package/src/components/simple_markdown/simple_markdown.tsx +214 -0
- package/src/components/simple_markdown/simple_markdown_components.tsx +293 -0
- package/src/components/tabs/index.ts +2 -0
- package/src/components/tabs/tab.tsx +217 -0
- package/src/components/tabs/tabs.tsx +169 -0
- package/src/components/tag.tsx +196 -0
- package/src/components/text/heading_content.tsx +56 -0
- package/src/components/text/heading_marketing.tsx +55 -0
- package/src/components/text/heading_shared.tsx +55 -0
- package/src/components/text/index.ts +19 -0
- package/src/components/text/text_base.tsx +52 -0
- package/src/components/text/text_content.tsx +104 -0
- package/src/components/text/text_marketing.tsx +152 -0
- package/src/components/text/text_shared.tsx +95 -0
- package/src/components/tile/horizontal_tile.tsx +77 -0
- package/src/components/tile/index.ts +2 -0
- package/src/components/tile/shared.ts +27 -0
- package/src/components/tile/vertical_tile.tsx +59 -0
- package/src/components/to_consolidate/card.tsx +141 -0
- package/src/components/to_consolidate/index.ts +4 -0
- package/src/components/to_consolidate/markdown.tsx +609 -0
- package/src/components/to_consolidate/pagination.tsx +136 -0
- package/src/components/to_consolidate/tab_number_chip.tsx +31 -0
- package/src/design_system/colors/build_color_tokens.js +183 -0
- package/src/design_system/colors/figma_color_tokens.dark.json +886 -0
- package/src/design_system/colors/figma_color_tokens.light.json +886 -0
- package/src/design_system/colors/generated/colors_theme.dark.ts +110 -0
- package/src/design_system/colors/generated/colors_theme.light.ts +110 -0
- package/{dist/src/design_system/colors/generated/dark.js → src/design_system/colors/generated/css_variables.dark.ts} +1 -1
- package/{dist/src/design_system/colors/generated/light.js → src/design_system/colors/generated/css_variables.light.ts} +1 -1
- package/{dist/src/design_system/colors/generated/palette.dark.js → src/design_system/colors/generated/css_variables_palette.dark.ts} +1 -1
- package/{dist/src/design_system/colors/generated/palette.light.js → src/design_system/colors/generated/css_variables_palette.light.ts} +1 -1
- package/{dist/src/design_system/properties_theme.js → src/design_system/colors/generated/properties_theme.ts} +20 -156
- package/src/design_system/colors/index.ts +7 -0
- package/src/design_system/supernova_typography_tokens.json +657 -0
- package/src/design_system/theme.ts +25 -0
- package/src/design_system/tokens/index.ts +5 -0
- package/src/design_system/tokens/layouts.ts +29 -0
- package/src/design_system/tokens/radiuses.ts +22 -0
- package/src/design_system/tokens/shadows.ts +22 -0
- package/src/design_system/tokens/spaces.ts +15 -0
- package/src/design_system/tokens/transitions.ts +19 -0
- package/src/design_system/typography_theme.ts +197 -0
- package/src/index.ts +8 -0
- package/src/type_utils.ts +7 -0
- package/src/ui_dependency_provider.tsx +58 -0
- package/src/utils/copy_to_clipboard.ts +24 -0
- package/src/utils/image_color.ts +42 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/resize_observer.ts +18 -0
- package/src/utils/sanitization.ts +14 -0
- package/dist/src/design_system/colors/generated/dark.d.ts.map +0 -1
- package/dist/src/design_system/colors/generated/dark.js.map +0 -1
- package/dist/src/design_system/colors/generated/light.d.ts.map +0 -1
- package/dist/src/design_system/colors/generated/light.js.map +0 -1
- package/dist/src/design_system/colors/generated/palette.dark.d.ts.map +0 -1
- package/dist/src/design_system/colors/generated/palette.dark.js.map +0 -1
- package/dist/src/design_system/colors/generated/palette.light.d.ts.map +0 -1
- package/dist/src/design_system/colors/generated/palette.light.js.map +0 -1
- package/dist/src/design_system/colors_theme.d.ts +0 -213
- package/dist/src/design_system/colors_theme.d.ts.map +0 -1
- package/dist/src/design_system/colors_theme.js +0 -213
- package/dist/src/design_system/colors_theme.js.map +0 -1
- package/dist/src/design_system/properties_theme.d.ts +0 -175
- package/dist/src/design_system/properties_theme.d.ts.map +0 -1
- package/dist/src/design_system/properties_theme.js.map +0 -1
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import type { Element, Root, Text as TextNode } from 'hast';
|
|
2
|
+
import React, { useCallback, useMemo, useRef } from 'react';
|
|
3
|
+
import type { Components } from 'react-markdown';
|
|
4
|
+
import ReactMarkdown, { uriTransformer } from 'react-markdown';
|
|
5
|
+
import type { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown';
|
|
6
|
+
import rehypeRaw from 'rehype-raw';
|
|
7
|
+
import rehypeSanitize from 'rehype-sanitize';
|
|
8
|
+
import remarkGfm from 'remark-gfm';
|
|
9
|
+
import styled from 'styled-components';
|
|
10
|
+
import type { Pluggable, PluggableList } from 'unified';
|
|
11
|
+
import { visit } from 'unist-util-visit';
|
|
12
|
+
|
|
13
|
+
import { theme } from '../../design_system/theme.js';
|
|
14
|
+
import { useSharedUiDependencies } from '../../ui_dependency_provider.js';
|
|
15
|
+
import { Box } from '../box.js';
|
|
16
|
+
import { isUrlExternal } from '../link.js';
|
|
17
|
+
import { cleanMarkdown } from '../readme_renderer/utils.js';
|
|
18
|
+
import { HeadingContent } from '../text/heading_content.js';
|
|
19
|
+
import { Heading, Text } from '../text/index.js';
|
|
20
|
+
import { TextContent } from '../text/text_content.js';
|
|
21
|
+
import {
|
|
22
|
+
MarkdownBlockQuote,
|
|
23
|
+
MarkdownCode,
|
|
24
|
+
MarkdownLink,
|
|
25
|
+
MarkdownTable,
|
|
26
|
+
} from './simple_markdown_components.js';
|
|
27
|
+
|
|
28
|
+
export type SimpleMarkdownComponents = Components;
|
|
29
|
+
|
|
30
|
+
export const defaultAllowedElements = [
|
|
31
|
+
'a',
|
|
32
|
+
'b',
|
|
33
|
+
'blockquote',
|
|
34
|
+
'br',
|
|
35
|
+
'center',
|
|
36
|
+
'code',
|
|
37
|
+
'del',
|
|
38
|
+
'em',
|
|
39
|
+
'h1',
|
|
40
|
+
'h2',
|
|
41
|
+
'h3',
|
|
42
|
+
'h4',
|
|
43
|
+
'h5',
|
|
44
|
+
'hr',
|
|
45
|
+
'i',
|
|
46
|
+
'img',
|
|
47
|
+
'li',
|
|
48
|
+
'ol',
|
|
49
|
+
'p',
|
|
50
|
+
'pre',
|
|
51
|
+
'span',
|
|
52
|
+
'strong',
|
|
53
|
+
'table',
|
|
54
|
+
'tbody',
|
|
55
|
+
'td',
|
|
56
|
+
'tfoot',
|
|
57
|
+
'th',
|
|
58
|
+
'thead',
|
|
59
|
+
'tr',
|
|
60
|
+
'u',
|
|
61
|
+
'ul',
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const StyledMarkdown = styled(ReactMarkdown)`
|
|
65
|
+
scroll-margin-top: 10px;
|
|
66
|
+
|
|
67
|
+
@font-face {
|
|
68
|
+
font-family: "ellipsis-font";
|
|
69
|
+
src: local("Courier");
|
|
70
|
+
unicode-range: U+2026;
|
|
71
|
+
size-adjust: 0%;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
img {
|
|
75
|
+
max-width: 100%;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
hr {
|
|
79
|
+
color: ${theme.color.neutral.border};
|
|
80
|
+
}
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
// Function to recursively process unsupported elements
|
|
84
|
+
const unwrapUnsupportedElement = (node: Element): (Element | TextNode)[] => {
|
|
85
|
+
return node.children.flatMap((child) => {
|
|
86
|
+
if (child.type === 'text') {
|
|
87
|
+
return child; // Keep text as is
|
|
88
|
+
}
|
|
89
|
+
if (child.type === 'element') {
|
|
90
|
+
if (defaultAllowedElements.includes(child.tagName)) {
|
|
91
|
+
return child; // Keep supported elements
|
|
92
|
+
}
|
|
93
|
+
return unwrapUnsupportedElement(child as Element);
|
|
94
|
+
}
|
|
95
|
+
return []; // Ignore other node types
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const getUnsupportedRehypeTagsSanitationPlugIn = (
|
|
100
|
+
allowedElements: string[],
|
|
101
|
+
): Pluggable<unknown[]> => () => (tree: Root) => {
|
|
102
|
+
visit(tree, 'element', (node, index, parent) => {
|
|
103
|
+
if (!allowedElements.includes(node.tagName) && parent && typeof index === 'number') {
|
|
104
|
+
// Replace unsupported element with its valid children
|
|
105
|
+
parent.children.splice(index, 1, ...unwrapUnsupportedElement(node));
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const defaultRehypePlugins = [rehypeRaw];
|
|
111
|
+
export const defaultRemarkPlugins = [remarkGfm];
|
|
112
|
+
|
|
113
|
+
// TODO: Add Image component
|
|
114
|
+
const regularMarkdownSizeComponents: Components = {
|
|
115
|
+
h1: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading1'>{children}</HeadingContent>,
|
|
116
|
+
h2: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading2'>{children}</HeadingContent>,
|
|
117
|
+
h3: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading3'>{children}</HeadingContent>,
|
|
118
|
+
h4: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading4'>{children}</HeadingContent>,
|
|
119
|
+
h5: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading5'>{children}</HeadingContent>,
|
|
120
|
+
h6: ({ children }) => <HeadingContent mt='space32' mb='space16' type='heading6'>{children}</HeadingContent>,
|
|
121
|
+
p: ({ children }) => (<TextContent my='space16'>{children}</TextContent>),
|
|
122
|
+
strong: ({ children }) => <TextContent as='strong' weight='bold'>{children}</TextContent>,
|
|
123
|
+
b: ({ children }) => <TextContent as='b' weight='bold'>{children}</TextContent>,
|
|
124
|
+
th: ({ children }) => <TextContent as='th' weight='bold'>{children}</TextContent>,
|
|
125
|
+
blockquote: ({ children }) => <MarkdownBlockQuote>{children}</MarkdownBlockQuote>,
|
|
126
|
+
table: ({ children }) => <MarkdownTable>{children}</MarkdownTable>,
|
|
127
|
+
ul: ({ children }) => <Box as='ul' pl='space32'>{children}</Box>,
|
|
128
|
+
ol: ({ children, start }) => <TextContent as='ol' pl='space32' start={start}>{children}</TextContent>,
|
|
129
|
+
li: ({ children }) => <TextContent as='li' mt='space4'>{children}</TextContent>,
|
|
130
|
+
a: ({ children, href }) => <MarkdownLink to={href}>{children}</MarkdownLink>,
|
|
131
|
+
code: ({ children, inline }) => <MarkdownCode inline={inline} size='regular'>{children}</MarkdownCode>,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const smallMarkdownSizeComponents: Components = {
|
|
135
|
+
h1: ({ children }) => <Heading mt='space32' mb='space16' type='title2xl'>{children}</Heading>,
|
|
136
|
+
h2: ({ children }) => <Heading mt='space32' mb='space16' type='titleXl'>{children}</Heading>,
|
|
137
|
+
h3: ({ children }) => <Heading mt='space32' mb='space16' type='titleL'>{children}</Heading>,
|
|
138
|
+
h4: ({ children }) => <Heading mt='space32' mb='space16' type='titleM'>{children}</Heading>,
|
|
139
|
+
h5: ({ children }) => <Heading mt='space32' mb='space16' type='titleS'>{children}</Heading>,
|
|
140
|
+
h6: ({ children }) => <Heading mt='space32' mb='space16' type='titleXs'>{children}</Heading>,
|
|
141
|
+
p: ({ children }) => (<Text my='space16'>{children}</Text>),
|
|
142
|
+
strong: ({ children }) => <Text as='strong' weight='bold'>{children}</Text>,
|
|
143
|
+
b: ({ children }) => <Text as='b' weight='bold'>{children}</Text>,
|
|
144
|
+
th: ({ children }) => <Text as='th' weight='bold'>{children}</Text>,
|
|
145
|
+
blockquote: ({ children }) => <MarkdownBlockQuote>{children}</MarkdownBlockQuote>,
|
|
146
|
+
table: ({ children }) => <MarkdownTable>{children}</MarkdownTable>,
|
|
147
|
+
ul: ({ children }) => <Box as='ul' pl='space32'>{children}</Box>,
|
|
148
|
+
ol: ({ children, start }) => <Text as='ol' pl='space32' start={start}>{children}</Text>,
|
|
149
|
+
li: ({ children }) => <Text as='li' mt='space4'>{children}</Text>,
|
|
150
|
+
a: ({ children, href }) => <MarkdownLink to={href}>{children}</MarkdownLink>,
|
|
151
|
+
code: ({ children, inline }) => <MarkdownCode inline={inline} size='small'>{children}</MarkdownCode>,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
type MarkdownSize = 'regular' | 'small';
|
|
155
|
+
|
|
156
|
+
interface SimpleMarkdownProps extends ReactMarkdownOptions {
|
|
157
|
+
size?: MarkdownSize,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const useDefaultUrlTransform = () => {
|
|
161
|
+
const { windowLocationHost } = useSharedUiDependencies();
|
|
162
|
+
|
|
163
|
+
return useCallback((url: string) => {
|
|
164
|
+
if (!isUrlExternal(url, windowLocationHost)) {
|
|
165
|
+
// We want to make sure internal links will be relative links without https://host at the beginning because Link doesn't work with these
|
|
166
|
+
return url.replace(/^(https?:\/\/)?(www\.)?[^/]+/, '') || '/';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return url;
|
|
170
|
+
}, [windowLocationHost]);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export const SimpleMarkdown: React.FC<SimpleMarkdownProps> = ({
|
|
174
|
+
size = 'regular', // small is default for console
|
|
175
|
+
children: markdown,
|
|
176
|
+
components,
|
|
177
|
+
rehypePlugins,
|
|
178
|
+
remarkPlugins,
|
|
179
|
+
transformLinkUri,
|
|
180
|
+
allowedElements,
|
|
181
|
+
...rest
|
|
182
|
+
}) => {
|
|
183
|
+
const cleanedMarkdown = useMemo(() => cleanMarkdown(markdown), [markdown]);
|
|
184
|
+
|
|
185
|
+
const effectiveAllowedElements = useRef(allowedElements || defaultAllowedElements);
|
|
186
|
+
|
|
187
|
+
const effectiveComponents = useRef({
|
|
188
|
+
...(size === 'regular' ? regularMarkdownSizeComponents : smallMarkdownSizeComponents),
|
|
189
|
+
...components,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const rehypePluginsRef = useRef<PluggableList>([
|
|
193
|
+
...(rehypePlugins || [...defaultRehypePlugins, getUnsupportedRehypeTagsSanitationPlugIn(effectiveAllowedElements.current)]),
|
|
194
|
+
rehypeSanitize, // lets always sanitize the output
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
const defaultUrlTransform = useDefaultUrlTransform();
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<StyledMarkdown
|
|
201
|
+
components={effectiveComponents.current}
|
|
202
|
+
rehypePlugins={rehypePluginsRef.current}
|
|
203
|
+
remarkPlugins={remarkPlugins || defaultRemarkPlugins}
|
|
204
|
+
allowedElements={effectiveAllowedElements.current}
|
|
205
|
+
transformLinkUri={(href, children, title) => {
|
|
206
|
+
const transformed = transformLinkUri ? transformLinkUri(href, children, title) : defaultUrlTransform(href);
|
|
207
|
+
return uriTransformer(transformed);
|
|
208
|
+
}}
|
|
209
|
+
{...rest}
|
|
210
|
+
>
|
|
211
|
+
{cleanedMarkdown}
|
|
212
|
+
</StyledMarkdown>
|
|
213
|
+
);
|
|
214
|
+
};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import qs from 'query-string';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import type { CodeProps, ReactMarkdownProps } from 'react-markdown/lib/ast-to-react';
|
|
4
|
+
import styled from 'styled-components';
|
|
5
|
+
|
|
6
|
+
import { CheckIcon, LinkIcon } from '@apify/ui-icons';
|
|
7
|
+
|
|
8
|
+
import { theme } from '../../design_system/theme.js';
|
|
9
|
+
import type { WithOptional } from '../../type_utils.js';
|
|
10
|
+
import { useCopyToClipboard } from '../../utils/index.js';
|
|
11
|
+
import { Box } from '../box.js';
|
|
12
|
+
import { CodeBlock, InlineCode, OneLineCode } from '../code/index.js';
|
|
13
|
+
import type { LinkProps } from '../link.js';
|
|
14
|
+
import { Link } from '../link.js';
|
|
15
|
+
import { slugifyHeadingChildren } from '../readme_renderer/utils.js';
|
|
16
|
+
import type { HeadingSharedProps } from '../text/heading_shared.js';
|
|
17
|
+
import { Heading } from '../text/index.js';
|
|
18
|
+
import type { SharedTextProps, SharedTextSize } from '../text/text_shared.js';
|
|
19
|
+
|
|
20
|
+
const simpleMarkdownClassNames = {
|
|
21
|
+
HEADING_ICON_LINK: 'headingIconLink',
|
|
22
|
+
} as const;
|
|
23
|
+
|
|
24
|
+
export const MarkdownPlainHeading: React.FC<HeadingSharedProps> = ({
|
|
25
|
+
children,
|
|
26
|
+
...rest
|
|
27
|
+
}) => {
|
|
28
|
+
const id = slugifyHeadingChildren(children);
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<Heading
|
|
32
|
+
mt='space32'
|
|
33
|
+
mb='space16'
|
|
34
|
+
id={id}
|
|
35
|
+
{...rest}
|
|
36
|
+
>
|
|
37
|
+
{children}
|
|
38
|
+
</Heading>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const StyledMarkdownHeading = styled(Box)`
|
|
43
|
+
display: flex;
|
|
44
|
+
align-items: center;
|
|
45
|
+
gap: ${theme.space.space4};
|
|
46
|
+
|
|
47
|
+
.${simpleMarkdownClassNames.HEADING_ICON_LINK} {
|
|
48
|
+
height: 16px;
|
|
49
|
+
display: none;
|
|
50
|
+
|
|
51
|
+
svg {
|
|
52
|
+
max-height: 1em !important;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
&:hover {
|
|
57
|
+
.${simpleMarkdownClassNames.HEADING_ICON_LINK} {
|
|
58
|
+
display: initial;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Default markdown heading that renders copy icon next to it.
|
|
65
|
+
* Use `MarkdownPlainHeading` if you want to render simple heading instead
|
|
66
|
+
*/
|
|
67
|
+
// TODO: This should be used for readmes
|
|
68
|
+
export const MarkdownHeadingWrapper: React.FC<HeadingSharedProps> = ({
|
|
69
|
+
children,
|
|
70
|
+
...rest
|
|
71
|
+
}) => {
|
|
72
|
+
const id = slugifyHeadingChildren(children);
|
|
73
|
+
|
|
74
|
+
const [isCopied, handleClick] = useCopyToClipboard({
|
|
75
|
+
text: id ?? '',
|
|
76
|
+
// We want the whole URL to be copied, not just the ID
|
|
77
|
+
transform: (text) => {
|
|
78
|
+
const url = new URL(window.location.href);
|
|
79
|
+
url.hash = `#${text}`;
|
|
80
|
+
return url.toString();
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<StyledMarkdownHeading
|
|
86
|
+
mt='space32'
|
|
87
|
+
mb='space16'
|
|
88
|
+
id={id}
|
|
89
|
+
{...rest}
|
|
90
|
+
>
|
|
91
|
+
{children}
|
|
92
|
+
<Link
|
|
93
|
+
className={simpleMarkdownClassNames.HEADING_ICON_LINK}
|
|
94
|
+
onClick={handleClick}
|
|
95
|
+
to={`#${id}`}
|
|
96
|
+
>
|
|
97
|
+
{isCopied ? (
|
|
98
|
+
<CheckIcon
|
|
99
|
+
size="16"
|
|
100
|
+
color={theme.color.success.action}
|
|
101
|
+
/>
|
|
102
|
+
) : (
|
|
103
|
+
<LinkIcon
|
|
104
|
+
size="16"
|
|
105
|
+
color={theme.color.primary.text}
|
|
106
|
+
/>
|
|
107
|
+
)}
|
|
108
|
+
</Link>
|
|
109
|
+
</StyledMarkdownHeading>
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const StyledInlineCode = styled(InlineCode)`
|
|
114
|
+
/* If code is rendered within heading, it should inherit its font styles */
|
|
115
|
+
/* TODO: Look at implementation of InlineCode - maybe we can live without it */
|
|
116
|
+
h1 & code, h2 & code, h3 & code, h4 & code, h5 & code {
|
|
117
|
+
font-size: inherit !important;
|
|
118
|
+
line-height: inherit !important;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
b & code, strong & code{
|
|
122
|
+
font-weight: inherit !important;
|
|
123
|
+
}
|
|
124
|
+
`;
|
|
125
|
+
|
|
126
|
+
type MarkdownCodeProps = Pick<CodeProps, 'inline' | 'className' | 'children'>
|
|
127
|
+
|
|
128
|
+
export const MarkdownCode: React.FC<MarkdownCodeProps & { size: SharedTextSize }> = ({
|
|
129
|
+
inline,
|
|
130
|
+
className,
|
|
131
|
+
children,
|
|
132
|
+
size,
|
|
133
|
+
}) => {
|
|
134
|
+
if (inline) {
|
|
135
|
+
return <StyledInlineCode size={size} className={className}>{children}</StyledInlineCode>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const code = String(children).replace(/\n$/, '').trim();
|
|
139
|
+
const match = /language-(\w+)/.exec(className || '');
|
|
140
|
+
const language = match?.[1]?.toLowerCase();
|
|
141
|
+
const isOneLineCode = code.split('\n').length <= 1;
|
|
142
|
+
|
|
143
|
+
if (isOneLineCode) {
|
|
144
|
+
return (
|
|
145
|
+
<OneLineCode
|
|
146
|
+
language={language}
|
|
147
|
+
size={size}
|
|
148
|
+
fullWidth
|
|
149
|
+
>
|
|
150
|
+
{code}
|
|
151
|
+
</OneLineCode>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
<CodeBlock
|
|
157
|
+
content={code}
|
|
158
|
+
language={language}
|
|
159
|
+
size={size}
|
|
160
|
+
hideLineNumbers
|
|
161
|
+
fullWidth
|
|
162
|
+
hideBashHeader
|
|
163
|
+
hideBashPromptPrefixes
|
|
164
|
+
/>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const youtubeRegex = /^(?:https?:\/\/)?(?:www\.)?(?:m\.)?(?:youtube(?:-nocookie)?\.com|youtu\.be)\/(?:watch\?v=|embed\/|v\/)?([a-zA-Z0-9\-_]+)(?:\S*)?$/;
|
|
169
|
+
const vimeoRegex = /^((?:https?:\/\/)?(?:player\.)?vimeo\.com(?:\/video)?\/(\d+))$/;
|
|
170
|
+
|
|
171
|
+
const getVideoSrc = (link: string) => {
|
|
172
|
+
const youtubeLink = link.match(youtubeRegex);
|
|
173
|
+
const vimeoLink = link.match(vimeoRegex);
|
|
174
|
+
let src;
|
|
175
|
+
if (youtubeLink) {
|
|
176
|
+
// add rel=0 to disable related videos from other channels at the end of the video
|
|
177
|
+
// add enablejsapi=1 to enable tracking videos via API through Google Analytics
|
|
178
|
+
src = qs.stringifyUrl({ url: `https://www.youtube.com/embed/${youtubeLink[1]}`, query: { rel: 0, enablejsapi: 1 } });
|
|
179
|
+
}
|
|
180
|
+
if (vimeoLink) src = `https://player.vimeo.com/video/${vimeoLink[2]}`;
|
|
181
|
+
|
|
182
|
+
return src;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const StyledVideo = styled.div`
|
|
186
|
+
width: 65%;
|
|
187
|
+
|
|
188
|
+
& > div {
|
|
189
|
+
position: relative;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
width: 100%;
|
|
192
|
+
padding-top: 56.25%;
|
|
193
|
+
|
|
194
|
+
& > iframe {
|
|
195
|
+
position: absolute;
|
|
196
|
+
top: 0;
|
|
197
|
+
left: 0;
|
|
198
|
+
bottom: 0;
|
|
199
|
+
right: 0;
|
|
200
|
+
width: 100%;
|
|
201
|
+
height: 100%;
|
|
202
|
+
border: 0px;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
`;
|
|
206
|
+
|
|
207
|
+
interface MarkdownVideoProps {
|
|
208
|
+
src: string;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const MarkdownVideo = ({ src }: MarkdownVideoProps) => {
|
|
212
|
+
return (
|
|
213
|
+
<StyledVideo>
|
|
214
|
+
<div>
|
|
215
|
+
<iframe loading="lazy" allowFullScreen src={src} />
|
|
216
|
+
</div>
|
|
217
|
+
</StyledVideo>
|
|
218
|
+
);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const StyledMarkdownLink = styled(Link)`
|
|
222
|
+
overflow-wrap: break-word;
|
|
223
|
+
|
|
224
|
+
h1 &, h2 &, h3 &, h4 &, h5 & {
|
|
225
|
+
overflow-wrap: normal;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
&:hover {
|
|
229
|
+
/* Links normally don't have underline, but in markdown we want to have it */
|
|
230
|
+
text-decoration: underline;
|
|
231
|
+
}
|
|
232
|
+
`;
|
|
233
|
+
|
|
234
|
+
export const MarkdownLink: React.FC<WithOptional<LinkProps, 'to'>> = ({
|
|
235
|
+
to,
|
|
236
|
+
children,
|
|
237
|
+
rel,
|
|
238
|
+
className,
|
|
239
|
+
}) => {
|
|
240
|
+
return (to && typeof to === 'string') ? (
|
|
241
|
+
<StyledMarkdownLink to={to} hideExternalIcon={true} rel={rel} className={className}>{children}</StyledMarkdownLink>
|
|
242
|
+
) : (
|
|
243
|
+
(<span>{children}</span>) // Invalid links can be rendered as a regular span instead
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const MarkdownBlockQuote = styled(Box).attrs({
|
|
248
|
+
my: 'space16',
|
|
249
|
+
py: 'none',
|
|
250
|
+
forwardedAs: 'blockquote',
|
|
251
|
+
})`
|
|
252
|
+
border-left: 2px solid ${theme.color.neutral.separatorSubtle};
|
|
253
|
+
padding-left: ${theme.space.space16};
|
|
254
|
+
|
|
255
|
+
color: ${theme.color.neutral.textMuted};
|
|
256
|
+
|
|
257
|
+
& > p {
|
|
258
|
+
margin: 0;
|
|
259
|
+
}
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
export const MarkdownTable = styled.table`
|
|
263
|
+
display: block;
|
|
264
|
+
overflow: auto;
|
|
265
|
+
border-collapse: collapse;
|
|
266
|
+
|
|
267
|
+
td, th {
|
|
268
|
+
border: 1px solid ${theme.color.neutral.border};
|
|
269
|
+
padding: ${theme.space.space16};
|
|
270
|
+
text-align: left;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
tr:nth-child(even):not([class]) {
|
|
274
|
+
> th, > td {
|
|
275
|
+
background-color: inherit;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
`;
|
|
279
|
+
|
|
280
|
+
// TODO: This should be used for readmes
|
|
281
|
+
export const MarkdownParagraphContent: React.FC<SharedTextProps & Pick<ReactMarkdownProps, 'node'>> = ({
|
|
282
|
+
children,
|
|
283
|
+
node,
|
|
284
|
+
}) => {
|
|
285
|
+
const child = node.children[0];
|
|
286
|
+
const isText = child?.type === 'text';
|
|
287
|
+
|
|
288
|
+
const videoSrc = isText && getVideoSrc(child.value);
|
|
289
|
+
if (videoSrc) return <MarkdownVideo src={videoSrc} />;
|
|
290
|
+
|
|
291
|
+
// the || null part is important because non-supported elements will fall back to this paragraph component;
|
|
292
|
+
return children || null;
|
|
293
|
+
};
|