@apify/ui-library 0.63.1 → 0.63.2-featcodeblockwithtabs-5a4360.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apify/ui-library",
3
- "version": "0.63.1",
3
+ "version": "0.63.2-featcodeblockwithtabs-5a4360.31+71d988711ce",
4
4
  "description": "React UI library used by apify.com",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -66,5 +66,5 @@
66
66
  "typescript": "^5.1.6",
67
67
  "typescript-eslint": "^8.24.0"
68
68
  },
69
- "gitHead": "578d4492a2df2afbc48bb0fe0a2bffd11ba8a06f"
69
+ "gitHead": "71d988711ce0ad1fd229d4887d7ff538b4cc1bfc"
70
70
  }
@@ -32,7 +32,7 @@ type HeaderProps = {
32
32
 
33
33
  const LANGUAGES_WITHOUT_LINE_NUMBERS = ['json', 'jsonp', 'jsonp', 'rss', 'yaml', 'xml', 'html', 'bash', 'text', 'dockerfile', 'http'];
34
34
 
35
- function HeaderDots() {
35
+ const HeaderDots = () => {
36
36
  return (
37
37
  <div className="CodeBlock-HederDotsWrapper">
38
38
  <div className="CodeBlock-HeaderDot" />
@@ -40,15 +40,15 @@ function HeaderDots() {
40
40
  <div className="CodeBlock-HeaderDot" />
41
41
  </div>
42
42
  );
43
- }
43
+ };
44
44
 
45
- function Header({
45
+ const Header = ({
46
46
  tabs,
47
47
  currentTab,
48
48
  showBashHeader,
49
49
  onTabChange,
50
50
  title,
51
- }: HeaderProps) {
51
+ }: HeaderProps) => {
52
52
  return (
53
53
  <div className="CodeBlock-Header">
54
54
  {showBashHeader && <HeaderDots />}
@@ -87,9 +87,9 @@ function Header({
87
87
  </div>
88
88
  </div>
89
89
  );
90
- }
90
+ };
91
91
 
92
- type CodeBlockProps = RegularBoxProps & {
92
+ export type CodeBlockProps = RegularBoxProps & {
93
93
  content: string | CodeTab[]; // TODO: Try to make it work with children props
94
94
  size?: SharedTextSize;
95
95
  language?: string | undefined;
@@ -112,7 +112,7 @@ type CodeBlockProps = RegularBoxProps & {
112
112
  hideLineNumbers?: boolean | undefined;
113
113
  }
114
114
 
115
- export function CodeBlock({
115
+ export const CodeBlock = ({
116
116
  content,
117
117
  size,
118
118
  language,
@@ -133,7 +133,7 @@ export function CodeBlock({
133
133
  hideBashPromptPrefixes,
134
134
  onActionButtonClick,
135
135
  ...rest
136
- }: CodeBlockProps) {
136
+ }: CodeBlockProps) => {
137
137
  const isMultiTab = content instanceof Array;
138
138
  const defaultTab = isMultiTab
139
139
  ? content.find((tab) => tab.key === defaultTabKey) ?? content[0]
@@ -232,4 +232,4 @@ export function CodeBlock({
232
232
  )}
233
233
  </CodeBlockWrapper >
234
234
  );
235
- }
235
+ };
@@ -0,0 +1,208 @@
1
+ import clsx from 'clsx';
2
+ import styled from 'styled-components';
3
+
4
+ import { theme } from '../../../design_system/theme.js';
5
+ import { Link, type RegularLinkProps } from '../../link.js';
6
+ import { HeadingShared } from '../../text/heading_shared.js';
7
+ import { CodeBlock, type CodeBlockProps } from './code_block.js';
8
+
9
+ export type CodeBlockTabKey = 'cli' | 'http' | 'javascript' | 'mcp' | 'openapi' | 'python' | 'typescript';
10
+ type CodeBlockTabConfig = {
11
+ label: string;
12
+ language: string;
13
+ src: string;
14
+ };
15
+
16
+ const CODE_BLOCK_TAB_CATALOG: Record<CodeBlockTabKey, CodeBlockTabConfig> = {
17
+ cli: {
18
+ label: 'CLI',
19
+ language: 'bash',
20
+ src: 'https://apify.com/img/icons/code.svg',
21
+ },
22
+ http: {
23
+ label: 'HTTP',
24
+ language: 'bash',
25
+ src: 'https://apify.com/img/icons/http.svg',
26
+ },
27
+ javascript: {
28
+ label: 'JavaScript',
29
+ language: 'javascript',
30
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
31
+ src: 'https://apify.com/img/template-icons/javascript.svg',
32
+ },
33
+ mcp: {
34
+ label: 'MCP',
35
+ language: 'bash',
36
+ src: 'https://apify.com/img/icons/mcp.svg',
37
+ },
38
+ openapi: {
39
+ label: 'OpenAPI',
40
+ language: 'json',
41
+ src: 'https://apify.com/img/icons/openapi.svg',
42
+ },
43
+ python: {
44
+ label: 'Python',
45
+ language: 'python',
46
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
47
+ src: 'https://apify.com/img/template-icons/python.svg',
48
+ },
49
+ typescript: {
50
+ label: 'TypeScript',
51
+ language: 'typescript',
52
+ // TODO: duplicate icon from 'template-icons' to 'icons' folder on the web
53
+ src: 'https://apify.com/img/template-icons/typescript.svg',
54
+ },
55
+ };
56
+
57
+ type SharedCodeBlockProps = Omit<CodeBlockProps, 'content' | 'language' | 'defaultTabKey' | 'onTabChange'>;
58
+ type SharedLinkProps = Pick<RegularLinkProps, 'to' | 'rel' | 'target'>;
59
+
60
+ type CodeBlockTabProps = {
61
+ key: CodeBlockTabKey;
62
+ content: string;
63
+ onClick?: (e: React.MouseEvent) => void;
64
+ } & Partial<SharedLinkProps>;
65
+
66
+ type CodeBlockWithTabsProps = SharedCodeBlockProps & {
67
+ currentTabKey: CodeBlockTabKey;
68
+ tabs: CodeBlockTabProps[];
69
+ };
70
+
71
+ export const CODE_BLOCK_WITH_TABS_CLASSNAMES = {
72
+ WRAPPER: 'CodeBlockWithTabsWrapper',
73
+ TABS: 'CodeBlockWithTabsTabs',
74
+ TAB: 'CodeBlockWithTabsTab',
75
+ CONTENT: 'CodeBlockWithTabsContent',
76
+ };
77
+
78
+ const CodeBlockWithTabsWrapper = styled.div`
79
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.TABS} {
80
+ min-width: 266px;
81
+ height: 72px;
82
+ padding-inline: ${theme.space.space8};
83
+ display: flex;
84
+ gap: ${theme.space.space4};
85
+ overflow-x: auto;
86
+ background-color: ${theme.color.neutral.backgroundSubtle};
87
+ border: 1px solid ${theme.color.neutral.border};
88
+ border-bottom: none;
89
+ border-top-right-radius: ${theme.radius.radius12};
90
+ border-top-left-radius: ${theme.radius.radius12};
91
+
92
+ @media ${theme.device.tablet} {
93
+ height: 88px;
94
+ justify-content: center;
95
+ gap: ${theme.space.space24};
96
+ padding-inline: ${theme.space.space24};
97
+ }
98
+ }
99
+
100
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.TAB} {
101
+ min-width: 64px;
102
+ position: relative;
103
+ padding: ${theme.space.space12} ${theme.space.space8};
104
+ color: ${theme.color.neutral.textMuted};
105
+ display: flex;
106
+ flex-direction: column;
107
+ flex-shrink: 0;
108
+ align-items: center;
109
+ justify-content: flex-end;
110
+ gap: ${theme.space.space4};
111
+ cursor: pointer;
112
+
113
+ img {
114
+ width: 20px;
115
+ height: 20px;
116
+ }
117
+
118
+ [role="tabpanel"] {
119
+ bottom: 0;
120
+ width: 100%;
121
+ height: 2px;
122
+ color: ${theme.color.neutral.text};
123
+ background-color: ${theme.color.primaryBlack.action};
124
+ border-radius: ${theme.radius.radiusFull};
125
+ display: none;
126
+ position: absolute;
127
+ z-index: 2;
128
+ }
129
+
130
+ &.selected {
131
+ [role="tabpanel"] {
132
+ display: block;
133
+ }
134
+ }
135
+
136
+ &:hover {
137
+ color: ${theme.color.neutral.text};
138
+ text-decoration: none;
139
+ }
140
+ }
141
+
142
+ .${CODE_BLOCK_WITH_TABS_CLASSNAMES.CONTENT} {
143
+ max-width: initial;
144
+ border-top-left-radius: 0;
145
+ border-top-right-radius: 0;
146
+ }
147
+ `;
148
+
149
+ export const CodeBlockWithTabs = ({ currentTabKey, tabs, className, hideBashHeader = true, ...props }: CodeBlockWithTabsProps) => {
150
+ // Math.Max(0, ) ensures that the index is not negative, in case the currentTabKey is not found in the tabs array
151
+ const currentTabIndex = Math.max(0, tabs.findIndex((tab) => tab.key === currentTabKey));
152
+ const currentTab = tabs[currentTabIndex];
153
+
154
+ return (
155
+ <CodeBlockWithTabsWrapper className={clsx(CODE_BLOCK_WITH_TABS_CLASSNAMES.WRAPPER, className)}>
156
+ <div className={CODE_BLOCK_WITH_TABS_CLASSNAMES.TABS}>
157
+ {tabs.map((tab) => {
158
+ const { label, src } = CODE_BLOCK_TAB_CATALOG[tab.key];
159
+ const selected = tab.key === currentTab?.key;
160
+
161
+ const children = (
162
+ <>
163
+ <img src={src} alt={label} />
164
+ <HeadingShared type="titleS" as="p">
165
+ {label}
166
+ </HeadingShared>
167
+ <div role="tabpanel" />
168
+ </>
169
+ );
170
+
171
+ // if the tab has a 'to' prop, render a Link component
172
+ if (tab.to) {
173
+ return (
174
+ <Link
175
+ key={tab.key}
176
+ className={clsx(CODE_BLOCK_WITH_TABS_CLASSNAMES.TAB, { selected })}
177
+ to={tab.to}
178
+ rel={tab.rel}
179
+ target={tab.target}
180
+ onClick={tab.onClick}
181
+ >
182
+ {children}
183
+ </Link>
184
+ );
185
+ }
186
+
187
+ return (
188
+ <div
189
+ key={tab.key}
190
+ className={clsx(CODE_BLOCK_WITH_TABS_CLASSNAMES.TAB, { selected })}
191
+ role="button"
192
+ onClick={tab.onClick}
193
+ >
194
+ {children}
195
+ </div>
196
+ );
197
+ })}
198
+ </div>
199
+ <CodeBlock
200
+ content={currentTab?.content ?? ''}
201
+ language={CODE_BLOCK_TAB_CATALOG[currentTab?.key]?.language}
202
+ className={CODE_BLOCK_WITH_TABS_CLASSNAMES.CONTENT}
203
+ hideBashHeader={hideBashHeader}
204
+ {...props}
205
+ />
206
+ </CodeBlockWithTabsWrapper>
207
+ );
208
+ };
@@ -1,3 +1,4 @@
1
1
  export * from './code_block/code_block.js';
2
+ export * from './code_block/code_block_with_tabs.js';
2
3
  export * from './one_line_code/one_line_code.js';
3
4
  export * from './inline_code/inline_code.js';