@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.
Files changed (121) hide show
  1. package/dist/src/design_system/colors/generated/{dark.d.ts → css_variables.dark.d.ts} +1 -1
  2. package/dist/src/design_system/colors/generated/css_variables.dark.d.ts.map +1 -0
  3. package/dist/src/design_system/colors/generated/css_variables.dark.js +147 -0
  4. package/dist/src/design_system/colors/generated/css_variables.dark.js.map +1 -0
  5. package/dist/src/design_system/colors/generated/{light.d.ts → css_variables.light.d.ts} +1 -1
  6. package/dist/src/design_system/colors/generated/css_variables.light.d.ts.map +1 -0
  7. package/dist/src/design_system/colors/generated/css_variables.light.js +147 -0
  8. package/dist/src/design_system/colors/generated/css_variables.light.js.map +1 -0
  9. package/dist/src/design_system/colors/generated/{palette.dark.d.ts → css_variables_palette.dark.d.ts} +1 -1
  10. package/dist/src/design_system/colors/generated/css_variables_palette.dark.d.ts.map +1 -0
  11. package/dist/src/design_system/colors/generated/css_variables_palette.dark.js +74 -0
  12. package/dist/src/design_system/colors/generated/css_variables_palette.dark.js.map +1 -0
  13. package/dist/src/design_system/colors/generated/{palette.light.d.ts → css_variables_palette.light.d.ts} +1 -1
  14. package/dist/src/design_system/colors/generated/css_variables_palette.light.d.ts.map +1 -0
  15. package/dist/src/design_system/colors/generated/css_variables_palette.light.js +74 -0
  16. package/dist/src/design_system/colors/generated/css_variables_palette.light.js.map +1 -0
  17. package/dist/src/design_system/colors/index.d.ts +4 -4
  18. package/dist/src/design_system/colors/index.d.ts.map +1 -1
  19. package/dist/src/design_system/colors/index.js +4 -4
  20. package/dist/src/design_system/colors/index.js.map +1 -1
  21. package/dist/tsconfig.build.tsbuildinfo +1 -1
  22. package/package.json +3 -2
  23. package/src/codemods/generate_typograpy_tokens_files.mjs +137 -0
  24. package/src/components/action_link.tsx +60 -0
  25. package/src/components/actor_template_card.tsx +116 -0
  26. package/src/components/badge.tsx +148 -0
  27. package/src/components/banner.tsx +94 -0
  28. package/src/components/blog_article.tsx +85 -0
  29. package/src/components/box.tsx +127 -0
  30. package/src/components/button.tsx +305 -0
  31. package/src/components/chip.tsx +128 -0
  32. package/src/components/code/action_button.tsx +96 -0
  33. package/src/components/code/code_block/code_block.styled.tsx +180 -0
  34. package/src/components/code/code_block/code_block.tsx +224 -0
  35. package/src/components/code/code_block/code_block_with_tabs.tsx +257 -0
  36. package/src/components/code/code_block/utils.tsx +67 -0
  37. package/src/components/code/index.ts +5 -0
  38. package/src/components/code/inline_code/inline_code.tsx +62 -0
  39. package/src/components/code/one_line_code/one_line_code.tsx +228 -0
  40. package/src/components/code/prism_highlighter.tsx +180 -0
  41. package/src/components/color_wheel_gradient.tsx +31 -0
  42. package/src/components/floating/index.ts +3 -0
  43. package/src/components/floating/menu.tsx +189 -0
  44. package/src/components/floating/menu_common.tsx +31 -0
  45. package/src/components/floating/menu_components.tsx +99 -0
  46. package/src/components/image.tsx +24 -0
  47. package/src/components/index.ts +22 -0
  48. package/src/components/link.tsx +114 -0
  49. package/src/components/message.tsx +153 -0
  50. package/src/components/rating.tsx +106 -0
  51. package/src/components/readme_renderer/index.ts +3 -0
  52. package/src/components/readme_renderer/pythonize_value.ts +76 -0
  53. package/src/components/readme_renderer/table_of_contents.tsx +272 -0
  54. package/src/components/readme_renderer/utils.tsx +46 -0
  55. package/src/components/simple_markdown/index.ts +2 -0
  56. package/src/components/simple_markdown/simple_markdown.tsx +214 -0
  57. package/src/components/simple_markdown/simple_markdown_components.tsx +293 -0
  58. package/src/components/tabs/index.ts +2 -0
  59. package/src/components/tabs/tab.tsx +217 -0
  60. package/src/components/tabs/tabs.tsx +169 -0
  61. package/src/components/tag.tsx +196 -0
  62. package/src/components/text/heading_content.tsx +56 -0
  63. package/src/components/text/heading_marketing.tsx +55 -0
  64. package/src/components/text/heading_shared.tsx +55 -0
  65. package/src/components/text/index.ts +19 -0
  66. package/src/components/text/text_base.tsx +52 -0
  67. package/src/components/text/text_content.tsx +104 -0
  68. package/src/components/text/text_marketing.tsx +152 -0
  69. package/src/components/text/text_shared.tsx +95 -0
  70. package/src/components/tile/horizontal_tile.tsx +77 -0
  71. package/src/components/tile/index.ts +2 -0
  72. package/src/components/tile/shared.ts +27 -0
  73. package/src/components/tile/vertical_tile.tsx +59 -0
  74. package/src/components/to_consolidate/card.tsx +141 -0
  75. package/src/components/to_consolidate/index.ts +4 -0
  76. package/src/components/to_consolidate/markdown.tsx +609 -0
  77. package/src/components/to_consolidate/pagination.tsx +136 -0
  78. package/src/components/to_consolidate/tab_number_chip.tsx +31 -0
  79. package/src/design_system/colors/build_color_tokens.js +183 -0
  80. package/src/design_system/colors/figma_color_tokens.dark.json +886 -0
  81. package/src/design_system/colors/figma_color_tokens.light.json +886 -0
  82. package/src/design_system/colors/generated/colors_theme.dark.ts +110 -0
  83. package/src/design_system/colors/generated/colors_theme.light.ts +110 -0
  84. package/{dist/src/design_system/colors/generated/dark.js → src/design_system/colors/generated/css_variables.dark.ts} +1 -1
  85. package/{dist/src/design_system/colors/generated/light.js → src/design_system/colors/generated/css_variables.light.ts} +1 -1
  86. package/{dist/src/design_system/colors/generated/palette.dark.js → src/design_system/colors/generated/css_variables_palette.dark.ts} +1 -1
  87. package/{dist/src/design_system/colors/generated/palette.light.js → src/design_system/colors/generated/css_variables_palette.light.ts} +1 -1
  88. package/{dist/src/design_system/properties_theme.js → src/design_system/colors/generated/properties_theme.ts} +20 -156
  89. package/src/design_system/colors/index.ts +7 -0
  90. package/src/design_system/supernova_typography_tokens.json +657 -0
  91. package/src/design_system/theme.ts +25 -0
  92. package/src/design_system/tokens/index.ts +5 -0
  93. package/src/design_system/tokens/layouts.ts +29 -0
  94. package/src/design_system/tokens/radiuses.ts +22 -0
  95. package/src/design_system/tokens/shadows.ts +22 -0
  96. package/src/design_system/tokens/spaces.ts +15 -0
  97. package/src/design_system/tokens/transitions.ts +19 -0
  98. package/src/design_system/typography_theme.ts +197 -0
  99. package/src/index.ts +8 -0
  100. package/src/type_utils.ts +7 -0
  101. package/src/ui_dependency_provider.tsx +58 -0
  102. package/src/utils/copy_to_clipboard.ts +24 -0
  103. package/src/utils/image_color.ts +42 -0
  104. package/src/utils/index.ts +4 -0
  105. package/src/utils/resize_observer.ts +18 -0
  106. package/src/utils/sanitization.ts +14 -0
  107. package/dist/src/design_system/colors/generated/dark.d.ts.map +0 -1
  108. package/dist/src/design_system/colors/generated/dark.js.map +0 -1
  109. package/dist/src/design_system/colors/generated/light.d.ts.map +0 -1
  110. package/dist/src/design_system/colors/generated/light.js.map +0 -1
  111. package/dist/src/design_system/colors/generated/palette.dark.d.ts.map +0 -1
  112. package/dist/src/design_system/colors/generated/palette.dark.js.map +0 -1
  113. package/dist/src/design_system/colors/generated/palette.light.d.ts.map +0 -1
  114. package/dist/src/design_system/colors/generated/palette.light.js.map +0 -1
  115. package/dist/src/design_system/colors_theme.d.ts +0 -213
  116. package/dist/src/design_system/colors_theme.d.ts.map +0 -1
  117. package/dist/src/design_system/colors_theme.js +0 -213
  118. package/dist/src/design_system/colors_theme.js.map +0 -1
  119. package/dist/src/design_system/properties_theme.d.ts +0 -175
  120. package/dist/src/design_system/properties_theme.d.ts.map +0 -1
  121. package/dist/src/design_system/properties_theme.js.map +0 -1
@@ -0,0 +1,180 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ import { theme } from '../../../design_system/theme.js';
4
+ import { Box } from '../../box.js';
5
+ import type { SharedTextProps, SharedTextSize } from '../../text/text_shared.js';
6
+
7
+ export type SyntaxHighlighterBaseStylesWrapperProps = SharedTextProps & {
8
+ $fullWidth?: boolean;
9
+ $fullHeight?: string;
10
+ $showLineNumbers?: boolean;
11
+ }
12
+
13
+ export const SyntaxHighlighterBaseStylesWrapper = styled(Box)<SyntaxHighlighterBaseStylesWrapperProps>`
14
+ background-color: ${theme.color.neutral.backgroundMuted} !important;
15
+ border: 1px solid ${theme.color.neutral.border};
16
+ border-radius: ${theme.radius.radius12};
17
+ overflow: hidden;
18
+
19
+ width: 100%;
20
+ ${({ $fullWidth }) => !$fullWidth && css`max-width: 860px;`}
21
+ ${({ $fullHeight }) => !$fullHeight && css`max-height: 600px;`}
22
+
23
+ background-color: transparent;
24
+ position: relative;
25
+ `;
26
+
27
+ export const CodeBlockWrapper = styled(SyntaxHighlighterBaseStylesWrapper) <{
28
+ $hasHeader: boolean;
29
+ $gradientRotation?: number;
30
+ $highlightLines?: number[];
31
+ $hasActionButton?: boolean;
32
+ $hasTabs?: boolean;
33
+ $fullWidth?: boolean;
34
+ $showLineNumbers?: boolean;
35
+ $fullHeight?: boolean;
36
+ size?: SharedTextSize,
37
+ }>`
38
+ position: relative;
39
+ display: flex;
40
+ flex-direction: column;
41
+ min-width: 266px;
42
+
43
+ .CodeBlock-Header {
44
+ padding-top: 7px; /* only 7 due to transform translateY(1px) */
45
+ gap: ${theme.space.space4};
46
+ background-color: ${theme.color.neutral.backgroundSubtle};
47
+ border-bottom: 1px solid ${theme.color.neutral.border};
48
+ z-index: 3;
49
+ position: relative;
50
+ display: flex;
51
+ height: 4rem;
52
+
53
+ .CodeBlock-HeaderTitle {
54
+ margin-left: ${theme.space.space16};
55
+ margin-bottom: ${theme.space.space8};
56
+ margin-top: 0.5rem;
57
+ }
58
+
59
+ .CodeBlock-HeaderTabsWrapper {
60
+ display: flex;
61
+ transform: translateY(1px);
62
+ padding-right: ${theme.space.space32};
63
+ padding-left: ${theme.space.space8};
64
+ overflow-x: auto;
65
+ overflow-y: hidden;
66
+ gap: ${theme.space.space4};
67
+
68
+ margin-right: ${({ $hasActionButton }) => ($hasActionButton ? '128' : '20')}px;
69
+
70
+ .CodeBlock-Tab {
71
+ border-radius: ${theme.radius.radius8};
72
+ background-color: transparent;
73
+ border: 1px solid transparent;
74
+ border-bottom-left-radius: 0;
75
+ border-bottom-right-radius: 0;
76
+ border-bottom: none;
77
+ cursor: pointer;
78
+ height: 3.2rem;
79
+ max-width: 26rem;
80
+
81
+ color: ${theme.color.neutral.textMuted};
82
+
83
+ text-align: center;
84
+ white-space: nowrap;
85
+
86
+ padding: 0.2rem ${theme.space.space8} 0.6rem ${theme.space.space8};
87
+ p {
88
+ padding-top: 0.2rem;
89
+ text-overflow: ellipsis;
90
+ overflow: hidden;
91
+ }
92
+
93
+
94
+ &.selected {
95
+ color: ${theme.color.neutral.text};
96
+ border: 1px solid ${theme.color.neutral.border};
97
+ background-color: ${theme.color.neutral.backgroundMuted};
98
+ border-bottom: none;
99
+ padding: 0.6rem ${theme.space.space8};
100
+ p {
101
+ padding-top: 0;
102
+ }
103
+ }
104
+
105
+ &:hover {
106
+ border: 1px solid ${theme.color.neutral.border};
107
+ border-bottom: none;
108
+ text-decoration: none;
109
+ }
110
+
111
+ &::after {
112
+ /* ensures same width of text when switching tabs and making it bold */
113
+ ${theme.typography.shared.tablet.bodyMStrong}
114
+ display: block;
115
+ content: attr(title);
116
+ font-weight: bold;
117
+ height: 0px;
118
+ color: transparent;
119
+ overflow: hidden;
120
+ visibility: hidden;
121
+ }
122
+ }
123
+ }
124
+ }
125
+
126
+
127
+ .CodeBlock-TopRightActionsWrapper {
128
+ position: absolute;
129
+ top: 0;
130
+ right: 0;
131
+ ${({ $hasHeader }) => ($hasHeader
132
+ ? css`
133
+ padding-top: 6px;
134
+ padding-right: ${theme.space.space8};`
135
+ : css`
136
+ padding-top: ${theme.space.space16};
137
+ padding-right: ${theme.space.space16};`)}
138
+
139
+ display: flex;
140
+ gap: ${theme.space.space8};
141
+ z-index: 3;
142
+ height: 3.9rem;
143
+
144
+ ${({ $hasHeader }) => $hasHeader && css`
145
+ padding-left: ${theme.space.space32};
146
+ background: linear-gradient(
147
+ to right,
148
+ transparent,
149
+ ${theme.color.neutral.backgroundSubtle} 20px
150
+ );
151
+ `}
152
+ }
153
+
154
+ .CodeBlock-Gradient {
155
+ right: 0px;
156
+ bottom: 0px;
157
+
158
+ aspect-ratio: 1/1;
159
+ height: 250%;
160
+
161
+ transform: translate(50%, 70%) rotate(${({ $gradientRotation }) => $gradientRotation ?? 0}deg) scaleX(-1);
162
+ }
163
+
164
+ .CodeBlock-HederDotsWrapper {
165
+ display: flex;
166
+ padding: ${theme.space.space8};
167
+ align-items: flex-start;
168
+ gap: ${theme.space.space8};
169
+ margin-bottom: 0.5rem;
170
+ margin-left: ${theme.space.space8};
171
+ background: transparent;
172
+
173
+ .CodeBlock-HeaderDot {
174
+ width: 1.2rem;
175
+ height: 1.2rem;
176
+ border-radius: 50%;
177
+ background-color: ${theme.color.neutral.iconSubtle};
178
+ }
179
+ }
180
+ `;
@@ -0,0 +1,224 @@
1
+ import clsx from 'clsx';
2
+ import React, { useMemo, useRef, useState } from 'react';
3
+
4
+ import type { RegularBoxProps } from '../../box.js';
5
+ import { ColorWheelGradient } from '../../color_wheel_gradient.js';
6
+ import { Link } from '../../link.js';
7
+ import { Text } from '../../text/index.js';
8
+ import type { SharedTextSize } from '../../text/text_shared.js';
9
+ import { ActionButton, CopyButton } from '../action_button.js';
10
+ import { PrismSyntaxHighlighter } from '../prism_highlighter.js';
11
+ import { CodeBlockWrapper } from './code_block.styled.js';
12
+ import { getBashLinePrefixes, getNumberLinePrefixes } from './utils.js';
13
+
14
+ type CodeTab = {
15
+ key: string;
16
+ label: string;
17
+ code: string;
18
+ id?: string;
19
+ highlightLines?: number[];
20
+ language?: string;
21
+ bashCommandsStart?: number[];
22
+ to?: string;
23
+ }
24
+
25
+ type HeaderProps = {
26
+ tabs: CodeTab[] | null;
27
+ currentTab: CodeTab | null;
28
+ onTabChange: (tab: CodeTab, e: React.MouseEvent) => void;
29
+ title?: string;
30
+ showBashHeader?: boolean;
31
+ }
32
+
33
+ const LANGUAGES_WITHOUT_LINE_NUMBERS = ['json', 'jsonp', 'jsonp', 'rss', 'yaml', 'xml', 'html', 'bash', 'text', 'dockerfile', 'http'];
34
+
35
+ const HeaderDots = () => {
36
+ return (
37
+ <div className="CodeBlock-HederDotsWrapper">
38
+ <div className="CodeBlock-HeaderDot" />
39
+ <div className="CodeBlock-HeaderDot" />
40
+ <div className="CodeBlock-HeaderDot" />
41
+ </div>
42
+ );
43
+ };
44
+
45
+ const Header = ({
46
+ tabs,
47
+ currentTab,
48
+ showBashHeader,
49
+ onTabChange,
50
+ title,
51
+ }: HeaderProps) => {
52
+ return (
53
+ <div className="CodeBlock-Header">
54
+ {showBashHeader && <HeaderDots />}
55
+ {title && (
56
+ <Text weight="bold" className="CodeBlock-HeaderTitle">
57
+ {title}
58
+ </Text>
59
+ )}
60
+ <div className="CodeBlock-HeaderTabsWrapper">
61
+ {tabs?.map((tab) => {
62
+ const selected = tab.key === currentTab?.key;
63
+ const props = {
64
+ className: clsx('CodeBlock-Tab', { selected }),
65
+ onClick: (e: React.MouseEvent) => onTabChange(tab, e),
66
+ title: tab.label,
67
+ id: tab.id ?? tab.key,
68
+ };
69
+
70
+ const label = <Text weight={tab.key === currentTab?.key ? 'bold' : 'normal'}>
71
+ {tab.label}
72
+ </Text>;
73
+
74
+ if (tab.to) {
75
+ return (
76
+ <Link key={tab.key} to={tab.to} {...props}>
77
+ {label}
78
+ </Link>
79
+ );
80
+ }
81
+ return (
82
+ <div key={tab.key} {...props}>
83
+ {label}
84
+ </div>
85
+ );
86
+ })}
87
+ </div>
88
+ </div>
89
+ );
90
+ };
91
+
92
+ export type CodeBlockProps = RegularBoxProps & {
93
+ content: string | CodeTab[]; // TODO: Try to make it work with children props
94
+ size?: SharedTextSize;
95
+ language?: string | undefined;
96
+ className?: string;
97
+ hideBashHeader?: boolean;
98
+ onTabChange?: (key: string, e: React.MouseEvent) => Promise<void> | void;
99
+ showGradient?: boolean;
100
+ gradientRotation?: number;
101
+ highlightLines?: number[];
102
+ showActionButton?: boolean;
103
+ actionButtonLabel?: string;
104
+ onActionButtonClick?: () => Promise<void> | void;
105
+ defaultTabKey?: string;
106
+ fullWidth?: boolean;
107
+ fullHeight?: boolean;
108
+ title?: string;
109
+ hideCopyButton?: boolean;
110
+ bashCommandsStart?: number[];
111
+ hideBashPromptPrefixes?: boolean;
112
+ hideLineNumbers?: boolean | undefined;
113
+ }
114
+
115
+ export const CodeBlock = ({
116
+ content,
117
+ size,
118
+ language,
119
+ highlightLines,
120
+ onTabChange,
121
+ defaultTabKey,
122
+ showGradient,
123
+ hideBashHeader,
124
+ showActionButton,
125
+ fullWidth,
126
+ actionButtonLabel,
127
+ gradientRotation = 340,
128
+ hideLineNumbers,
129
+ fullHeight,
130
+ title,
131
+ hideCopyButton,
132
+ bashCommandsStart,
133
+ hideBashPromptPrefixes,
134
+ onActionButtonClick,
135
+ ...rest
136
+ }: CodeBlockProps) => {
137
+ const isMultiTab = content instanceof Array;
138
+ const defaultTab = isMultiTab
139
+ ? content.find((tab) => tab.key === defaultTabKey) ?? content[0]
140
+ : null;
141
+ const [currentTab, setCurrentTab] = useState<CodeTab | null>(defaultTab);
142
+ const currentCode = (isMultiTab ? currentTab?.code : content)?.trim() ?? '';
143
+ const currentLanguage = language ?? currentTab?.language;
144
+
145
+ const currentHighlightLines = useMemo(
146
+ () => (isMultiTab ? currentTab?.highlightLines ?? [] : highlightLines),
147
+ [isMultiTab, currentTab, highlightLines],
148
+ );
149
+
150
+ const isBash = currentLanguage === 'bash';
151
+
152
+ const showLineNumbers = !hideLineNumbers && !LANGUAGES_WITHOUT_LINE_NUMBERS.includes(currentLanguage ?? '');
153
+ const showBashHeader = isBash && !hideBashHeader && !isMultiTab;
154
+
155
+ const showHeader = isMultiTab || !!title || showBashHeader;
156
+
157
+ const syntaxHighlighterRef = useRef<HTMLPreElement>(null);
158
+
159
+ const handleTabClick = async (tab: CodeTab, e: React.MouseEvent) => {
160
+ await onTabChange?.(tab.key, e);
161
+ setCurrentTab(tab);
162
+ syntaxHighlighterRef.current?.scrollTo(0, 0);
163
+ };
164
+
165
+ const showBashPrompt = isBash && !hideBashPromptPrefixes;
166
+ const effectiveBashCommandsStart = currentTab?.bashCommandsStart ?? bashCommandsStart;
167
+
168
+ const linePrefixes = useMemo(() => {
169
+ if (showBashPrompt) {
170
+ return getBashLinePrefixes(currentCode, effectiveBashCommandsStart);
171
+ }
172
+
173
+ if (showLineNumbers) {
174
+ return getNumberLinePrefixes(currentCode);
175
+ }
176
+
177
+ return {};
178
+ }, [showLineNumbers, showBashPrompt, currentCode, effectiveBashCommandsStart]);
179
+
180
+ return (
181
+ <CodeBlockWrapper
182
+ $hasHeader={showHeader}
183
+ $gradientRotation={gradientRotation}
184
+ $hasActionButton={showActionButton}
185
+ $hasTabs={isMultiTab}
186
+ $fullWidth={fullWidth}
187
+ $fullHeight={fullHeight}
188
+ $showLineNumbers={showLineNumbers}
189
+ {...rest}
190
+ >
191
+ {showHeader && (
192
+ <Header
193
+ tabs={isMultiTab ? content : null}
194
+ currentTab={currentTab}
195
+ onTabChange={handleTabClick}
196
+ showBashHeader={showBashHeader}
197
+ title={title}
198
+ />
199
+ )}
200
+
201
+ <PrismSyntaxHighlighter
202
+ code={currentCode}
203
+ linePrefixes={linePrefixes}
204
+ highlightLines={currentHighlightLines}
205
+ language={currentLanguage || 'text'}
206
+ size={size}
207
+ ref={syntaxHighlighterRef}
208
+ isSingleLine={false}
209
+ />
210
+
211
+ <div className="CodeBlock-TopRightActionsWrapper">
212
+ {showActionButton && <ActionButton successStyle onClick={onActionButtonClick}>{actionButtonLabel ?? 'Run on Apify'}</ActionButton>}
213
+ {!hideCopyButton && <CopyButton code={currentCode} />}
214
+ </div>
215
+
216
+ {showGradient && (
217
+ <ColorWheelGradient
218
+ className="CodeBlock-Gradient"
219
+ blurSize={69}
220
+ />
221
+ )}
222
+ </CodeBlockWrapper >
223
+ );
224
+ };
@@ -0,0 +1,257 @@
1
+ import clsx from 'clsx';
2
+ import React from 'react';
3
+ import styled from 'styled-components';
4
+
5
+ import { theme } from '../../../design_system/theme.js';
6
+ import { useSharedUiDependencies } from '../../../ui_dependency_provider.js';
7
+ import { Box, type MarginSpacingProps, type RegularBoxProps } from '../../box.js';
8
+ import { Link, type RegularLinkProps } from '../../link.js';
9
+ import { HeadingShared } from '../../text/heading_shared.js';
10
+ import { CodeBlock, type CodeBlockProps } from './code_block.js';
11
+
12
+ export type CodeBlockTabKey = 'cli' | 'http' | 'javascript' | 'mcp' | 'openapi' | 'python' | 'typescript';
13
+ type CodeBlockTabConfig = {
14
+ label: string;
15
+ language: string;
16
+ src: string;
17
+ };
18
+
19
+ export const CODE_BLOCK_TAB_CATALOG: Record<CodeBlockTabKey, CodeBlockTabConfig> = {
20
+ cli: {
21
+ label: 'CLI',
22
+ language: 'bash',
23
+ src: 'https://apify.com/img/icons/code.svg',
24
+ },
25
+ http: {
26
+ label: 'HTTP',
27
+ language: 'bash',
28
+ src: 'https://apify.com/img/icons/http.svg',
29
+ },
30
+ javascript: {
31
+ label: 'JavaScript',
32
+ language: 'javascript',
33
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
34
+ src: 'https://apify.com/img/template-icons/javascript.svg',
35
+ },
36
+ mcp: {
37
+ label: 'MCP',
38
+ language: 'bash',
39
+ src: 'https://apify.com/img/icons/mcp.svg',
40
+ },
41
+ openapi: {
42
+ label: 'OpenAPI',
43
+ language: 'json',
44
+ src: 'https://apify.com/img/icons/openapi.svg',
45
+ },
46
+ python: {
47
+ label: 'Python',
48
+ language: 'python',
49
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
50
+ src: 'https://apify.com/img/template-icons/python.svg',
51
+ },
52
+ typescript: {
53
+ label: 'TypeScript',
54
+ language: 'typescript',
55
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
56
+ src: 'https://apify.com/img/template-icons/typescript.svg',
57
+ },
58
+ };
59
+
60
+ type SharedBoxProps = Omit<RegularBoxProps, 'as' | 'children' | 'onClick'> & MarginSpacingProps;
61
+
62
+ type SharedCodeBlockProps = Omit<CodeBlockProps, 'bashCommandsStart' | 'content' | 'language' | 'defaultTabKey' | 'onTabChange'>;
63
+ type SharedLinkProps = Pick<RegularLinkProps, 'to' | 'rel' | 'target'>;
64
+
65
+ export type CodeBlockTabProps = {
66
+ key: CodeBlockTabKey;
67
+ bashCommandsStart?: number[];
68
+ content: string;
69
+ languageOverride?: string;
70
+ onClick?: (e: React.MouseEvent) => void;
71
+ } & Partial<SharedLinkProps>;
72
+
73
+ type CodeBlockWithTabsProps = SharedBoxProps & {
74
+ codeBlockProps: SharedCodeBlockProps;
75
+ currentTabKey: CodeBlockTabKey;
76
+ tabs: CodeBlockTabProps[];
77
+ };
78
+
79
+ export const CODE_BLOCK_WITH_TABS_CLASSNAMES = {
80
+ WRAPPER: 'CodeBlockWithTabsWrapper',
81
+ TABS: 'CodeBlockWithTabsTabs',
82
+ TAB: 'CodeBlockWithTabsTab',
83
+ CONTENT: 'CodeBlockWithTabsContent',
84
+ };
85
+
86
+ const CodeBlockWithTabsWrapper = styled(Box)`
87
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.TABS} {
88
+ min-width: 266px;
89
+ height: 72px;
90
+ padding-inline: ${theme.space.space8};
91
+ display: flex;
92
+ gap: ${theme.space.space4};
93
+ overflow-x: auto;
94
+ background-color: ${theme.color.neutral.backgroundSubtle};
95
+ border: 1px solid ${theme.color.neutral.border};
96
+ border-bottom: none;
97
+ border-top-right-radius: ${theme.radius.radius12};
98
+ border-top-left-radius: ${theme.radius.radius12};
99
+ position: relative;
100
+
101
+ & > [role="tabpanel"] {
102
+ position: sticky;
103
+ right: 0;
104
+ top: 0;
105
+ height: 100%;
106
+ width: 0;
107
+ margin-left: auto;
108
+
109
+ &::after {
110
+ right: -${theme.space.space8};
111
+ height: 100%;
112
+ width: ${theme.space.space16};
113
+ background: linear-gradient(90deg, rgba(255, 255, 255, 0) 0%, ${theme.color.neutral.backgroundSubtle} 100%);
114
+ content: ' ';
115
+ pointer-events: none;
116
+ position: absolute;
117
+ }
118
+ }
119
+
120
+ @media ${theme.device.tablet} {
121
+ height: 88px;
122
+ justify-content: center;
123
+ gap: ${theme.space.space24};
124
+ padding-inline: ${theme.space.space24};
125
+
126
+ & > [role="tabpanel"] {
127
+ margin-left: unset;
128
+
129
+ &::after {
130
+ right: -${theme.space.space24};
131
+ width: ${theme.space.space32};
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.TAB} {
138
+ min-width: 64px;
139
+ position: relative;
140
+ padding: ${theme.space.space12} ${theme.space.space8};
141
+ color: ${theme.color.neutral.textMuted};
142
+ display: flex;
143
+ flex-direction: column;
144
+ flex-shrink: 0;
145
+ align-items: center;
146
+ justify-content: flex-end;
147
+ gap: ${theme.space.space4};
148
+ cursor: pointer;
149
+
150
+ img {
151
+ width: 20px;
152
+ height: 20px;
153
+ }
154
+
155
+ [role="tabpanel"] {
156
+ bottom: 0;
157
+ width: 100%;
158
+ height: 2px;
159
+ color: ${theme.color.neutral.text};
160
+ background-color: ${theme.color.primaryBlack.action};
161
+ border-radius: ${theme.radius.radiusFull};
162
+ display: none;
163
+ position: absolute;
164
+ z-index: 2;
165
+ }
166
+
167
+ &.selected {
168
+ color: ${theme.color.neutral.text};
169
+
170
+ [role="tabpanel"] {
171
+ display: block;
172
+ }
173
+ }
174
+
175
+ &:hover {
176
+ color: ${theme.color.neutral.text};
177
+ text-decoration: none;
178
+ }
179
+ }
180
+
181
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.CONTENT} {
182
+ max-width: initial;
183
+ border-top-left-radius: 0;
184
+ border-top-right-radius: 0;
185
+ }
186
+ `;
187
+
188
+ const IMG_RESIZE = {
189
+ width: 20,
190
+ height: 20,
191
+ };
192
+
193
+ export const CodeBlockWithTabs = ({ className, codeBlockProps, currentTabKey, tabs, ...props }: CodeBlockWithTabsProps) => {
194
+ const { generateProxyImageUrl } = useSharedUiDependencies();
195
+ const currentTab = tabs.find((tab) => tab.key === currentTabKey) ?? tabs[0];
196
+
197
+ return (
198
+ <CodeBlockWithTabsWrapper className={clsx(CODE_BLOCK_WITH_TABS_CLASSNAMES.WRAPPER, className)} {...props}>
199
+ <div className={CODE_BLOCK_WITH_TABS_CLASSNAMES.TABS}>
200
+ {tabs.map((tab) => {
201
+ const { label, src } = CODE_BLOCK_TAB_CATALOG[tab.key];
202
+ const selected = tab.key === currentTab?.key;
203
+
204
+ const commonProps = {
205
+ className: clsx(CODE_BLOCK_WITH_TABS_CLASSNAMES.TAB, { selected }),
206
+ 'data-test': `code-block-tab-${tab.key}`,
207
+ onClick: tab.onClick,
208
+ };
209
+ const children = (
210
+ <>
211
+ <img src={generateProxyImageUrl?.(src, { resize: IMG_RESIZE }) ?? src} alt={label} />
212
+ <HeadingShared type="titleS" as="p">
213
+ {label}
214
+ </HeadingShared>
215
+ <div role="tabpanel" />
216
+ </>
217
+ );
218
+
219
+ // if the tab has a 'to' prop, render a Link component
220
+ if (tab.to) {
221
+ return (
222
+ <Link
223
+ {...commonProps}
224
+ to={tab.to}
225
+ rel={tab.rel}
226
+ target={tab.target}
227
+ key={tab.key}
228
+ >
229
+ {children}
230
+ </Link>
231
+ );
232
+ }
233
+
234
+ return (
235
+ <div
236
+ {...commonProps}
237
+ role="button"
238
+ key={tab.key}
239
+ >
240
+ {children}
241
+ </div>
242
+ );
243
+ })}
244
+
245
+ <div role="tabpanel" />
246
+ </div>
247
+ <CodeBlock
248
+ bashCommandsStart={currentTab?.bashCommandsStart}
249
+ content={currentTab?.content ?? ''}
250
+ language={currentTab.languageOverride ?? CODE_BLOCK_TAB_CATALOG[currentTab?.key]?.language}
251
+ className={CODE_BLOCK_WITH_TABS_CLASSNAMES.CONTENT}
252
+ hideBashHeader
253
+ {...codeBlockProps}
254
+ />
255
+ </CodeBlockWithTabsWrapper>
256
+ );
257
+ };