@redocly/theme 0.0.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.
- package/LICENSE +1 -0
- package/lib/package.json +91 -0
- package/lib/src/Button/Button.tsx +122 -0
- package/lib/src/Button/index.ts +1 -0
- package/lib/src/CodeBlock/CodeBlock.ts +125 -0
- package/lib/src/CodeBlock/index.ts +1 -0
- package/lib/src/CopyButton/CopyButton.tsx +26 -0
- package/lib/src/CopyButton/CopyButtonWrapper.tsx +52 -0
- package/lib/src/CopyButton/index.ts +2 -0
- package/lib/src/Headings/Headings.ts +23 -0
- package/lib/src/Headings/index.ts +1 -0
- package/lib/src/JsonViewer/JsonViewer.tsx +130 -0
- package/lib/src/JsonViewer/index.ts +1 -0
- package/lib/src/JsonViewer/styled.ts +103 -0
- package/lib/src/Logo/Logo.tsx +23 -0
- package/lib/src/Navbar/Navbar.tsx +60 -0
- package/lib/src/Navbar/NavbarItem.tsx +90 -0
- package/lib/src/Navbar/NavbarMenu.tsx +29 -0
- package/lib/src/Panel/CodePanel.ts +31 -0
- package/lib/src/Panel/ContentPanel.ts +43 -0
- package/lib/src/Panel/DarkHeader.ts +8 -0
- package/lib/src/Panel/Panel.ts +18 -0
- package/lib/src/Panel/PanelBody.ts +30 -0
- package/lib/src/Panel/PanelComponent.tsx +73 -0
- package/lib/src/Panel/PanelHeader.ts +25 -0
- package/lib/src/Panel/PanelHeaderTitle.ts +11 -0
- package/lib/src/Panel/index.ts +7 -0
- package/lib/src/SamplesPanelControls/SamplesPanelControls.ts +70 -0
- package/lib/src/SamplesPanelControls/index.ts +1 -0
- package/lib/src/SidebarLogo/SidebarLogo.tsx +47 -0
- package/lib/src/SidebarLogo/index.ts +1 -0
- package/lib/src/SourceCode/SourceCode.tsx +67 -0
- package/lib/src/SourceCode/index.ts +1 -0
- package/lib/src/Tooltip/Tooltip.tsx +171 -0
- package/lib/src/Tooltip/index.ts +1 -0
- package/lib/src/globalStyle.ts +512 -0
- package/lib/src/hooks/index.ts +3 -0
- package/lib/src/hooks/useControl.ts +20 -0
- package/lib/src/hooks/useMount.ts +8 -0
- package/lib/src/hooks/useUnmount.ts +10 -0
- package/lib/src/icons/ShelfIcon/ShelfIcon.tsx +45 -0
- package/lib/src/icons/ShelfIcon/index.ts +2 -0
- package/lib/src/icons/index.ts +1 -0
- package/lib/src/index.ts +14 -0
- package/lib/src/mocks/Link.tsx +7 -0
- package/lib/src/mocks/utils.ts +3 -0
- package/lib/src/utils/ClipboardService.ts +92 -0
- package/lib/src/utils/css-variables.ts +2 -0
- package/lib/src/utils/highlight.ts +81 -0
- package/lib/src/utils/index.ts +6 -0
- package/lib/src/utils/jsonToHtml.ts +122 -0
- package/lib/src/utils/media-css.ts +16 -0
- package/lib/src/utils/theme-helpers.ts +34 -0
- package/package.json +91 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import { Link } from '@portal/Link';
|
|
5
|
+
|
|
6
|
+
export interface LogoConfig {
|
|
7
|
+
image?: string;
|
|
8
|
+
altText?: string;
|
|
9
|
+
link?: string;
|
|
10
|
+
favicon?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Logo({ logo }: { logo: LogoConfig }): JSX.Element | null {
|
|
14
|
+
const img = <NavLogo src={logo.image} alt={logo.altText} data-component-name="Logo/Logo" />;
|
|
15
|
+
return logo?.image ? logo.link ? <Link to={logo.link}>{img}</Link> : img : null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const NavLogo = styled.img`
|
|
19
|
+
max-width: var(--logo-max-width);
|
|
20
|
+
max-height: var(--logo-max-height);
|
|
21
|
+
height: var(--logo-height);
|
|
22
|
+
margin: var(--logo-margin);
|
|
23
|
+
`;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
// import { mediaQueries } from '@portal/media-css';
|
|
5
|
+
|
|
6
|
+
interface NavbarProps {
|
|
7
|
+
menu: React.ReactNode;
|
|
8
|
+
logo: React.ReactNode;
|
|
9
|
+
search: React.ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export default function Navbar({ menu, logo, search }: NavbarProps): JSX.Element {
|
|
13
|
+
return (
|
|
14
|
+
<NavbarContainer data-component-name="Navbar/Navbar">
|
|
15
|
+
{logo}
|
|
16
|
+
{menu}
|
|
17
|
+
{search}
|
|
18
|
+
</NavbarContainer>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const NavbarContainer = styled.nav`
|
|
23
|
+
height: var(--navbar-height);
|
|
24
|
+
box-sizing: border-box;
|
|
25
|
+
display: flex;
|
|
26
|
+
color: var(--navbar-color-text);
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
flex-shrink: 0;
|
|
30
|
+
|
|
31
|
+
font-size: 0.875rem;
|
|
32
|
+
position: sticky;
|
|
33
|
+
top: 0;
|
|
34
|
+
z-index: 200;
|
|
35
|
+
padding: 1.25rem;
|
|
36
|
+
padding: 10px var(--sidebar-margin-left);
|
|
37
|
+
background: var(--navbar-color-background);
|
|
38
|
+
font-family: var(--font-family-h);
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
// ${mediaQueries.medium} {
|
|
42
|
+
// padding: 10px var(--sidebar-margin-left);
|
|
43
|
+
// }
|
|
44
|
+
|
|
45
|
+
// ${mediaQueries.medium} {
|
|
46
|
+
// font-size: 1rem;
|
|
47
|
+
// }
|
|
48
|
+
|
|
49
|
+
// ${mediaQueries.print} {
|
|
50
|
+
// background: transparent;
|
|
51
|
+
// display: none;
|
|
52
|
+
|
|
53
|
+
// > :not(a, img) {
|
|
54
|
+
// display: none !important;
|
|
55
|
+
// }
|
|
56
|
+
// img {
|
|
57
|
+
// padding: 0;
|
|
58
|
+
// margin: 0;
|
|
59
|
+
// }
|
|
60
|
+
// }
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { useLocation } from 'react-router-dom';
|
|
4
|
+
|
|
5
|
+
import { Link } from '@portal/Link';
|
|
6
|
+
import { withPathPrefix } from '@portal/utils';
|
|
7
|
+
|
|
8
|
+
export enum MenuStyle {
|
|
9
|
+
Drilldown = 'drilldown',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type ResolvedNavLinkItem = {
|
|
13
|
+
type: 'link';
|
|
14
|
+
link: string;
|
|
15
|
+
label: string;
|
|
16
|
+
items?: ResolvedNavItem[];
|
|
17
|
+
external?: boolean;
|
|
18
|
+
version?: string;
|
|
19
|
+
default?: string;
|
|
20
|
+
httpVerb?: string; // TODO: make a separate type of item
|
|
21
|
+
separatorLine?: boolean;
|
|
22
|
+
active?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type ResolvedNavGroupItem = {
|
|
26
|
+
type: 'group';
|
|
27
|
+
link?: string;
|
|
28
|
+
label: string;
|
|
29
|
+
items: ResolvedNavItem[];
|
|
30
|
+
version?: string;
|
|
31
|
+
default?: string;
|
|
32
|
+
menuStyle?: MenuStyle;
|
|
33
|
+
separatorLine?: boolean;
|
|
34
|
+
active?: boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ResolvedNavItem =
|
|
38
|
+
| ResolvedNavLinkItem
|
|
39
|
+
| ResolvedNavGroupItem
|
|
40
|
+
| {
|
|
41
|
+
type: 'separator';
|
|
42
|
+
label?: string;
|
|
43
|
+
separatorLine?: boolean;
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
type: 'error';
|
|
47
|
+
label: string;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
interface NavbarItemProps {
|
|
51
|
+
navItem: ResolvedNavLinkItem;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default function NavbarItem({ navItem }: NavbarItemProps): JSX.Element {
|
|
55
|
+
const { pathname } = useLocation();
|
|
56
|
+
const isActive = pathname === withPathPrefix(navItem.link);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<NavMenuItem active={isActive} data-component-name="Navbar/NavbarItem">
|
|
60
|
+
<NavLink to={navItem.link} active={isActive}>
|
|
61
|
+
<NavLabel>{navItem.label}</NavLabel>
|
|
62
|
+
</NavLink>
|
|
63
|
+
</NavMenuItem>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const NavMenuItem = styled.li<{ active?: boolean }>`
|
|
68
|
+
display: inline-block;
|
|
69
|
+
padding: 20px calc(var(--sidebar-padding-horizontal) * 2);
|
|
70
|
+
text-align: center;
|
|
71
|
+
line-height: 1;
|
|
72
|
+
font-size: var(--navbar-item-font-size);
|
|
73
|
+
margin-left: var(--navbar-item-margin-horizontal);
|
|
74
|
+
margin-right: var(--navbar-item-margin-horizontal);
|
|
75
|
+
border-radius: var(--navbar-item-border-radius);
|
|
76
|
+
font-weight: var(--navbar-item-font-weight);
|
|
77
|
+
background: ${({ active }) => (active ? 'var(--navbar-item-active-background-color)' : 'none')};
|
|
78
|
+
`;
|
|
79
|
+
|
|
80
|
+
const NavLink = styled(Link)`
|
|
81
|
+
color: ${({ active }) =>
|
|
82
|
+
active ? 'var(--navbar-item-active-text-color)' : 'var(--navbar-color-text)'};
|
|
83
|
+
text-decoration: ${({ active }) =>
|
|
84
|
+
active ? 'var(--navbar-item-active-text-decoration)' : 'none'};
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
const NavLabel = styled.span`
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
vertical-align: middle;
|
|
90
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import NavbarItem from '@theme/Navbar/NavbarItem';
|
|
5
|
+
|
|
6
|
+
export default function NavbarMenu({ menuItems }: { menuItems: any[] }): JSX.Element | null {
|
|
7
|
+
if (!menuItems || !menuItems.length) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<NavItemsContainer data-component-name="Navbar/NavbarMenu">
|
|
13
|
+
{menuItems.map((navItem, index) => {
|
|
14
|
+
return <NavbarItem key={index} data-cy={navItem.label} navItem={navItem} />;
|
|
15
|
+
})}
|
|
16
|
+
</NavItemsContainer>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const NavItemsContainer = styled.ul`
|
|
21
|
+
list-style: none;
|
|
22
|
+
margin: 0;
|
|
23
|
+
padding: 0;
|
|
24
|
+
display: block;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
// ${mediaQueries.small} {
|
|
28
|
+
// display: block;
|
|
29
|
+
// }
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
import { ShelfIcon } from '../icons/ShelfIcon';
|
|
4
|
+
|
|
5
|
+
import { Panel } from './Panel';
|
|
6
|
+
import { PanelHeader } from './PanelHeader';
|
|
7
|
+
import { DarkHeader } from './DarkHeader';
|
|
8
|
+
import { PanelBody } from './PanelBody';
|
|
9
|
+
import { PanelHeaderTitle } from './PanelHeaderTitle';
|
|
10
|
+
|
|
11
|
+
export const CodePanel = styled(Panel)`
|
|
12
|
+
> ${PanelHeader} {
|
|
13
|
+
${DarkHeader};
|
|
14
|
+
min-height: 50px;
|
|
15
|
+
${ShelfIcon} {
|
|
16
|
+
fill: var(--color-content-inverse);
|
|
17
|
+
position: relative;
|
|
18
|
+
}
|
|
19
|
+
${PanelHeaderTitle} {
|
|
20
|
+
color: var(--color-content-inverse);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
&& {
|
|
25
|
+
${PanelBody} {
|
|
26
|
+
padding: 10px 20px 20px;
|
|
27
|
+
background-color: var(--samples-panel-background-color);
|
|
28
|
+
color: var(--color-content-inverse);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
`;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
import { ShelfIcon } from '../icons/ShelfIcon';
|
|
4
|
+
|
|
5
|
+
import { PanelHeaderTitle } from './PanelHeaderTitle';
|
|
6
|
+
import { Panel } from './Panel';
|
|
7
|
+
import { PanelBody } from './PanelBody';
|
|
8
|
+
import { PanelHeader } from './PanelHeader';
|
|
9
|
+
|
|
10
|
+
export const ContentPanel = styled(Panel)`
|
|
11
|
+
border: 1px solid var(--global-border-color);
|
|
12
|
+
|
|
13
|
+
font-size: var(--font-size-base);
|
|
14
|
+
font-weight: var(--font-weight-regular);
|
|
15
|
+
line-height: var(--line-height-base);
|
|
16
|
+
|
|
17
|
+
&:not(:last-child) {
|
|
18
|
+
margin-bottom: 20px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
> ${PanelHeader} {
|
|
22
|
+
padding: calc(var(--spacing-unit) * 4);
|
|
23
|
+
font-size: 18px;
|
|
24
|
+
font-weight: var(--font-weight-bold);
|
|
25
|
+
background-color: var(--panels-background-color);
|
|
26
|
+
|
|
27
|
+
${PanelHeaderTitle} {
|
|
28
|
+
color: var(--color-content);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
${ShelfIcon} {
|
|
32
|
+
fill: var(--color-content);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
> ${PanelBody} {
|
|
37
|
+
padding: calc(var(--spacing-unit) * 4);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
> ${PanelHeader} + ${PanelBody} {
|
|
41
|
+
padding-top: 1px; /* to prevent border overflow */
|
|
42
|
+
}
|
|
43
|
+
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
import { PanelBody } from './PanelBody';
|
|
4
|
+
import { PanelComponent } from './PanelComponent';
|
|
5
|
+
import { PanelHeader } from './PanelHeader';
|
|
6
|
+
|
|
7
|
+
export const Panel = styled(PanelComponent)`
|
|
8
|
+
border-radius: var(--panels-border-radius);
|
|
9
|
+
|
|
10
|
+
${PanelHeader} + ${PanelBody} {
|
|
11
|
+
border-top-left-radius: 0;
|
|
12
|
+
border-top-right-radius: 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&:not(:last-child) {
|
|
16
|
+
margin-bottom: 10px;
|
|
17
|
+
}
|
|
18
|
+
`;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import styled, { keyframes, css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
const showPanel = keyframes`
|
|
4
|
+
0% {
|
|
5
|
+
transform: translateY(-10px);
|
|
6
|
+
}
|
|
7
|
+
100% {
|
|
8
|
+
transform: translateY(0);
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const showPanelAnimation = css`
|
|
13
|
+
animation: ${showPanel} 0.2s;
|
|
14
|
+
`;
|
|
15
|
+
|
|
16
|
+
interface PanelBodyProps {
|
|
17
|
+
animate?: boolean;
|
|
18
|
+
hidden?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const PanelBody = styled.div<PanelBodyProps>`
|
|
22
|
+
${({ animate }) => animate && showPanelAnimation};
|
|
23
|
+
${({ hidden }) => hidden && 'visibility: hidden'};
|
|
24
|
+
|
|
25
|
+
border-top-left-radius: var(--panels-border-radius);
|
|
26
|
+
border-top-right-radius: var(--panels-border-radius);
|
|
27
|
+
border-bottom-right-radius: var(--panels-border-radius);
|
|
28
|
+
border-bottom-left-radius: var(--panels-border-radius);
|
|
29
|
+
background-color: var(--panels-background-color);
|
|
30
|
+
`;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
BaseSyntheticEvent,
|
|
3
|
+
PropsWithChildren,
|
|
4
|
+
ReactNode,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import styled from 'styled-components';
|
|
9
|
+
|
|
10
|
+
import { ShelfIcon } from '../icons';
|
|
11
|
+
|
|
12
|
+
import { PanelBody } from './PanelBody';
|
|
13
|
+
import { PanelHeader, PanelHeaderProps } from './PanelHeader';
|
|
14
|
+
import { PanelHeaderTitle } from './PanelHeaderTitle';
|
|
15
|
+
|
|
16
|
+
export interface PanelComponentProps {
|
|
17
|
+
expanded?: boolean;
|
|
18
|
+
className?: string;
|
|
19
|
+
header?: ReactNode | ((props: PanelHeaderProps) => ReactNode);
|
|
20
|
+
onToggle?: (expanded: boolean) => void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function PanelComponent({
|
|
24
|
+
expanded = true,
|
|
25
|
+
header,
|
|
26
|
+
className,
|
|
27
|
+
children,
|
|
28
|
+
onToggle,
|
|
29
|
+
}: PropsWithChildren<PanelComponentProps>): JSX.Element {
|
|
30
|
+
const [isExpanded, setExpanded] = useState(expanded);
|
|
31
|
+
const [animate, setAnimate] = useState(false);
|
|
32
|
+
|
|
33
|
+
const toggle = ({ target }: BaseSyntheticEvent): void => {
|
|
34
|
+
if (target instanceof HTMLAnchorElement) return;
|
|
35
|
+
setAnimate(true);
|
|
36
|
+
setExpanded(!isExpanded);
|
|
37
|
+
onToggle?.(!isExpanded);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
useEffect(() => setExpanded(expanded), [expanded]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div
|
|
44
|
+
className={className}
|
|
45
|
+
data-cy={
|
|
46
|
+
typeof header === 'string' ? ('panel-' + header).replace(/ /g, '-').toLowerCase() : null
|
|
47
|
+
}
|
|
48
|
+
>
|
|
49
|
+
{header &&
|
|
50
|
+
(typeof header === 'function' ? (
|
|
51
|
+
header({ expanded: isExpanded, toggle })
|
|
52
|
+
) : (
|
|
53
|
+
<PanelHeader data-cy="panel-header" onClick={toggle} expanded={isExpanded}>
|
|
54
|
+
<InlineBox>
|
|
55
|
+
<PanelHeaderTitle data-cy="title">{header}</PanelHeaderTitle>
|
|
56
|
+
<ShelfIcon direction={isExpanded ? 'down' : 'right'} />
|
|
57
|
+
</InlineBox>
|
|
58
|
+
</PanelHeader>
|
|
59
|
+
))}
|
|
60
|
+
|
|
61
|
+
{isExpanded && (
|
|
62
|
+
<PanelBody data-cy="panel-body" hidden={!isExpanded} animate={animate}>
|
|
63
|
+
{children}
|
|
64
|
+
</PanelBody>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const InlineBox = styled.div`
|
|
71
|
+
display: inline-flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
`;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BaseSyntheticEvent } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
export interface PanelHeaderProps {
|
|
5
|
+
expanded: boolean;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
toggle?: (e: BaseSyntheticEvent) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const PanelHeader = styled.div<PanelHeaderProps>`
|
|
11
|
+
position: relative;
|
|
12
|
+
z-index: 1;
|
|
13
|
+
display: flex;
|
|
14
|
+
align-items: center;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
white-space: nowrap;
|
|
17
|
+
cursor: pointer;
|
|
18
|
+
|
|
19
|
+
border-top-left-radius: var(--panels-border-radius);
|
|
20
|
+
border-top-right-radius: var(--panels-border-radius);
|
|
21
|
+
border-bottom-right-radius: ${({ expanded }) => (expanded ? '0' : 'var(--panels-border-radius)')};
|
|
22
|
+
border-bottom-left-radius: ${({ expanded }) => (expanded ? '0' : 'var(--panels-border-radius)')};
|
|
23
|
+
|
|
24
|
+
pointer-events: ${({ disabled }) => (disabled ? 'none' : 'auto')};
|
|
25
|
+
`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
export const PanelHeaderTitle = styled.span`
|
|
4
|
+
line-height: 1;
|
|
5
|
+
margin-right: 5px;
|
|
6
|
+
font-weight: 700;
|
|
7
|
+
/* changed from var(--color-content-inverse) to var(--color-content)
|
|
8
|
+
should change several components in openapi/graphql reference-docs to use inverse color
|
|
9
|
+
*/
|
|
10
|
+
color: var(--color-content);
|
|
11
|
+
`;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import styled from 'styled-components';
|
|
2
|
+
|
|
3
|
+
import { CodeBlock } from '../CodeBlock';
|
|
4
|
+
|
|
5
|
+
export const SamplesControlButton = styled.button`
|
|
6
|
+
background-color: var(--samples-panel-controls-background-color);
|
|
7
|
+
border: 0;
|
|
8
|
+
outline: 0;
|
|
9
|
+
border-radius: var(--global-border-radius);
|
|
10
|
+
height: 20px;
|
|
11
|
+
color: var(--color-content-inverse);
|
|
12
|
+
font-size: 12px;
|
|
13
|
+
line-height: 12px;
|
|
14
|
+
cursor: pointer;
|
|
15
|
+
padding: 1px 6px;
|
|
16
|
+
min-width: 90px;
|
|
17
|
+
|
|
18
|
+
${({ theme }) => theme.mediaQueries.small} {
|
|
19
|
+
padding: 2px 20px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:hover,
|
|
23
|
+
:focus {
|
|
24
|
+
background-color: var(--samples-panel-controls-hover-background-color);
|
|
25
|
+
}
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
export const SampleControls = styled.div`
|
|
29
|
+
padding: 10px 0;
|
|
30
|
+
opacity: 0.7;
|
|
31
|
+
transition: opacity 0.3s ease;
|
|
32
|
+
text-align: right;
|
|
33
|
+
|
|
34
|
+
&:focus-within {
|
|
35
|
+
opacity: 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
> div,
|
|
39
|
+
> ${SamplesControlButton} {
|
|
40
|
+
/* can have tooltip wrapper div also */
|
|
41
|
+
margin-top: 5px;
|
|
42
|
+
margin-left: 10px;
|
|
43
|
+
|
|
44
|
+
${({ theme }) => theme.mediaQueries.small} {
|
|
45
|
+
margin-top: 0;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
&:first-child {
|
|
49
|
+
margin-left: 0;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
export const SampleControlsWrap = styled.div`
|
|
55
|
+
&:hover ${SampleControls} {
|
|
56
|
+
opacity: 1;
|
|
57
|
+
}
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export const PreformattedCodeBlock = styled(CodeBlock.withComponent('pre'))`
|
|
61
|
+
overflow-x: auto;
|
|
62
|
+
margin: 0;
|
|
63
|
+
font-family: var(--code-font-family);
|
|
64
|
+
padding: 20px;
|
|
65
|
+
border-radius: var(--global-border-radius);
|
|
66
|
+
background-color: var(--samples-panel-controls-background-color);
|
|
67
|
+
color: var(--color-content-inverse);
|
|
68
|
+
font-size: var(--code-font-size);
|
|
69
|
+
white-space: var(--code-wrap, pre);
|
|
70
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SamplesPanelControls';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
export interface LogoProps {
|
|
5
|
+
imageUrl?: string;
|
|
6
|
+
href?: string;
|
|
7
|
+
altText?: string;
|
|
8
|
+
dataTestId?: string;
|
|
9
|
+
backgroundColor?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function SidebarLogo({
|
|
13
|
+
imageUrl,
|
|
14
|
+
href,
|
|
15
|
+
altText,
|
|
16
|
+
backgroundColor,
|
|
17
|
+
dataTestId,
|
|
18
|
+
}: LogoProps = {}): JSX.Element | null {
|
|
19
|
+
if (!imageUrl) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const logo = <LogoImgEl src={imageUrl} alt={altText || 'logo'} />;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<LogoWrap data-cy={dataTestId} style={{ backgroundColor }}>
|
|
27
|
+
{href ? <Link href={href}>{logo}</Link> : logo}
|
|
28
|
+
</LogoWrap>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const LogoImgEl = styled.img`
|
|
33
|
+
max-height: var(--logo-max-height);
|
|
34
|
+
max-width: var(--logo-max-width);
|
|
35
|
+
padding: var(--logo-padding);
|
|
36
|
+
width: 100%;
|
|
37
|
+
display: block;
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const LogoWrap = styled.div`
|
|
41
|
+
text-align: center;
|
|
42
|
+
line-height: 0;
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const Link = styled.a`
|
|
46
|
+
display: inline-block;
|
|
47
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SidebarLogo';
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { highlight } from '../utils/highlight';
|
|
4
|
+
import { SampleControls, SampleControlsWrap, PreformattedCodeBlock } from '../SamplesPanelControls';
|
|
5
|
+
import { CopyButtonWrapper } from '../CopyButton';
|
|
6
|
+
|
|
7
|
+
export interface SourceCodeProps {
|
|
8
|
+
lang: string;
|
|
9
|
+
source?: string;
|
|
10
|
+
externalSource?: ExternalSource;
|
|
11
|
+
withCopyButton?: boolean;
|
|
12
|
+
dataTestId?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Sample {
|
|
16
|
+
lang: string;
|
|
17
|
+
label?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type UnstableExternalCodeSample = Sample & {
|
|
21
|
+
get: (source: ExternalSource) => string;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export interface ExternalSource {
|
|
25
|
+
sample: UnstableExternalCodeSample;
|
|
26
|
+
exampleName?: string;
|
|
27
|
+
pathParams?: any;
|
|
28
|
+
properties?: any;
|
|
29
|
+
operation?: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function Code({
|
|
33
|
+
source,
|
|
34
|
+
lang,
|
|
35
|
+
dataTestId,
|
|
36
|
+
}: Required<Omit<SourceCodeProps, 'externalSource' | 'withCopyButton'>>): JSX.Element {
|
|
37
|
+
return (
|
|
38
|
+
<PreformattedCodeBlock
|
|
39
|
+
dangerouslySetInnerHTML={{ __html: highlight(source, lang) }}
|
|
40
|
+
data-cy={dataTestId}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function SourceCode({
|
|
46
|
+
lang,
|
|
47
|
+
source,
|
|
48
|
+
externalSource,
|
|
49
|
+
withCopyButton,
|
|
50
|
+
dataTestId = 'source-code',
|
|
51
|
+
}: SourceCodeProps): JSX.Element {
|
|
52
|
+
const _source = source || externalSource?.sample?.get?.(externalSource) || '';
|
|
53
|
+
if (withCopyButton) {
|
|
54
|
+
return (
|
|
55
|
+
<CopyButtonWrapper data={source}>
|
|
56
|
+
{({ renderCopyButton }) => (
|
|
57
|
+
<SampleControlsWrap>
|
|
58
|
+
<SampleControls data-cy="copy-button">{renderCopyButton()}</SampleControls>
|
|
59
|
+
<Code lang={lang} source={_source} dataTestId={dataTestId} />
|
|
60
|
+
</SampleControlsWrap>
|
|
61
|
+
)}
|
|
62
|
+
</CopyButtonWrapper>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return <Code dataTestId={dataTestId} lang={lang} source={_source} />;
|
|
67
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './SourceCode';
|