@redocly/theme 0.16.0 → 0.17.1

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 (117) hide show
  1. package/lib/components/CodeBlock/CodeBlock.d.ts +29 -1
  2. package/lib/components/CodeBlock/CodeBlock.js +56 -94
  3. package/lib/components/CodeBlock/CodeBlockContainer.d.ts +3 -0
  4. package/lib/components/CodeBlock/CodeBlockContainer.js +120 -0
  5. package/lib/components/CodeBlock/CodeBlockControlButton.d.ts +5 -0
  6. package/lib/components/CodeBlock/CodeBlockControlButton.js +63 -0
  7. package/lib/components/CodeBlock/CodeBlockControls.d.ts +33 -0
  8. package/lib/components/CodeBlock/CodeBlockControls.js +56 -0
  9. package/lib/components/CodeBlock/index.d.ts +3 -0
  10. package/lib/components/CodeBlock/index.js +3 -0
  11. package/lib/components/CopyButton/CopyButton.d.ts +12 -3
  12. package/lib/components/CopyButton/CopyButton.js +30 -26
  13. package/lib/components/CopyButton/index.d.ts +0 -1
  14. package/lib/components/CopyButton/index.js +0 -1
  15. package/lib/components/Feedback/useReportDialog.d.ts +12 -6
  16. package/lib/components/Feedback/useReportDialog.js +13 -5
  17. package/lib/components/JsonViewer/JsonViewer.d.ts +0 -1
  18. package/lib/components/JsonViewer/JsonViewer.js +100 -119
  19. package/lib/components/Markdown/MarkdownWrapper.d.ts +5 -1
  20. package/lib/components/Markdown/MarkdownWrapper.js +59 -53
  21. package/lib/components/Menu/MobileMenu.js +2 -2
  22. package/lib/components/Menu/MobileMenuGroup.js +1 -1
  23. package/lib/components/Panel/PanelHeader.js +0 -1
  24. package/lib/components/Tooltip/Tooltip.js +2 -0
  25. package/lib/components/index.d.ts +0 -3
  26. package/lib/components/index.js +0 -3
  27. package/lib/config.d.ts +526 -24
  28. package/lib/config.js +150 -4
  29. package/lib/globalStyle.js +50 -7
  30. package/lib/icons/ArrowIcon/ArrowIcon.d.ts +3 -3
  31. package/lib/icons/ArrowIcon/ArrowIcon.js +33 -6
  32. package/lib/icons/CollapseIcon/CollapseIcon.d.ts +7 -0
  33. package/lib/icons/CollapseIcon/CollapseIcon.js +22 -0
  34. package/lib/icons/CollapseIcon/index.d.ts +1 -0
  35. package/lib/{components/SourceCode → icons/CollapseIcon}/index.js +1 -1
  36. package/lib/icons/CopyIcon/CopyIcon.d.ts +7 -0
  37. package/lib/icons/CopyIcon/CopyIcon.js +17 -0
  38. package/lib/icons/CopyIcon/index.d.ts +1 -0
  39. package/lib/{components/CodeSample → icons/CopyIcon}/index.js +1 -1
  40. package/lib/icons/DeselectIcon/DeselectIcon.d.ts +7 -0
  41. package/lib/icons/DeselectIcon/DeselectIcon.js +19 -0
  42. package/lib/icons/DeselectIcon/index.d.ts +1 -0
  43. package/lib/{components/SamplesPanelControls → icons/DeselectIcon}/index.js +1 -1
  44. package/lib/icons/ExpandIcon/ExpandIcon.d.ts +5 -6
  45. package/lib/icons/ExpandIcon/ExpandIcon.js +10 -19
  46. package/lib/icons/FileIcon/FileIcon.d.ts +7 -0
  47. package/lib/icons/FileIcon/FileIcon.js +17 -0
  48. package/lib/icons/FileIcon/index.d.ts +1 -0
  49. package/lib/icons/FileIcon/index.js +18 -0
  50. package/lib/icons/ReportIcon/ReportIcon.d.ts +7 -0
  51. package/lib/icons/ReportIcon/ReportIcon.js +19 -0
  52. package/lib/icons/ReportIcon/index.d.ts +1 -0
  53. package/lib/icons/ReportIcon/index.js +18 -0
  54. package/lib/icons/SelectIcon/SelectIcon.d.ts +7 -0
  55. package/lib/icons/SelectIcon/SelectIcon.js +19 -0
  56. package/lib/icons/SelectIcon/index.d.ts +1 -0
  57. package/lib/icons/SelectIcon/index.js +18 -0
  58. package/lib/icons/index.d.ts +7 -1
  59. package/lib/icons/index.js +7 -1
  60. package/lib/layouts/Forbidden.js +2 -2
  61. package/lib/ui/darkColors.js +4 -4
  62. package/lib/utils/highlight.d.ts +1 -0
  63. package/lib/utils/highlight.js +1 -0
  64. package/package.json +2 -2
  65. package/src/components/CodeBlock/CodeBlock.tsx +100 -0
  66. package/src/components/CodeBlock/{CodeBlock.ts → CodeBlockContainer.tsx} +23 -6
  67. package/src/components/CodeBlock/CodeBlockControlButton.tsx +38 -0
  68. package/src/components/CodeBlock/CodeBlockControls.tsx +182 -0
  69. package/src/components/CodeBlock/index.ts +3 -0
  70. package/src/components/CopyButton/CopyButton.tsx +71 -19
  71. package/src/components/CopyButton/index.ts +0 -1
  72. package/src/components/Feedback/useReportDialog.ts +24 -14
  73. package/src/components/JsonViewer/JsonViewer.tsx +112 -142
  74. package/src/components/Markdown/MarkdownWrapper.tsx +65 -54
  75. package/src/components/Menu/MobileMenu.tsx +3 -3
  76. package/src/components/Menu/MobileMenuGroup.tsx +4 -2
  77. package/src/components/Panel/PanelHeader.ts +0 -1
  78. package/src/components/Tooltip/Tooltip.tsx +2 -0
  79. package/src/components/index.ts +0 -3
  80. package/src/config.ts +168 -8
  81. package/src/globalStyle.ts +50 -7
  82. package/src/icons/ArrowIcon/ArrowIcon.tsx +37 -14
  83. package/src/icons/CollapseIcon/CollapseIcon.tsx +40 -0
  84. package/src/icons/CollapseIcon/index.tsx +1 -0
  85. package/src/icons/CopyIcon/CopyIcon.tsx +26 -0
  86. package/src/icons/CopyIcon/index.ts +1 -0
  87. package/src/icons/DeselectIcon/DeselectIcon.tsx +28 -0
  88. package/src/icons/DeselectIcon/index.ts +1 -0
  89. package/src/icons/ExpandIcon/ExpandIcon.tsx +28 -34
  90. package/src/icons/FileIcon/FileIcon.tsx +29 -0
  91. package/src/icons/FileIcon/index.ts +1 -0
  92. package/src/icons/ReportIcon/ReportIcon.tsx +36 -0
  93. package/src/icons/ReportIcon/index.ts +1 -0
  94. package/src/icons/SelectIcon/SelectIcon.tsx +31 -0
  95. package/src/icons/SelectIcon/index.ts +1 -0
  96. package/src/icons/index.ts +7 -1
  97. package/src/layouts/Forbidden.tsx +1 -1
  98. package/src/ui/darkColors.tsx +4 -4
  99. package/src/utils/highlight.ts +1 -0
  100. package/lib/components/CodeSample/CodeSample.d.ts +0 -10
  101. package/lib/components/CodeSample/CodeSample.js +0 -226
  102. package/lib/components/CodeSample/index.d.ts +0 -1
  103. package/lib/components/CopyButton/CopyButtonWrapper.d.ts +0 -11
  104. package/lib/components/CopyButton/CopyButtonWrapper.js +0 -53
  105. package/lib/components/SamplesPanelControls/SamplesPanelControls.d.ts +0 -4
  106. package/lib/components/SamplesPanelControls/SamplesPanelControls.js +0 -76
  107. package/lib/components/SamplesPanelControls/index.d.ts +0 -1
  108. package/lib/components/SourceCode/SourceCode.d.ts +0 -33
  109. package/lib/components/SourceCode/SourceCode.js +0 -60
  110. package/lib/components/SourceCode/index.d.ts +0 -1
  111. package/src/components/CodeSample/CodeSample.tsx +0 -257
  112. package/src/components/CodeSample/index.ts +0 -1
  113. package/src/components/CopyButton/CopyButtonWrapper.tsx +0 -55
  114. package/src/components/SamplesPanelControls/SamplesPanelControls.ts +0 -76
  115. package/src/components/SamplesPanelControls/index.ts +0 -1
  116. package/src/components/SourceCode/SourceCode.tsx +0 -128
  117. package/src/components/SourceCode/index.ts +0 -1
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../../icons/ReportIcon/ReportIcon"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ /// <reference types="react" />
2
+ interface IconProps {
3
+ className?: string;
4
+ color?: string;
5
+ }
6
+ export declare const SelectIcon: import("styled-components").StyledComponent<({ className, color }: IconProps) => JSX.Element, any, {}, never>;
7
+ export {};
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SelectIcon = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const Icon = ({ className, color = 'white' }) => (react_1.default.createElement("svg", { "data-component-name": "icons/SelectIcon", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: className },
10
+ react_1.default.createElement("path", { d: "M10.5 3.64746H1.5V4.64746H10.5V3.64746Z", fill: color }),
11
+ react_1.default.createElement("path", { d: "M10.5 6.64746H1.5V7.64746H10.5V6.64746Z", fill: color }),
12
+ react_1.default.createElement("path", { d: "M7.5 9.64746H1.5V10.6475H7.5V9.64746Z", fill: color }),
13
+ react_1.default.createElement("path", { d: "M10.5 10.9425L9.205 9.64746L8.5 10.3525L10.5 12.3525L14.5 8.35246L13.795 7.64746L10.5 10.9425Z", fill: color })));
14
+ exports.SelectIcon = (0, styled_components_1.default)(Icon) `
15
+ width: 1.3em;
16
+ height: 1.3em;
17
+ vertical-align: middle;
18
+ `;
19
+ //# sourceMappingURL=SelectIcon.js.map
@@ -0,0 +1 @@
1
+ export * from '../../icons/SelectIcon/SelectIcon';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("../../icons/SelectIcon/SelectIcon"), exports);
18
+ //# sourceMappingURL=index.js.map
@@ -5,7 +5,13 @@ export * from '../icons/ColorModeIcon';
5
5
  export * from '../icons/AnchorIcon';
6
6
  export * from '../icons/ExternalIcon';
7
7
  export * from '../icons/SpinnerIcon';
8
+ export * from '../icons/CollapseIcon';
9
+ export * from '../icons/CopyIcon';
10
+ export * from '../icons/DeselectIcon';
11
+ export * from '../icons/ExpandIcon';
12
+ export * from '../icons/FileIcon';
13
+ export * from '../icons/ReportIcon';
14
+ export * from '../icons/SelectIcon';
8
15
  export * from '../icons/BurgerIcon';
9
16
  export * from '../icons/CloseIcon';
10
17
  export * from '../icons/LogoutIcon';
11
- export * from '../icons/ExpandIcon';
@@ -21,8 +21,14 @@ __exportStar(require("../icons/ColorModeIcon"), exports);
21
21
  __exportStar(require("../icons/AnchorIcon"), exports);
22
22
  __exportStar(require("../icons/ExternalIcon"), exports);
23
23
  __exportStar(require("../icons/SpinnerIcon"), exports);
24
+ __exportStar(require("../icons/CollapseIcon"), exports);
25
+ __exportStar(require("../icons/CopyIcon"), exports);
26
+ __exportStar(require("../icons/DeselectIcon"), exports);
27
+ __exportStar(require("../icons/ExpandIcon"), exports);
28
+ __exportStar(require("../icons/FileIcon"), exports);
29
+ __exportStar(require("../icons/ReportIcon"), exports);
30
+ __exportStar(require("../icons/SelectIcon"), exports);
24
31
  __exportStar(require("../icons/BurgerIcon"), exports);
25
32
  __exportStar(require("../icons/CloseIcon"), exports);
26
33
  __exportStar(require("../icons/LogoutIcon"), exports);
27
- __exportStar(require("../icons/ExpandIcon"), exports);
28
34
  //# sourceMappingURL=index.js.map
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Forbidden = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
- const _theme_1 = require("../index.js");
9
+ const Button_1 = require("../components/Button");
10
10
  const hooks_1 = require("../mocks/hooks");
11
11
  function Forbidden() {
12
12
  const { translate } = (0, hooks_1.useTranslate)();
@@ -39,7 +39,7 @@ const Description = styled_components_1.default.div `
39
39
  line-height: var(--page-403-description-line-height);
40
40
  font-weight: var(--page-403-description-font-weight);
41
41
  `;
42
- const HomeButton = (0, styled_components_1.default)(_theme_1.Button) `
42
+ const HomeButton = (0, styled_components_1.default)(Button_1.Button) `
43
43
  margin-top: var(--page-403-button-margin);
44
44
  `;
45
45
  //# sourceMappingURL=Forbidden.js.map
@@ -8,13 +8,13 @@ exports.darkMode = (0, styled_components_1.css) `
8
8
  * @presenter Color
9
9
  */
10
10
  --background-color: #141414;
11
- --footer-background-color: #1F1F1F;
12
- --navbar-background-color: #1F1F1F;
11
+ --footer-background-color: #1f1f1f;
12
+ --navbar-background-color: #1f1f1f;
13
13
  --mobile-menu-background: #141414;
14
14
  --mobile-menu-profile-background: var(--mobile-menu-background);
15
- --mobile-menu-login-button-background: var(--mobile-menu-profile-background); rgba(255, 255, 255, 0.6);
15
+ --mobile-menu-login-button-background: var(--mobile-menu-profile-background);
16
16
  --mobile-menu-item-text-color: rgba(255, 255, 255, 0.6);
17
- --mobile-menu-item-active-color: #1F1F1F;
17
+ --mobile-menu-item-active-color: #1f1f1f;
18
18
  --mobile-menu-item-active-text-color: rgba(255, 255, 255, 0.85);
19
19
  --mobile-menu-control-button-color: #ffffff;
20
20
  --mobile-menu-profile-border-color: #222222;
@@ -8,6 +8,7 @@ import 'prismjs/components/prism-go.js';
8
8
  import 'prismjs/components/prism-http.js';
9
9
  import 'prismjs/components/prism-java.js';
10
10
  import 'prismjs/components/prism-lua.js';
11
+ import 'prismjs/components/prism-markdown.js';
11
12
  import 'prismjs/components/prism-markup-templating.js';
12
13
  import 'prismjs/components/prism-markup.js';
13
14
  import 'prismjs/components/prism-objectivec.js';
@@ -35,6 +35,7 @@ require("prismjs/components/prism-go.js");
35
35
  require("prismjs/components/prism-http.js");
36
36
  require("prismjs/components/prism-java.js");
37
37
  require("prismjs/components/prism-lua.js");
38
+ require("prismjs/components/prism-markdown.js");
38
39
  require("prismjs/components/prism-markup-templating.js"); // dep of php
39
40
  require("prismjs/components/prism-markup.js"); // xml
40
41
  require("prismjs/components/prism-objectivec.js");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.16.0",
4
- "description": "Shared UI components library",
3
+ "version": "0.17.1",
4
+ "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
7
7
  "redocly"
@@ -0,0 +1,100 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { highlight, addLineNumbers } from '@theme/utils';
5
+ import type { ReportDialogProps } from '@theme/components/Feedback';
6
+ import { ReportDialog, useReportDialog } from '@theme/components/Feedback';
7
+ import type { CodeBlockControlsProps } from '@theme/components/CodeBlock';
8
+ import { CodeBlockContainer, CodeBlockControls } from '@theme/components/CodeBlock';
9
+
10
+ export interface CodeBlockProps {
11
+ lang?: string;
12
+ source?: string;
13
+ externalSource?: ExternalSource;
14
+ header?: CodeBlockControlsProps;
15
+ dataTestId?: string;
16
+ className?: string;
17
+
18
+ withLineNumbers?: boolean;
19
+ startLineNumber?: number;
20
+ highlightedHtml?: string;
21
+ codeBlockRef?: (instance: HTMLPreElement | null) => void;
22
+ }
23
+
24
+ export interface Sample {
25
+ lang: string;
26
+ label?: string;
27
+ }
28
+
29
+ export type UnstableExternalCodeSample = Sample & {
30
+ get: (source: ExternalSource) => string;
31
+ };
32
+
33
+ export interface ExternalSource {
34
+ sample: UnstableExternalCodeSample;
35
+ exampleName?: string;
36
+ pathParams?: any;
37
+ properties?: any;
38
+ operation?: any;
39
+ }
40
+
41
+ export function CodeBlock({
42
+ lang,
43
+ source,
44
+ externalSource,
45
+ header,
46
+ dataTestId = 'source-code',
47
+ codeBlockRef,
48
+ highlightedHtml,
49
+ withLineNumbers,
50
+ startLineNumber,
51
+ className,
52
+ }: CodeBlockProps): JSX.Element {
53
+ const [sourceCode, setSourceCode] = useState<string>(source ?? '');
54
+
55
+ const highlightedCode = highlightedHtml || highlight(sourceCode, lang);
56
+
57
+ // The same initial value should be returned for ssr and frontend to avoid issues
58
+ // Because we don't have session storage in ssr and can't get the security details there
59
+ // Issue for more details https://github.com/Redocly/reference-docs/issues/888
60
+ useEffect(() => {
61
+ const _source = source || externalSource?.sample?.get?.(externalSource);
62
+ if (_source) {
63
+ setSourceCode(_source);
64
+ }
65
+ }, [source, externalSource]);
66
+
67
+ const { reportDialog, reportButton } = useReportDialog();
68
+ const controls = {
69
+ ...header?.controls,
70
+ report: header?.controls?.report
71
+ ? { ...header.controls.report, props: reportButton.props }
72
+ : { props: reportButton.props },
73
+ };
74
+
75
+ if (controls.copy && !controls.copy.handleOutside) {
76
+ controls.copy.data = sourceCode;
77
+ }
78
+
79
+ return (
80
+ <Wrapper data-component-name="CodeBlock/CodeBlock" className={className}>
81
+ <CodeBlockControls className={header?.className} title={header?.title} controls={controls} />
82
+ <CodeBlockContainer
83
+ ref={codeBlockRef}
84
+ className={withLineNumbers ? 'line-numbers' : ''}
85
+ dangerouslySetInnerHTML={{
86
+ __html: withLineNumbers
87
+ ? addLineNumbers(highlightedCode, startLineNumber)
88
+ : highlightedCode,
89
+ }}
90
+ data-cy={dataTestId}
91
+ withControls={true}
92
+ />
93
+ {reportDialog.visible && (
94
+ <ReportDialog {...(reportDialog.props as ReportDialogProps)} location={sourceCode} />
95
+ )}
96
+ </Wrapper>
97
+ );
98
+ }
99
+
100
+ const Wrapper = styled.div``;
@@ -2,12 +2,30 @@ import styled from 'styled-components';
2
2
 
3
3
  import { generateCodeBlockTokens } from '@theme/utils';
4
4
 
5
- export const CodeBlock = styled.div.attrs<{ className?: string }>(({ className }) => ({
6
- 'data-component-name': 'CodeBlock/CodeBlock',
5
+ export const CodeBlockContainer = styled.pre.attrs<{ className?: string }>(({ className }) => ({
6
+ 'data-component-name': 'CodeBlock/CodeBlockContainer',
7
7
  className,
8
- }))`
9
- max-height: var(--code-block-max-height, 600px);
10
- word-break: var(--code-block-word-break, initial);
8
+ }))<{ withControls?: boolean }>`
9
+ && {
10
+ overflow-x: auto;
11
+ max-height: var(--code-block-max-height);
12
+ font-family: var(--code-block-font-family);
13
+ line-height: var(--code-line-height);
14
+ padding: var(--code-block-padding);
15
+ margin: var(--code-block-margin);
16
+ border-radius: ${(withControls) =>
17
+ withControls
18
+ ? '0 0 var(--code-block-border-radius) var(--code-block-border-radius)'
19
+ : 'var(--code-block-border-radius)'};
20
+ background-color: var(--code-block-background-color);
21
+ color: var(--code-block-text-color);
22
+ font-size: var(--code-font-size);
23
+ white-space: var(--code-wrap, pre);
24
+ max-height: var(--code-block-max-height, 600px);
25
+ word-break: var(--code-block-word-break, initial);
26
+ border: none;
27
+ }
28
+
11
29
  /**
12
30
  * Based on prism-dark.css
13
31
  */
@@ -15,7 +33,6 @@ export const CodeBlock = styled.div.attrs<{ className?: string }>(({ className }
15
33
  pre[class*='language-'] {
16
34
  text-shadow: 0 -0.1em 0.2em black;
17
35
  text-align: left;
18
- white-space: pre;
19
36
  word-spacing: normal;
20
37
  word-break: normal;
21
38
  word-wrap: normal;
@@ -0,0 +1,38 @@
1
+ import styled, { css } from 'styled-components';
2
+
3
+ export const CodeBlockControlButton = styled.button.attrs(() => ({
4
+ 'data-component-name': 'CodeBlockControls/CodeBlockControlButton',
5
+ }))<{ asIcon?: boolean }>`
6
+ border: 0;
7
+ outline: 0;
8
+ border-radius: var(--border-radius);
9
+ height: 20px;
10
+ font-size: 12px;
11
+ line-height: 12px;
12
+ cursor: pointer;
13
+ padding: 1px 6px;
14
+ color: var(--panel-samples-controls-text-color);
15
+
16
+ ${({ asIcon }) =>
17
+ asIcon
18
+ ? css`
19
+ background-color: var(--panel-samples-icon-controls-background-color);
20
+ `
21
+ : css`
22
+ min-width: 90px;
23
+ background-color: var(--panel-samples-text-controls-background-color);
24
+ `}
25
+
26
+ ${({ theme }) => theme.mediaQueries?.small} {
27
+ padding: ${({ asIcon }) => (asIcon ? '2px 10px' : '2px 20px')};
28
+ }
29
+
30
+ :not(:first-child) {
31
+ margin-left: 5px;
32
+ }
33
+
34
+ :hover,
35
+ :focus {
36
+ background-color: var(--panel-samples-controls-hover-background-color);
37
+ }
38
+ `;
@@ -0,0 +1,182 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import { CodeBlockControlButton } from '@theme/components/CodeBlock';
5
+ import { CollapseIcon, DeselectIcon, ExpandIcon, ReportIcon, SelectIcon } from '@theme/icons';
6
+ import { useThemeConfig } from '@theme/hooks';
7
+
8
+ import { CopyButton } from '../CopyButton';
9
+
10
+ export interface CodeBlockControlsProps {
11
+ children?: React.ReactNode;
12
+ className?: string;
13
+ title?: React.ReactNode | string;
14
+ controls?: ControlItems;
15
+ }
16
+
17
+ interface ControlItems {
18
+ copy?: CopyControlProps;
19
+ expand?: ControlProps;
20
+ collapse?: ControlProps;
21
+ report?: ControlProps;
22
+ select?: ControlProps;
23
+ deselect?: ControlProps;
24
+ }
25
+
26
+ interface ControlProps {
27
+ label?: string;
28
+ tooltipText?: string;
29
+ onClick?: () => void;
30
+ props?: Record<string, any>;
31
+ }
32
+
33
+ interface CopyControlProps extends ControlProps {
34
+ data?: string;
35
+ dataSource?: string;
36
+ dataHash?: string;
37
+ toasterPlacement?: 'left' | 'right' | 'top' | 'bottom';
38
+ toasterText?: string;
39
+ toasterDuration?: number;
40
+ handleOutside?: boolean;
41
+ }
42
+
43
+ export type ControlItemType = 'text' | 'icon';
44
+
45
+ export function CodeBlockControls({
46
+ children,
47
+ className,
48
+ title,
49
+ controls,
50
+ }: CodeBlockControlsProps): JSX.Element | null {
51
+ const { codeSnippet } = useThemeConfig();
52
+ const controlsType = (codeSnippet?.controlsStyle as ControlItemType) || 'icon';
53
+ const { copy, expand, collapse, select, deselect, report } = controls
54
+ ? controls
55
+ : { copy: null, expand: null, collapse: null, select: null, deselect: null, report: null };
56
+
57
+ const defaultControls = controls ? (
58
+ <>
59
+ <Title>{title}</Title>
60
+ <ControlsWrapper>
61
+ {copy && !codeSnippet?.copy?.hide ? (
62
+ <CopyButton
63
+ data={copy.data}
64
+ data-source={copy.dataSource}
65
+ data-hash={copy.dataHash}
66
+ type={controlsType}
67
+ toasterPlacement={copy.toasterPlacement}
68
+ toasterDuration={copy.toasterDuration}
69
+ buttonText={copy.label}
70
+ tooltipText={copy.tooltipText}
71
+ onCopyClick={copy?.onClick}
72
+ />
73
+ ) : null}
74
+
75
+ {expand && !codeSnippet?.expand?.hide ? (
76
+ <CodeBlockControlButton
77
+ data-cy="expand-all"
78
+ data-testid="expand-all"
79
+ asIcon={controlsType === 'icon'}
80
+ onClick={expand?.onClick}
81
+ title={expand?.tooltipText || 'Expand all'}
82
+ >
83
+ {controlsType === 'icon' ? <ExpandIcon /> : expand?.label ? expand.label : 'Expand all'}
84
+ </CodeBlockControlButton>
85
+ ) : null}
86
+
87
+ {collapse && !codeSnippet?.collapse?.hide ? (
88
+ <CodeBlockControlButton
89
+ data-cy="collapse-all"
90
+ data-testid="collapse-all"
91
+ asIcon={controlsType === 'icon'}
92
+ onClick={collapse?.onClick}
93
+ title={collapse?.tooltipText || 'Collapse all'}
94
+ >
95
+ {controlsType === 'icon' ? (
96
+ <CollapseIcon />
97
+ ) : collapse?.label ? (
98
+ collapse.label
99
+ ) : (
100
+ 'Collapse all'
101
+ )}
102
+ </CodeBlockControlButton>
103
+ ) : null}
104
+
105
+ {select ? (
106
+ <CodeBlockControlButton
107
+ data-cy="select-all"
108
+ data-testid="select-all"
109
+ asIcon={controlsType === 'icon'}
110
+ onClick={select?.onClick}
111
+ title={select?.tooltipText}
112
+ >
113
+ {controlsType === 'icon' ? <SelectIcon /> : select?.label ? select.label : 'Select all'}
114
+ </CodeBlockControlButton>
115
+ ) : null}
116
+
117
+ {deselect ? (
118
+ <CodeBlockControlButton
119
+ data-cy="clear-all"
120
+ data-testid="clear-all"
121
+ asIcon={controlsType === 'icon'}
122
+ onClick={deselect?.onClick}
123
+ title={deselect?.tooltipText}
124
+ >
125
+ {controlsType === 'icon' ? (
126
+ <DeselectIcon />
127
+ ) : deselect?.label ? (
128
+ deselect.label
129
+ ) : (
130
+ 'Clear all'
131
+ )}
132
+ </CodeBlockControlButton>
133
+ ) : null}
134
+
135
+ {report && report?.props?.visible ? (
136
+ <CodeBlockControlButton
137
+ data-cy="report-button"
138
+ data-testid="report-button"
139
+ asIcon={controlsType === 'icon'}
140
+ title={report.tooltipText}
141
+ {...report.props}
142
+ >
143
+ {controlsType === 'icon' ? <ReportIcon /> : report.props?.buttonText || 'Report'}
144
+ </CodeBlockControlButton>
145
+ ) : null}
146
+ </ControlsWrapper>
147
+ </>
148
+ ) : null;
149
+
150
+ return children || controls ? (
151
+ <ContainerWraper data-component-name="CodeBlock/CodeBlockControls" className={className}>
152
+ {children ? children : defaultControls}
153
+ </ContainerWraper>
154
+ ) : null;
155
+ }
156
+
157
+ const ContainerWraper = styled.div`
158
+ display: flex;
159
+ justify-content: space-between;
160
+ padding: 5px 10px;
161
+ min-height: 30px;
162
+ background-color: var(--panel-samples-controls-background-color);
163
+ border-bottom: var(--panel-samples-controls-border);
164
+ border-radius: var(--border-radius) var(--border-radius) 0 0;
165
+ margin-top: 10px;
166
+ `;
167
+
168
+ const Title = styled.span`
169
+ display: flex;
170
+ align-items: center;
171
+ color: var(--panel-samples-controls-text-color);
172
+ `;
173
+
174
+ const ControlsWrapper = styled.div`
175
+ opacity: 0.7;
176
+ transition: opacity 0.3s ease;
177
+ text-align: right;
178
+
179
+ &:focus-within {
180
+ opacity: 1;
181
+ }
182
+ `;
@@ -1 +1,4 @@
1
1
  export * from '@theme/components/CodeBlock/CodeBlock';
2
+ export * from '@theme/components/CodeBlock/CodeBlockContainer';
3
+ export * from '@theme/components/CodeBlock/CodeBlockControls';
4
+ export * from '@theme/components/CodeBlock/CodeBlockControlButton';
@@ -1,31 +1,83 @@
1
- import React, { useState } from 'react';
2
- import copy from 'copy-to-clipboard';
3
- import { Button } from '@theme';
1
+ import React, { memo } from 'react';
2
+
3
+ import type { ControlItemType } from '@theme/components/CodeBlock';
4
+ import { CodeBlockControlButton } from '@theme/components/CodeBlock';
5
+ import { Tooltip } from '@theme/components/Tooltip';
6
+ import { useControl } from '@theme/hooks';
7
+ import { ClipboardService } from '@theme/utils';
8
+ import type { TooltipProps } from '@theme/components/Tooltip';
9
+ import { CopyIcon } from '@theme/icons';
10
+ import { useTranslate } from '@portal/hooks';
4
11
 
5
12
  export interface CopyButtonProps {
6
- text: string;
13
+ data: unknown;
14
+ type?: ControlItemType;
15
+ toasterPlacement?: TooltipProps['placement'];
16
+ toasterText?: TooltipProps['tip'];
17
+ toasterDuration?: number;
18
+ buttonText?: string;
19
+ tooltipText?: string;
20
+ onCopyClick?: () => void;
7
21
  dataTestId?: string;
8
22
  }
9
23
 
10
- export const CopyButton = ({ text, dataTestId = 'copy-button' }: CopyButtonProps): JSX.Element => {
11
- const [title, setTitle] = useState('Copy');
24
+ function CopyButtonComponent({
25
+ data,
26
+ type = 'icon',
27
+ toasterPlacement = 'top',
28
+ toasterText,
29
+ toasterDuration,
30
+ buttonText,
31
+ tooltipText,
32
+ onCopyClick,
33
+ dataTestId = 'copy-button',
34
+ }: CopyButtonProps): JSX.Element {
35
+ const tooltip = useControl();
36
+ const { translate } = useTranslate();
37
+ const translationKeys = {
38
+ buttonText: 'theme.codeSnippet.copy.buttonText',
39
+ tooltipText: 'theme.codeSnippet.copy.tooltipText',
40
+ toasterText: 'theme.codeSnippet.copy.toasterText',
41
+ };
42
+
43
+ const showTooltip = (duration: number = 1500): void => {
44
+ tooltip.handleOpen();
12
45
 
13
- async function write() {
14
- await copy(text);
46
+ setTimeout(() => {
47
+ tooltip.handleClose();
48
+ }, duration);
49
+ };
15
50
 
16
- setTitle('Copied!');
51
+ const copy = (duration?: number): void => {
52
+ const content = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
53
+ ClipboardService.copyCustom(content);
54
+ showTooltip(duration);
17
55
 
18
- setTimeout(() => setTitle('Copy'), 1500);
19
- }
56
+ onCopyClick?.();
57
+ };
20
58
 
21
59
  return (
22
- <Button
23
- color="secondary"
24
- onClick={write}
25
- data-cy={dataTestId}
26
- data-component-name="CopyButton/CopyButton"
60
+ <Tooltip
61
+ className="copy-button"
62
+ tip={translate(translationKeys.toasterText, toasterText || 'Copied!')}
63
+ isOpen={tooltip.isOpened}
64
+ placement={toasterPlacement}
27
65
  >
28
- {title}
29
- </Button>
66
+ <CodeBlockControlButton
67
+ onClick={() => copy(toasterDuration)}
68
+ data-cy={dataTestId}
69
+ asIcon={type === 'icon'}
70
+ title={translate(translationKeys.tooltipText, tooltipText || 'Copy to clipboard')}
71
+ data-testid={dataTestId}
72
+ >
73
+ {type === 'icon' ? (
74
+ <CopyIcon />
75
+ ) : (
76
+ translate(translationKeys.buttonText, buttonText || 'Copy')
77
+ )}
78
+ </CodeBlockControlButton>
79
+ </Tooltip>
30
80
  );
31
- };
81
+ }
82
+
83
+ export const CopyButton = memo<CopyButtonProps>(CopyButtonComponent);
@@ -1,2 +1 @@
1
- export * from '@theme/components/CopyButton/CopyButtonWrapper';
2
1
  export * from '@theme/components/CopyButton/CopyButton';