@redocly/theme 0.32.1 → 0.32.2

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.
@@ -79,7 +79,8 @@ const StyledCard = styled_components_1.default.div.attrs({ 'data-cy': 'Catalog/C
79
79
  font-size: var(--catalog-card-font-size);
80
80
  font-weight: var(--catalog-card-font-weight);
81
81
  background-color: var(--catalog-card-background-color);
82
- border: var(--catalog-card-border-width) var(--catalog-card-border-style) var(--catalog-card-border-color);
82
+ border: var(--catalog-card-border-width) var(--catalog-card-border-style)
83
+ var(--catalog-card-border-color);
83
84
  transition: all 0.2s ease-in-out;
84
85
  border-radius: var(--catalog-card-border-radius);
85
86
  cursor: pointer;
@@ -98,6 +99,7 @@ const StyledCard = styled_components_1.default.div.attrs({ 'data-cy': 'Catalog/C
98
99
  fill: var(--catalog-card-icon-hover-color);
99
100
  }
100
101
  }
102
+ }
101
103
  `;
102
104
  const CardTitle = styled_components_1.default.h4 `
103
105
  color: var(--catalog-card-title-color);
@@ -7,10 +7,14 @@ export interface CodeBlockProps {
7
7
  header?: CodeBlockControlsProps;
8
8
  dataTestId?: string;
9
9
  className?: string;
10
+ tabs?: FileTabs;
10
11
  withLineNumbers?: boolean;
11
12
  startLineNumber?: number;
12
13
  highlightedHtml?: string;
14
+ skipHighlight?: boolean;
13
15
  codeBlockRef?: (instance: HTMLPreElement | null) => void;
16
+ codeBlockMaxHeight?: string;
17
+ hideCodeColors?: boolean;
14
18
  }
15
19
  export interface Sample {
16
20
  lang: string;
@@ -19,6 +23,13 @@ export interface Sample {
19
23
  export type UnstableExternalCodeSample = Sample & {
20
24
  get: (source: ExternalSource) => string;
21
25
  };
26
+ export type FileTabs = {
27
+ files: {
28
+ name: string;
29
+ }[];
30
+ handleTabSwitch: (name: string) => void;
31
+ activeTabName: string;
32
+ };
22
33
  export interface ExternalSource {
23
34
  sample: UnstableExternalCodeSample;
24
35
  exampleName?: string;
@@ -26,4 +37,4 @@ export interface ExternalSource {
26
37
  properties?: any;
27
38
  operation?: any;
28
39
  }
29
- export declare function CodeBlock({ lang, source, externalSource, header, dataTestId, codeBlockRef, highlightedHtml, withLineNumbers, startLineNumber, className, }: CodeBlockProps): JSX.Element;
40
+ export declare function CodeBlock({ lang, source, externalSource, header, dataTestId, codeBlockRef, highlightedHtml, withLineNumbers, startLineNumber, className, codeBlockMaxHeight, tabs, hideCodeColors, }: CodeBlockProps): JSX.Element;
@@ -32,7 +32,7 @@ const styled_components_1 = __importDefault(require("styled-components"));
32
32
  const utils_1 = require("../../utils");
33
33
  const Feedback_1 = require("../../components/Feedback");
34
34
  const CodeBlock_1 = require("../../components/CodeBlock");
35
- function CodeBlock({ lang, source, externalSource, header, dataTestId = 'source-code', codeBlockRef, highlightedHtml, withLineNumbers, startLineNumber, className, }) {
35
+ function CodeBlock({ lang, source, externalSource, header, dataTestId = 'source-code', codeBlockRef, highlightedHtml, withLineNumbers, startLineNumber, className, codeBlockMaxHeight, tabs, hideCodeColors, }) {
36
36
  var _a;
37
37
  const [sourceCode, setSourceCode] = (0, react_1.useState)(source !== null && source !== void 0 ? source : '');
38
38
  const highlightedCode = highlightedHtml || (0, utils_1.highlight)(sourceCode, lang);
@@ -53,13 +53,13 @@ function CodeBlock({ lang, source, externalSource, header, dataTestId = 'source-
53
53
  controls.copy.data = sourceCode;
54
54
  }
55
55
  return (react_1.default.createElement(Wrapper, { "data-component-name": "CodeBlock/CodeBlock", className: className },
56
- react_1.default.createElement(CodeBlock_1.CodeBlockControls, { className: header === null || header === void 0 ? void 0 : header.className, title: header === null || header === void 0 ? void 0 : header.title, controls: controls }),
56
+ react_1.default.createElement(CodeBlock_1.CodeBlockControls, { tabs: tabs, className: header === null || header === void 0 ? void 0 : header.className, title: header === null || header === void 0 ? void 0 : header.title, controls: controls }),
57
57
  react_1.default.createElement(ContainerWrapper, null,
58
58
  react_1.default.createElement(CodeBlock_1.CodeBlockContainer, { ref: codeBlockRef, className: withLineNumbers ? 'line-numbers' : '', dangerouslySetInnerHTML: {
59
59
  __html: withLineNumbers
60
60
  ? (0, utils_1.addLineNumbers)(highlightedCode, startLineNumber)
61
61
  : highlightedCode,
62
- }, "data-cy": dataTestId, withControls: true }),
62
+ }, "data-cy": dataTestId, hideCodeColors: hideCodeColors, maxHeight: codeBlockMaxHeight, withControls: true }),
63
63
  reportDialog.visible && (react_1.default.createElement(Feedback_1.ReportDialog, Object.assign({}, reportDialog.props, { location: sourceCode, lang: lang }))))));
64
64
  }
65
65
  exports.CodeBlock = CodeBlock;
@@ -1,3 +1,5 @@
1
1
  export declare const CodeBlockContainer: import("styled-components").StyledComponent<"pre", any, {
2
2
  withControls?: boolean | undefined;
3
+ maxHeight?: string | undefined;
4
+ hideCodeColors?: boolean | undefined;
3
5
  }, never>;
@@ -32,7 +32,6 @@ exports.CodeBlockContainer = styled_components_1.default.pre.attrs(({ className
32
32
  })) `
33
33
  && {
34
34
  overflow-x: auto;
35
- max-height: var(--code-block-max-height);
36
35
  font-family: var(--code-block-font-family);
37
36
  line-height: var(--code-block-line-height);
38
37
  font-weight: var(--code-block-font-weight);
@@ -42,7 +41,7 @@ exports.CodeBlockContainer = styled_components_1.default.pre.attrs(({ className
42
41
  color: var(--code-block-text-color);
43
42
  font-size: var(--code-block-font-size);
44
43
  white-space: var(--code-wrap, pre);
45
- max-height: var(--code-block-max-height, 600px);
44
+ max-height: ${({ maxHeight }) => maxHeight ? maxHeight : 'var(--code-block-max-height, 600px);'};
46
45
  word-break: var(--code-block-word-break, initial);
47
46
 
48
47
  ${(withControls) => withControls
@@ -56,93 +55,106 @@ exports.CodeBlockContainer = styled_components_1.default.pre.attrs(({ className
56
55
  border-radius: var(--code-block-border-radius);
57
56
  border: 1px solid var(--code-block-border-color);
58
57
  `}
59
-
60
- /**
61
- * Based on prism-dark.css
62
- */
63
- code[class*='language-'],
64
- pre[class*='language-'] {
65
- text-shadow: 0 -0.1em 0.2em black;
66
- text-align: left;
67
- word-spacing: normal;
68
- word-break: normal;
69
- word-wrap: normal;
70
- line-height: 1.5;
71
-
72
- -moz-tab-size: 4;
73
- -o-tab-size: 4;
74
- tab-size: 4;
75
-
76
- -webkit-hyphens: none;
77
- -moz-hyphens: none;
78
- -ms-hyphens: none;
79
- hyphens: none;
80
- }
81
-
82
- @media print {
58
+ /**
59
+ * Based on prism-dark.css
60
+ */
83
61
  code[class*='language-'],
84
62
  pre[class*='language-'] {
85
- text-shadow: none;
63
+ text-shadow: 0 -0.1em 0.2em black;
64
+ text-align: left;
65
+ word-spacing: normal;
66
+ word-break: normal;
67
+ word-wrap: normal;
68
+ line-height: 1.5;
69
+
70
+ -moz-tab-size: 4;
71
+ -o-tab-size: 4;
72
+ tab-size: 4;
73
+
74
+ -webkit-hyphens: none;
75
+ -moz-hyphens: none;
76
+ -ms-hyphens: none;
77
+ hyphens: none;
78
+ }
79
+
80
+ @media print {
81
+ code[class*='language-'],
82
+ pre[class*='language-'] {
83
+ text-shadow: none;
84
+ }
85
+ }
86
+
87
+ /* Code blocks */
88
+
89
+ pre[class*='language-'] {
90
+ padding: 1em;
91
+ margin: 0.5em 0;
92
+ overflow: auto;
93
+ }
94
+
95
+ .token.punctuation {
96
+ opacity: 1;
97
+ }
98
+
99
+ .namespace {
100
+ opacity: 0.7;
86
101
  }
87
- }
88
-
89
- /* Code blocks */
90
- pre[class*='language-'] {
91
- padding: 1em;
92
- margin: 0.5em 0;
93
- overflow: auto;
94
- }
95
-
96
- .token.punctuation {
97
- opacity: 1;
98
- }
99
-
100
- .namespace {
101
- opacity: 0.7;
102
- }
103
-
104
- .token.selector,
105
- .token.attr-name,
106
- .token.string,
107
- .token.char,
108
- .token.builtin,
109
- .token.inserted {
110
- & + a,
111
- & + a:visited {
112
- color: var(--code-block-tokens-link-color);
113
- text-decoration: underline;
102
+
103
+ .token.selector,
104
+ .token.attr-name,
105
+ .token.string,
106
+ .token.char,
107
+ .token.builtin,
108
+ .token.inserted {
109
+ & + a,
110
+ & + a:visited {
111
+ color: var(--code-block-tokens-link-color);
112
+ text-decoration: underline;
113
+ }
114
+ }
115
+
116
+ .token.property.string {
117
+ color: var(--code-block-tokens-property-string-color);
114
118
  }
115
- }
116
-
117
- .token.property.string {
118
- color: var(--code-block-tokens-property-string-color);
119
- }
120
-
121
- .token.important,
122
- .token.bold {
123
- font-weight: bold;
124
- }
125
-
126
- .token.italic {
127
- font-style: italic;
128
- }
129
-
130
- .token.entity {
131
- cursor: help;
132
- }
133
-
134
- .code-line {
135
- &:before {
136
- content: attr(data-line-number);
137
- display: inline-block;
138
- min-width: 2em;
139
- padding-right: 0.8em;
140
- text-align: right;
141
- pointer-events: none;
142
- user-select: none;
119
+
120
+ .token.important,
121
+ .token.bold {
122
+ font-weight: bold;
143
123
  }
144
- }
145
124
 
146
- ${(0, utils_1.generateCodeBlockTokens)()}
125
+ .token.italic {
126
+ font-style: italic;
127
+ }
128
+
129
+ .token.entity {
130
+ cursor: help;
131
+ }
132
+
133
+ .code-line {
134
+ &:before {
135
+ content: attr(data-line-number);
136
+ display: inline-block;
137
+ min-width: 2em;
138
+ padding-right: 0.8em;
139
+ text-align: right;
140
+ pointer-events: none;
141
+ user-select: none;
142
+ }
143
+ }
144
+
145
+ .highlighted {
146
+ background: var(--bg-overlay);
147
+ }
148
+
149
+ ${(0, utils_1.generateCodeBlockTokens)()}
150
+
151
+ ${({ hideCodeColors }) => hideCodeColors &&
152
+ (0, styled_components_1.css) `
153
+ .code-line:not(.highlighted),
154
+ .code-line:not(.highlighted) > span,
155
+ .code-line:not(.highlighted) > span > span {
156
+ color: grey;
157
+ }
158
+ `}
147
159
  `;
148
160
  //# sourceMappingURL=CodeBlockContainer.js.map
@@ -1,9 +1,11 @@
1
1
  import React from 'react';
2
+ import type { FileTabs } from '../../components/CodeBlock';
2
3
  export interface CodeBlockControlsProps {
3
4
  children?: React.ReactNode;
4
5
  className?: string;
5
6
  title?: React.ReactNode | string;
6
7
  controls?: ControlItems;
8
+ tabs?: FileTabs;
7
9
  }
8
10
  interface ControlItems {
9
11
  copy?: CopyControlProps;
@@ -29,5 +31,5 @@ interface CopyControlProps extends ControlProps {
29
31
  handleOutside?: boolean;
30
32
  }
31
33
  export type ControlItemType = 'text' | 'icon';
32
- export declare function CodeBlockControls({ children, className, title, controls, }: CodeBlockControlsProps): JSX.Element | null;
34
+ export declare function CodeBlockControls({ children, className, title, controls, tabs, }: CodeBlockControlsProps): JSX.Element | null;
33
35
  export {};
@@ -11,15 +11,27 @@ const CopyButton_1 = require("../../components/CopyButton/CopyButton");
11
11
  const icons_1 = require("../../icons");
12
12
  const hooks_1 = require("../../hooks");
13
13
  const telemetry_1 = require("../../mocks/telemetry");
14
- function CodeBlockControls({ children, className, title, controls, }) {
14
+ const components_1 = require("../../components");
15
+ function CodeBlockControls({ children, className, title, controls, tabs, }) {
15
16
  var _a, _b, _c, _d, _e, _f;
16
17
  const { codeSnippet } = (0, hooks_1.useThemeConfig)();
17
18
  const controlsType = (codeSnippet === null || codeSnippet === void 0 ? void 0 : codeSnippet.elementFormat) || 'icon';
18
19
  const { copy, expand, collapse, select, deselect, report } = controls
19
20
  ? controls
20
21
  : { copy: null, expand: null, collapse: null, select: null, deselect: null, report: null };
22
+ const FileTabs = () => {
23
+ return (react_1.default.createElement(CodeTabsBlock, null, tabs === null || tabs === void 0 ? void 0 : tabs.files.map(({ name }) => {
24
+ const isActive = name === tabs.activeTabName;
25
+ return (react_1.default.createElement(CodeTabButton, { active: isActive, key: name, onClick: () => {
26
+ tabs.handleTabSwitch(name);
27
+ } },
28
+ react_1.default.createElement(icons_1.SmallFileIcon, null),
29
+ name));
30
+ })));
31
+ };
21
32
  const defaultControls = controls ? (react_1.default.createElement(react_1.default.Fragment, null,
22
- react_1.default.createElement(Title, null, title),
33
+ title && react_1.default.createElement(Title, null, title),
34
+ tabs && react_1.default.createElement(FileTabs, null),
23
35
  react_1.default.createElement(ControlsWrapper, null,
24
36
  report && ((_a = report === null || report === void 0 ? void 0 : report.props) === null || _a === void 0 ? void 0 : _a.visible) ? (react_1.default.createElement(CodeBlock_1.CodeBlockControlButton, Object.assign({ "data-cy": "report-button", "data-testid": "report-button", asIcon: controlsType === 'icon', title: (_b = report.props) === null || _b === void 0 ? void 0 : _b.tooltip }, report.props), controlsType === 'icon' ? react_1.default.createElement(icons_1.ReportIcon, null) : ((_c = report.props) === null || _c === void 0 ? void 0 : _c.buttonText) || 'Report')) : null,
25
37
  expand && !((_d = codeSnippet === null || codeSnippet === void 0 ? void 0 : codeSnippet.expand) === null || _d === void 0 ? void 0 : _d.hide) ? (react_1.default.createElement(CodeBlock_1.CodeBlockControlButton, { "data-cy": "expand-all", "data-testid": "expand-all", asIcon: controlsType === 'icon', onClick: expand === null || expand === void 0 ? void 0 : expand.onClick, title: (expand === null || expand === void 0 ? void 0 : expand.tooltipText) || 'Expand all' }, controlsType === 'icon' ? react_1.default.createElement(icons_1.ExpandIcon, null) : (expand === null || expand === void 0 ? void 0 : expand.label) ? expand.label : 'Expand all')) : null,
@@ -60,4 +72,26 @@ const ControlsWrapper = styled_components_1.default.div `
60
72
  opacity: 1;
61
73
  }
62
74
  `;
75
+ const CodeTabsBlock = styled_components_1.default.div `
76
+ background: transparent;
77
+ border-radius: var(--global-border-radius) 0;
78
+ display: flex;
79
+ flex-wrap: wrap;
80
+ margin: 12px 0;
81
+ justify-content: flex-start;
82
+ flex-direction: row;
83
+ padding: 0 12px;
84
+ `;
85
+ const CodeTabButton = (0, styled_components_1.default)(components_1.Button) `
86
+ border: none;
87
+ padding: 8px 12px;
88
+ background: ${({ active }) => (active ? 'var(--color-primary-base)' : 'transparent')};
89
+ color: ${({ active }) => (active ? '#fff' : 'var(--text-base)')};
90
+ font-size: var(--font-size-sm);
91
+
92
+ &:hover {
93
+ background: var(--color-primary-bg-hover);
94
+ border: none;
95
+ }
96
+ `;
63
97
  //# sourceMappingURL=CodeBlockControls.js.map
@@ -3,4 +3,5 @@ interface IconProps {
3
3
  className?: string;
4
4
  }
5
5
  export declare const FileIcon: import("styled-components").StyledComponent<({ className }: IconProps) => React.JSX.Element, any, {}, never>;
6
+ export declare const SmallFileIcon: import("styled-components").StyledComponent<({ className }: IconProps) => React.JSX.Element, any, {}, never>;
6
7
  export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.FileIcon = void 0;
6
+ exports.SmallFileIcon = exports.FileIcon = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const Icon = ({ className }) => (react_1.default.createElement("svg", { "data-component-name": "icons/FileIcon", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className },
@@ -14,4 +14,8 @@ exports.FileIcon = (0, styled_components_1.default)(Icon) `
14
14
  height: 1.3em;
15
15
  vertical-align: middle;
16
16
  `;
17
+ exports.SmallFileIcon = (0, styled_components_1.default)(Icon) `
18
+ width: 1em;
19
+ height: 1em;
20
+ `;
17
21
  //# sourceMappingURL=FileIcon.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -76,7 +76,8 @@ const StyledCard = styled.div.attrs({ 'data-cy': 'Catalog/CatalogCard' })`
76
76
  font-size: var(--catalog-card-font-size);
77
77
  font-weight: var(--catalog-card-font-weight);
78
78
  background-color: var(--catalog-card-background-color);
79
- border: var(--catalog-card-border-width) var(--catalog-card-border-style) var(--catalog-card-border-color);
79
+ border: var(--catalog-card-border-width) var(--catalog-card-border-style)
80
+ var(--catalog-card-border-color);
80
81
  transition: all 0.2s ease-in-out;
81
82
  border-radius: var(--catalog-card-border-radius);
82
83
  cursor: pointer;
@@ -95,6 +96,7 @@ const StyledCard = styled.div.attrs({ 'data-cy': 'Catalog/CatalogCard' })`
95
96
  fill: var(--catalog-card-icon-hover-color);
96
97
  }
97
98
  }
99
+ }
98
100
  `;
99
101
 
100
102
  const CardTitle = styled.h4`
@@ -14,11 +14,14 @@ export interface CodeBlockProps {
14
14
  header?: CodeBlockControlsProps;
15
15
  dataTestId?: string;
16
16
  className?: string;
17
-
17
+ tabs?: FileTabs;
18
18
  withLineNumbers?: boolean;
19
19
  startLineNumber?: number;
20
20
  highlightedHtml?: string;
21
+ skipHighlight?: boolean;
21
22
  codeBlockRef?: (instance: HTMLPreElement | null) => void;
23
+ codeBlockMaxHeight?: string;
24
+ hideCodeColors?: boolean;
22
25
  }
23
26
 
24
27
  export interface Sample {
@@ -30,6 +33,12 @@ export type UnstableExternalCodeSample = Sample & {
30
33
  get: (source: ExternalSource) => string;
31
34
  };
32
35
 
36
+ export type FileTabs = {
37
+ files: { name: string }[];
38
+ handleTabSwitch: (name: string) => void;
39
+ activeTabName: string;
40
+ };
41
+
33
42
  export interface ExternalSource {
34
43
  sample: UnstableExternalCodeSample;
35
44
  exampleName?: string;
@@ -49,6 +58,9 @@ export function CodeBlock({
49
58
  withLineNumbers,
50
59
  startLineNumber,
51
60
  className,
61
+ codeBlockMaxHeight,
62
+ tabs,
63
+ hideCodeColors,
52
64
  }: CodeBlockProps): JSX.Element {
53
65
  const [sourceCode, setSourceCode] = useState<string>(source ?? '');
54
66
 
@@ -78,7 +90,12 @@ export function CodeBlock({
78
90
 
79
91
  return (
80
92
  <Wrapper data-component-name="CodeBlock/CodeBlock" className={className}>
81
- <CodeBlockControls className={header?.className} title={header?.title} controls={controls} />
93
+ <CodeBlockControls
94
+ tabs={tabs}
95
+ className={header?.className}
96
+ title={header?.title}
97
+ controls={controls}
98
+ />
82
99
  <ContainerWrapper>
83
100
  <CodeBlockContainer
84
101
  ref={codeBlockRef}
@@ -89,6 +106,8 @@ export function CodeBlock({
89
106
  : highlightedCode,
90
107
  }}
91
108
  data-cy={dataTestId}
109
+ hideCodeColors={hideCodeColors}
110
+ maxHeight={codeBlockMaxHeight}
92
111
  withControls={true}
93
112
  />
94
113
  {reportDialog.visible && (
@@ -5,10 +5,9 @@ import { generateCodeBlockTokens } from '@theme/utils';
5
5
  export const CodeBlockContainer = styled.pre.attrs<{ className?: string }>(({ className }) => ({
6
6
  'data-component-name': 'CodeBlock/CodeBlockContainer',
7
7
  className,
8
- }))<{ withControls?: boolean }>`
8
+ }))<{ withControls?: boolean; maxHeight?: string; hideCodeColors?: boolean }>`
9
9
  && {
10
10
  overflow-x: auto;
11
- max-height: var(--code-block-max-height);
12
11
  font-family: var(--code-block-font-family);
13
12
  line-height: var(--code-block-line-height);
14
13
  font-weight: var(--code-block-font-weight);
@@ -18,7 +17,8 @@ export const CodeBlockContainer = styled.pre.attrs<{ className?: string }>(({ cl
18
17
  color: var(--code-block-text-color);
19
18
  font-size: var(--code-block-font-size);
20
19
  white-space: var(--code-wrap, pre);
21
- max-height: var(--code-block-max-height, 600px);
20
+ max-height: ${({ maxHeight }) =>
21
+ maxHeight ? maxHeight : 'var(--code-block-max-height, 600px);'};
22
22
  word-break: var(--code-block-word-break, initial);
23
23
 
24
24
  ${(withControls) =>
@@ -33,92 +33,106 @@ export const CodeBlockContainer = styled.pre.attrs<{ className?: string }>(({ cl
33
33
  border-radius: var(--code-block-border-radius);
34
34
  border: 1px solid var(--code-block-border-color);
35
35
  `}
36
-
37
- /**
38
- * Based on prism-dark.css
39
- */
40
- code[class*='language-'],
41
- pre[class*='language-'] {
42
- text-shadow: 0 -0.1em 0.2em black;
43
- text-align: left;
44
- word-spacing: normal;
45
- word-break: normal;
46
- word-wrap: normal;
47
- line-height: 1.5;
48
-
49
- -moz-tab-size: 4;
50
- -o-tab-size: 4;
51
- tab-size: 4;
52
-
53
- -webkit-hyphens: none;
54
- -moz-hyphens: none;
55
- -ms-hyphens: none;
56
- hyphens: none;
57
- }
58
-
59
- @media print {
36
+ /**
37
+ * Based on prism-dark.css
38
+ */
60
39
  code[class*='language-'],
61
40
  pre[class*='language-'] {
62
- text-shadow: none;
41
+ text-shadow: 0 -0.1em 0.2em black;
42
+ text-align: left;
43
+ word-spacing: normal;
44
+ word-break: normal;
45
+ word-wrap: normal;
46
+ line-height: 1.5;
47
+
48
+ -moz-tab-size: 4;
49
+ -o-tab-size: 4;
50
+ tab-size: 4;
51
+
52
+ -webkit-hyphens: none;
53
+ -moz-hyphens: none;
54
+ -ms-hyphens: none;
55
+ hyphens: none;
56
+ }
57
+
58
+ @media print {
59
+ code[class*='language-'],
60
+ pre[class*='language-'] {
61
+ text-shadow: none;
62
+ }
63
+ }
64
+
65
+ /* Code blocks */
66
+
67
+ pre[class*='language-'] {
68
+ padding: 1em;
69
+ margin: 0.5em 0;
70
+ overflow: auto;
71
+ }
72
+
73
+ .token.punctuation {
74
+ opacity: 1;
63
75
  }
64
- }
65
-
66
- /* Code blocks */
67
- pre[class*='language-'] {
68
- padding: 1em;
69
- margin: 0.5em 0;
70
- overflow: auto;
71
- }
72
-
73
- .token.punctuation {
74
- opacity: 1;
75
- }
76
-
77
- .namespace {
78
- opacity: 0.7;
79
- }
80
-
81
- .token.selector,
82
- .token.attr-name,
83
- .token.string,
84
- .token.char,
85
- .token.builtin,
86
- .token.inserted {
87
- & + a,
88
- & + a:visited {
89
- color: var(--code-block-tokens-link-color);
90
- text-decoration: underline;
76
+
77
+ .namespace {
78
+ opacity: 0.7;
79
+ }
80
+
81
+ .token.selector,
82
+ .token.attr-name,
83
+ .token.string,
84
+ .token.char,
85
+ .token.builtin,
86
+ .token.inserted {
87
+ & + a,
88
+ & + a:visited {
89
+ color: var(--code-block-tokens-link-color);
90
+ text-decoration: underline;
91
+ }
91
92
  }
92
- }
93
-
94
- .token.property.string {
95
- color: var(--code-block-tokens-property-string-color);
96
- }
97
-
98
- .token.important,
99
- .token.bold {
100
- font-weight: bold;
101
- }
102
-
103
- .token.italic {
104
- font-style: italic;
105
- }
106
-
107
- .token.entity {
108
- cursor: help;
109
- }
110
-
111
- .code-line {
112
- &:before {
113
- content: attr(data-line-number);
114
- display: inline-block;
115
- min-width: 2em;
116
- padding-right: 0.8em;
117
- text-align: right;
118
- pointer-events: none;
119
- user-select: none;
93
+
94
+ .token.property.string {
95
+ color: var(--code-block-tokens-property-string-color);
96
+ }
97
+
98
+ .token.important,
99
+ .token.bold {
100
+ font-weight: bold;
101
+ }
102
+
103
+ .token.italic {
104
+ font-style: italic;
105
+ }
106
+
107
+ .token.entity {
108
+ cursor: help;
109
+ }
110
+
111
+ .code-line {
112
+ &:before {
113
+ content: attr(data-line-number);
114
+ display: inline-block;
115
+ min-width: 2em;
116
+ padding-right: 0.8em;
117
+ text-align: right;
118
+ pointer-events: none;
119
+ user-select: none;
120
+ }
121
+ }
122
+
123
+ .highlighted {
124
+ background: var(--bg-overlay);
120
125
  }
121
- }
122
126
 
123
- ${generateCodeBlockTokens()}
127
+ ${generateCodeBlockTokens()}
128
+
129
+ ${({ hideCodeColors }) =>
130
+ hideCodeColors &&
131
+ css`
132
+ .code-line:not(.highlighted),
133
+ .code-line:not(.highlighted) > span,
134
+ .code-line:not(.highlighted) > span > span {
135
+ color: grey;
136
+ }
137
+ `}
124
138
  `;
@@ -3,15 +3,25 @@ import styled from 'styled-components';
3
3
 
4
4
  import { CodeBlockControlButton } from '@theme/components/CodeBlock';
5
5
  import { CopyButton } from '@theme/components/CopyButton/CopyButton';
6
- import { CollapseIcon, DeselectIcon, ExpandIcon, ReportIcon, SelectIcon } from '@theme/icons';
6
+ import {
7
+ CollapseIcon,
8
+ DeselectIcon,
9
+ ExpandIcon,
10
+ ReportIcon,
11
+ SelectIcon,
12
+ SmallFileIcon,
13
+ } from '@theme/icons';
7
14
  import { useThemeConfig } from '@theme/hooks';
8
15
  import { telemetry } from '@portal/telemetry';
16
+ import { Button } from '@theme/components';
17
+ import type { FileTabs } from '@theme/components/CodeBlock';
9
18
 
10
19
  export interface CodeBlockControlsProps {
11
20
  children?: React.ReactNode;
12
21
  className?: string;
13
22
  title?: React.ReactNode | string;
14
23
  controls?: ControlItems;
24
+ tabs?: FileTabs;
15
25
  }
16
26
 
17
27
  interface ControlItems {
@@ -47,6 +57,7 @@ export function CodeBlockControls({
47
57
  className,
48
58
  title,
49
59
  controls,
60
+ tabs,
50
61
  }: CodeBlockControlsProps): JSX.Element | null {
51
62
  const { codeSnippet } = useThemeConfig();
52
63
  const controlsType = (codeSnippet?.elementFormat as ControlItemType) || 'icon';
@@ -54,9 +65,32 @@ export function CodeBlockControls({
54
65
  ? controls
55
66
  : { copy: null, expand: null, collapse: null, select: null, deselect: null, report: null };
56
67
 
68
+ const FileTabs = () => {
69
+ return (
70
+ <CodeTabsBlock>
71
+ {tabs?.files.map(({ name }) => {
72
+ const isActive = name === tabs.activeTabName;
73
+ return (
74
+ <CodeTabButton
75
+ active={isActive}
76
+ key={name}
77
+ onClick={() => {
78
+ tabs.handleTabSwitch(name);
79
+ }}
80
+ >
81
+ <SmallFileIcon />
82
+ {name}
83
+ </CodeTabButton>
84
+ );
85
+ })}
86
+ </CodeTabsBlock>
87
+ );
88
+ };
89
+
57
90
  const defaultControls = controls ? (
58
91
  <>
59
- <Title>{title}</Title>
92
+ {title && <Title>{title}</Title>}
93
+ {tabs && <FileTabs />}
60
94
  <ControlsWrapper>
61
95
  {report && report?.props?.visible ? (
62
96
  <CodeBlockControlButton
@@ -186,3 +220,27 @@ const ControlsWrapper = styled.div`
186
220
  opacity: 1;
187
221
  }
188
222
  `;
223
+
224
+ const CodeTabsBlock = styled.div`
225
+ background: transparent;
226
+ border-radius: var(--global-border-radius) 0;
227
+ display: flex;
228
+ flex-wrap: wrap;
229
+ margin: 12px 0;
230
+ justify-content: flex-start;
231
+ flex-direction: row;
232
+ padding: 0 12px;
233
+ `;
234
+
235
+ const CodeTabButton = styled(Button)<{ active: boolean }>`
236
+ border: none;
237
+ padding: 8px 12px;
238
+ background: ${({ active }) => (active ? 'var(--color-primary-base)' : 'transparent')};
239
+ color: ${({ active }) => (active ? '#fff' : 'var(--text-base)')};
240
+ font-size: var(--font-size-sm);
241
+
242
+ &:hover {
243
+ background: var(--color-primary-bg-hover);
244
+ border: none;
245
+ }
246
+ `;
@@ -26,3 +26,8 @@ export const FileIcon = styled(Icon)`
26
26
  height: 1.3em;
27
27
  vertical-align: middle;
28
28
  `;
29
+
30
+ export const SmallFileIcon = styled(Icon)`
31
+ width: 1em;
32
+ height: 1em;
33
+ `;