@apify/ui-library 0.71.1-featcolortokens-178953.63 → 0.71.1-featcolortokens-178953.70

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 (86) hide show
  1. package/dist/tsconfig.build.tsbuildinfo +1 -1
  2. package/package.json +3 -2
  3. package/src/codemods/generate_typograpy_tokens_files.mjs +137 -0
  4. package/src/components/action_link.tsx +60 -0
  5. package/src/components/actor_template_card.tsx +116 -0
  6. package/src/components/badge.tsx +148 -0
  7. package/src/components/banner.tsx +94 -0
  8. package/src/components/blog_article.tsx +85 -0
  9. package/src/components/box.tsx +127 -0
  10. package/src/components/button.tsx +305 -0
  11. package/src/components/chip.tsx +128 -0
  12. package/src/components/code/action_button.tsx +96 -0
  13. package/src/components/code/code_block/code_block.styled.tsx +180 -0
  14. package/src/components/code/code_block/code_block.tsx +224 -0
  15. package/src/components/code/code_block/code_block_with_tabs.tsx +257 -0
  16. package/src/components/code/code_block/utils.tsx +67 -0
  17. package/src/components/code/index.ts +5 -0
  18. package/src/components/code/inline_code/inline_code.tsx +62 -0
  19. package/src/components/code/one_line_code/one_line_code.tsx +228 -0
  20. package/src/components/code/prism_highlighter.tsx +180 -0
  21. package/src/components/color_wheel_gradient.tsx +31 -0
  22. package/src/components/floating/index.ts +3 -0
  23. package/src/components/floating/menu.tsx +189 -0
  24. package/src/components/floating/menu_common.tsx +31 -0
  25. package/src/components/floating/menu_components.tsx +99 -0
  26. package/src/components/image.tsx +24 -0
  27. package/src/components/index.ts +22 -0
  28. package/src/components/link.tsx +114 -0
  29. package/src/components/message.tsx +153 -0
  30. package/src/components/rating.tsx +106 -0
  31. package/src/components/readme_renderer/index.ts +3 -0
  32. package/src/components/readme_renderer/pythonize_value.ts +76 -0
  33. package/src/components/readme_renderer/table_of_contents.tsx +272 -0
  34. package/src/components/readme_renderer/utils.tsx +46 -0
  35. package/src/components/simple_markdown/index.ts +2 -0
  36. package/src/components/simple_markdown/simple_markdown.tsx +214 -0
  37. package/src/components/simple_markdown/simple_markdown_components.tsx +293 -0
  38. package/src/components/tabs/index.ts +2 -0
  39. package/src/components/tabs/tab.tsx +217 -0
  40. package/src/components/tabs/tabs.tsx +169 -0
  41. package/src/components/tag.tsx +196 -0
  42. package/src/components/text/heading_content.tsx +56 -0
  43. package/src/components/text/heading_marketing.tsx +55 -0
  44. package/src/components/text/heading_shared.tsx +55 -0
  45. package/src/components/text/index.ts +19 -0
  46. package/src/components/text/text_base.tsx +52 -0
  47. package/src/components/text/text_content.tsx +104 -0
  48. package/src/components/text/text_marketing.tsx +152 -0
  49. package/src/components/text/text_shared.tsx +95 -0
  50. package/src/components/tile/horizontal_tile.tsx +77 -0
  51. package/src/components/tile/index.ts +2 -0
  52. package/src/components/tile/shared.ts +27 -0
  53. package/src/components/tile/vertical_tile.tsx +59 -0
  54. package/src/components/to_consolidate/card.tsx +141 -0
  55. package/src/components/to_consolidate/index.ts +4 -0
  56. package/src/components/to_consolidate/markdown.tsx +609 -0
  57. package/src/components/to_consolidate/pagination.tsx +136 -0
  58. package/src/components/to_consolidate/tab_number_chip.tsx +31 -0
  59. package/src/design_system/colors/build_color_tokens.js +183 -0
  60. package/src/design_system/colors/figma_color_tokens.dark.json +886 -0
  61. package/src/design_system/colors/figma_color_tokens.light.json +886 -0
  62. package/src/design_system/colors/generated/colors_theme.dark.ts +110 -0
  63. package/src/design_system/colors/generated/colors_theme.light.ts +110 -0
  64. package/src/design_system/colors/generated/css_variables.dark.ts +147 -0
  65. package/src/design_system/colors/generated/css_variables.light.ts +147 -0
  66. package/src/design_system/colors/generated/css_variables_palette.dark.ts +74 -0
  67. package/src/design_system/colors/generated/css_variables_palette.light.ts +74 -0
  68. package/src/design_system/colors/generated/properties_theme.ts +179 -0
  69. package/src/design_system/colors/index.ts +7 -0
  70. package/src/design_system/supernova_typography_tokens.json +657 -0
  71. package/src/design_system/theme.ts +25 -0
  72. package/src/design_system/tokens/index.ts +5 -0
  73. package/src/design_system/tokens/layouts.ts +29 -0
  74. package/src/design_system/tokens/radiuses.ts +22 -0
  75. package/src/design_system/tokens/shadows.ts +22 -0
  76. package/src/design_system/tokens/spaces.ts +15 -0
  77. package/src/design_system/tokens/transitions.ts +19 -0
  78. package/src/design_system/typography_theme.ts +197 -0
  79. package/src/index.ts +8 -0
  80. package/src/type_utils.ts +7 -0
  81. package/src/ui_dependency_provider.tsx +58 -0
  82. package/src/utils/copy_to_clipboard.ts +24 -0
  83. package/src/utils/image_color.ts +42 -0
  84. package/src/utils/index.ts +4 -0
  85. package/src/utils/resize_observer.ts +18 -0
  86. package/src/utils/sanitization.ts +14 -0
@@ -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
+ };