@redocly/theme 0.6.3 → 0.6.5
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/lib/Button/Button.d.ts +3 -3
- package/lib/Feedback/Rating.d.ts +3 -0
- package/lib/Feedback/Rating.js +69 -0
- package/lib/Feedback/Sentiment.d.ts +3 -0
- package/lib/Feedback/Sentiment.js +55 -0
- package/lib/Feedback/Thumbs.d.ts +7 -0
- package/lib/Feedback/Thumbs.js +79 -0
- package/lib/Feedback/index.d.ts +3 -0
- package/lib/Feedback/index.js +23 -0
- package/lib/Feedback/types.d.ts +26 -0
- package/lib/Feedback/types.js +3 -0
- package/lib/Markdown/CodeSample/CodeSample.d.ts +1 -1
- package/lib/Markdown/Details.d.ts +1 -1
- package/lib/Markdown/MarkdownLayout.d.ts +3 -2
- package/lib/Markdown/MarkdownLayout.js +8 -2
- package/lib/Markdown/Mermaid.d.ts +1 -1
- package/lib/Markdown/Tabs/Tab.d.ts +1 -1
- package/lib/Markdown/Tabs/Tabs.d.ts +1 -1
- package/lib/Navbar/MobileNavbarMenu.d.ts +3 -2
- package/lib/Navbar/MobileNavbarMenu.js +16 -4
- package/lib/Navbar/Navbar.js +6 -3
- package/lib/NavbarLogo/NavbarLogo.d.ts +1 -1
- package/lib/Profile/LoginLink.d.ts +5 -0
- package/lib/Profile/LoginLink.js +30 -0
- package/lib/Profile/Profile.js +8 -1
- package/lib/Profile/UserProfile.d.ts +13 -0
- package/lib/Profile/UserProfile.js +82 -0
- package/lib/Profile/index.d.ts +4 -0
- package/lib/Profile/index.js +5 -1
- package/lib/Search/Autocomplete.js +1 -0
- package/lib/Search/Popover.js +9 -1
- package/lib/Search/Search.js +37 -4
- package/lib/Sidebar/ArrowBack.js +2 -2
- package/lib/Sidebar/SidebarLayout.d.ts +1 -5
- package/lib/Sidebar/SidebarLayout.js +1 -26
- package/lib/SourceCode/SourceCode.d.ts +1 -1
- package/lib/config.d.ts +55 -2
- package/lib/config.js +18 -0
- package/lib/globalStyle.js +6 -2
- package/lib/hooks/useActiveHeading.d.ts +1 -1
- package/lib/hooks/useActiveSectionId.d.ts +1 -1
- package/lib/hooks/useActiveSectionId.js +0 -3
- package/lib/hooks/useControl.d.ts +1 -1
- package/lib/hooks/useNavbarHeight.d.ts +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/mocks/hooks/index.js +6 -0
- package/lib/mocks/types/index.d.ts +1 -1
- package/lib/mocks/types.d.ts +3 -3
- package/lib/types/config.d.ts +1 -1
- package/lib/ui/Box.d.ts +2 -2
- package/lib/ui/Box.js +1 -0
- package/lib/ui/Burger.js +3 -2
- package/lib/ui/Dropdown.d.ts +1 -1
- package/lib/ui/Tiles/ThinTile.d.ts +1 -1
- package/lib/utils/jsonToHtml.d.ts +1 -1
- package/lib/utils/media-css.d.ts +1 -1
- package/package.json +7 -4
- package/src/Feedback/Rating.tsx +79 -0
- package/src/Feedback/Sentiment.tsx +36 -0
- package/src/Feedback/Thumbs.tsx +116 -0
- package/src/Feedback/index.ts +3 -0
- package/src/Feedback/types.ts +29 -0
- package/src/Markdown/MarkdownLayout.tsx +10 -1
- package/src/Navbar/MobileNavbarMenu.tsx +14 -0
- package/src/Navbar/Navbar.tsx +12 -3
- package/src/Profile/LoginLink.tsx +29 -0
- package/src/Profile/Profile.tsx +8 -1
- package/src/Profile/UserProfile.tsx +101 -0
- package/src/Profile/index.ts +4 -0
- package/src/Search/Autocomplete.tsx +1 -0
- package/src/Search/Popover.tsx +9 -1
- package/src/Search/Search.tsx +25 -15
- package/src/Sidebar/ArrowBack.tsx +2 -2
- package/src/Sidebar/SidebarLayout.tsx +1 -38
- package/src/config.ts +19 -0
- package/src/globalStyle.ts +7 -2
- package/src/hooks/useActiveSectionId.ts +0 -2
- package/src/index.ts +1 -0
- package/src/mocks/hooks/index.ts +6 -0
- package/src/settings.yaml +2 -0
- package/src/types/portal/index.d.ts +1 -1
- package/src/ui/Box.tsx +5 -2
- package/src/ui/Burger.tsx +3 -2
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import type { SentimentProps } from '@theme/Feedback';
|
|
5
|
+
|
|
6
|
+
import { ThumbUp, ThumbDown } from './Thumbs';
|
|
7
|
+
|
|
8
|
+
export const Sentiment = ({ settings, onSubmit }: SentimentProps): JSX.Element => {
|
|
9
|
+
const { label } = settings || {};
|
|
10
|
+
return (
|
|
11
|
+
<Wrapper>
|
|
12
|
+
<Label>{label || 'Was this page helpful?'}</Label>
|
|
13
|
+
<Vote onClick={() => onSubmit(1)}>
|
|
14
|
+
<ThumbUp text="Yes" />
|
|
15
|
+
</Vote>
|
|
16
|
+
<Vote onClick={() => onSubmit(-1)}>
|
|
17
|
+
<ThumbDown />
|
|
18
|
+
</Vote>
|
|
19
|
+
</Wrapper>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const Wrapper = styled.div`
|
|
24
|
+
font-family: var(--font-family-base);
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
const Label = styled.h3`
|
|
30
|
+
margin-right: 15px;
|
|
31
|
+
`;
|
|
32
|
+
|
|
33
|
+
const Vote = styled.div`
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
margin: 0 10px;
|
|
36
|
+
`;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
export const ThumbUp = ({ text }: { text?: string }) => (
|
|
5
|
+
<Wrapper style={{ alignItems: 'normal' }}>
|
|
6
|
+
<Icon>
|
|
7
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512">
|
|
8
|
+
<g>
|
|
9
|
+
<g>
|
|
10
|
+
<g>
|
|
11
|
+
<path
|
|
12
|
+
d="M495.736,290.773C509.397,282.317,512,269.397,512,260.796c0-22.4-18.253-47.462-42.667-47.462H349.918
|
|
13
|
+
c-4.284-0.051-25.651-1.51-25.651-25.6c0-4.71-3.814-8.533-8.533-8.533s-8.533,3.823-8.533,8.533
|
|
14
|
+
c0,33.749,27.913,42.667,42.667,42.667h119.467c14.182,0,25.6,16.631,25.6,30.396c0,4.437,0,17.946-26.53,20.855
|
|
15
|
+
c-4.506,0.495-7.834,4.42-7.586,8.951c0.239,4.523,3.985,8.064,8.516,8.064c14.114,0,25.6,11.486,25.6,25.6
|
|
16
|
+
s-11.486,25.6-25.6,25.6h-17.067c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c14.114,0,25.6,11.486,25.6,25.6
|
|
17
|
+
s-11.486,25.6-25.6,25.6h-25.6c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c8.934,0,17.067,8.132,17.067,17.067
|
|
18
|
+
c0,8.61-8.448,17.067-17.067,17.067h-128c-35.627,0-48.444-7.074-63.292-15.258c-12.553-6.921-26.786-14.763-54.963-18.79
|
|
19
|
+
c-4.668-0.674-8.994,2.577-9.66,7.236c-0.666,4.668,2.569,8.994,7.236,9.66c25.105,3.584,37.325,10.325,49.152,16.845
|
|
20
|
+
c15.497,8.542,31.505,17.374,71.526,17.374h128c17.869,0,34.133-16.273,34.133-34.133c0-6.229-1.775-12.134-4.83-17.229
|
|
21
|
+
c21.794-1.877,38.963-20.224,38.963-42.505c0-10.829-4.062-20.736-10.735-28.271C500.42,358.212,512,342.571,512,324.267
|
|
22
|
+
C512,310.699,505.634,298.59,495.736,290.773z"
|
|
23
|
+
/>
|
|
24
|
+
<path
|
|
25
|
+
d="M76.8,443.733c9.412,0,17.067-7.654,17.067-17.067S86.212,409.6,76.8,409.6c-9.412,0-17.067,7.654-17.067,17.067
|
|
26
|
+
S67.388,443.733,76.8,443.733z"
|
|
27
|
+
/>
|
|
28
|
+
<path
|
|
29
|
+
d="M179.2,247.467c25.353,0,57.429-28.297,74.3-45.167c36.634-36.634,36.634-82.167,36.634-151.1
|
|
30
|
+
c0-5.342,3.191-8.533,8.533-8.533c29.508,0,42.667,13.158,42.667,42.667v102.4c0,4.71,3.814,8.533,8.533,8.533
|
|
31
|
+
s8.533-3.823,8.533-8.533v-102.4c0-39.083-20.659-59.733-59.733-59.733c-14.831,0-25.6,10.769-25.6,25.6
|
|
32
|
+
c0,66.978,0,107.401-31.633,139.034C216.661,215.006,192.811,230.4,179.2,230.4c-4.719,0-8.533,3.823-8.533,8.533
|
|
33
|
+
S174.481,247.467,179.2,247.467z"
|
|
34
|
+
/>
|
|
35
|
+
<path
|
|
36
|
+
d="M145.067,213.333H8.533c-4.719,0-8.533,3.823-8.533,8.533v256c0,4.71,3.814,8.533,8.533,8.533h136.533
|
|
37
|
+
c4.719,0,8.533-3.823,8.533-8.533v-256C153.6,217.156,149.786,213.333,145.067,213.333z M136.533,469.333H17.067V230.4h119.467
|
|
38
|
+
V469.333z"
|
|
39
|
+
/>
|
|
40
|
+
</g>
|
|
41
|
+
</g>
|
|
42
|
+
</g>
|
|
43
|
+
</svg>
|
|
44
|
+
</Icon>
|
|
45
|
+
<span>{text || 'Yes'}</span>
|
|
46
|
+
</Wrapper>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export const ThumbDown = ({ text }: { text?: string }) => (
|
|
50
|
+
<Wrapper style={{ alignItems: 'end' }}>
|
|
51
|
+
<Icon>
|
|
52
|
+
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 512 512">
|
|
53
|
+
<g>
|
|
54
|
+
<g>
|
|
55
|
+
<g>
|
|
56
|
+
<path
|
|
57
|
+
d="M76.8,247.467c9.412,0,17.067-7.654,17.067-17.067c0-9.412-7.654-17.067-17.067-17.067
|
|
58
|
+
c-9.412,0-17.067,7.654-17.067,17.067C59.733,239.812,67.388,247.467,76.8,247.467z"
|
|
59
|
+
/>
|
|
60
|
+
<path
|
|
61
|
+
d="M495.736,221.227C505.634,213.41,512,201.301,512,187.733c0-18.295-11.58-33.946-27.802-39.996
|
|
62
|
+
c6.673-7.535,10.735-17.434,10.735-28.271c0-22.281-17.169-40.627-38.963-42.505c3.055-5.094,4.83-10.999,4.83-17.229
|
|
63
|
+
c0-17.86-16.265-34.133-34.133-34.133h-128c-40.021,0-56.03,8.832-71.526,17.374c-11.827,6.519-24.047,13.261-49.152,16.845
|
|
64
|
+
c-4.668,0.666-7.902,4.992-7.236,9.66c0.666,4.659,4.949,7.885,9.66,7.236c28.177-4.028,42.411-11.87,54.963-18.79
|
|
65
|
+
c14.848-8.183,27.665-15.258,63.292-15.258h128c8.619,0,17.067,8.456,17.067,17.067c0,8.934-8.132,17.067-17.067,17.067
|
|
66
|
+
c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533h25.6c14.114,0,25.6,11.486,25.6,25.6s-11.486,25.6-25.6,25.6
|
|
67
|
+
c-4.719,0-8.533,3.823-8.533,8.533c0,4.71,3.814,8.533,8.533,8.533h17.067c14.114,0,25.6,11.486,25.6,25.6
|
|
68
|
+
s-11.486,25.6-25.6,25.6c-4.531,0-8.277,3.541-8.516,8.064c-0.247,4.531,3.081,8.457,7.586,8.951
|
|
69
|
+
c26.53,2.91,26.53,16.418,26.53,20.847c0,13.773-11.418,30.404-25.6,30.404H349.867c-14.763,0-42.667,8.917-42.667,42.667
|
|
70
|
+
c0,4.71,3.814,8.533,8.533,8.533s8.533-3.823,8.533-8.533c0-24.09,21.367-25.549,25.6-25.6h119.467
|
|
71
|
+
c24.414,0,42.667-25.054,42.667-47.471C512,242.603,509.397,229.683,495.736,221.227z"
|
|
72
|
+
/>
|
|
73
|
+
<path
|
|
74
|
+
d="M349.867,315.733c-4.719,0-8.533,3.823-8.533,8.533v102.4c0,29.508-13.158,42.667-42.667,42.667
|
|
75
|
+
c-5.342,0-8.533-3.192-8.533-8.533c0-68.932,0-114.466-36.634-151.1c-16.87-16.87-48.947-45.167-74.3-45.167
|
|
76
|
+
c-4.719,0-8.533,3.823-8.533,8.533s3.814,8.533,8.533,8.533c13.611,0,37.461,15.394,62.234,40.166
|
|
77
|
+
c31.633,31.633,31.633,72.055,31.633,139.034c0,14.831,10.769,25.6,25.6,25.6c39.074,0,59.733-20.651,59.733-59.733v-102.4
|
|
78
|
+
C358.4,319.556,354.586,315.733,349.867,315.733z"
|
|
79
|
+
/>
|
|
80
|
+
<path
|
|
81
|
+
d="M145.067,25.6H8.533C3.814,25.6,0,29.423,0,34.133v256c0,4.71,3.814,8.533,8.533,8.533h136.533
|
|
82
|
+
c4.719,0,8.533-3.823,8.533-8.533v-256C153.6,29.423,149.786,25.6,145.067,25.6z M136.533,281.6H17.067V42.667h119.467V281.6z"
|
|
83
|
+
/>
|
|
84
|
+
</g>
|
|
85
|
+
</g>
|
|
86
|
+
</g>
|
|
87
|
+
</svg>
|
|
88
|
+
</Icon>
|
|
89
|
+
<span>{text || 'No'}</span>
|
|
90
|
+
</Wrapper>
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const Wrapper = styled.div`
|
|
94
|
+
font-family: var(--font-family-base);
|
|
95
|
+
display: flex;
|
|
96
|
+
color: var(--color-primary-500);
|
|
97
|
+
&:hover {
|
|
98
|
+
color: var(--color-primary-600);
|
|
99
|
+
svg {
|
|
100
|
+
> g {
|
|
101
|
+
fill: var(--color-primary-600);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
|
|
107
|
+
const Icon = styled.div`
|
|
108
|
+
width: 18px;
|
|
109
|
+
height: 18px;
|
|
110
|
+
margin-right: 5px;
|
|
111
|
+
> svg {
|
|
112
|
+
> g {
|
|
113
|
+
fill: var(--color-primary-500);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
`;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type RatingProps = {
|
|
2
|
+
onSubmit: (value: number) => void;
|
|
3
|
+
settings?: {
|
|
4
|
+
label?: string;
|
|
5
|
+
max?: number;
|
|
6
|
+
submitText?: string;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type SentimentProps = {
|
|
11
|
+
onSubmit: (value: -1 | 1) => void;
|
|
12
|
+
settings?: {
|
|
13
|
+
label?: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type CommentProps = {
|
|
18
|
+
onSubmit: (value: string) => void;
|
|
19
|
+
settings?: {
|
|
20
|
+
label?: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ProblemProps = {
|
|
25
|
+
onSubmit: (value: string, location: string) => void; // TODO: maybe we don't need location here
|
|
26
|
+
settings?: {
|
|
27
|
+
label?: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -11,6 +11,7 @@ import { useThemeConfig } from '@theme/hooks';
|
|
|
11
11
|
type MarkdownLayoutProps = {
|
|
12
12
|
tableOfContent: React.ReactNode;
|
|
13
13
|
markdownWrapper: React.ReactNode;
|
|
14
|
+
feedback: React.ReactNode;
|
|
14
15
|
editPage?: {
|
|
15
16
|
to: string;
|
|
16
17
|
text: string;
|
|
@@ -23,13 +24,14 @@ type MarkdownLayoutProps = {
|
|
|
23
24
|
export function MarkdownLayout({
|
|
24
25
|
tableOfContent,
|
|
25
26
|
markdownWrapper,
|
|
27
|
+
feedback,
|
|
26
28
|
editPage,
|
|
27
29
|
lastModified,
|
|
28
30
|
}: MarkdownLayoutProps): JSX.Element {
|
|
29
31
|
const { markdown } = useThemeConfig();
|
|
30
32
|
const { editPage: themeEditPage } = markdown || {};
|
|
31
33
|
|
|
32
|
-
const mergedConf = editPage ? { ...
|
|
34
|
+
const mergedConf = editPage ? { ...themeEditPage, ...editPage } : undefined;
|
|
33
35
|
|
|
34
36
|
return (
|
|
35
37
|
<PageWrapper data-component-name="Markdown/MarkdownLayout">
|
|
@@ -41,6 +43,7 @@ export function MarkdownLayout({
|
|
|
41
43
|
)}
|
|
42
44
|
</LayoutTop>
|
|
43
45
|
{markdownWrapper}
|
|
46
|
+
<LayoutBottom>{feedback}</LayoutBottom>
|
|
44
47
|
<PageNavigation />
|
|
45
48
|
</ContainerWrapper>
|
|
46
49
|
{tableOfContent}
|
|
@@ -53,3 +56,9 @@ const LayoutTop = styled.div`
|
|
|
53
56
|
justify-content: space-between;
|
|
54
57
|
flex-flow: row nowrap;
|
|
55
58
|
`;
|
|
59
|
+
|
|
60
|
+
const LayoutBottom = styled(LayoutTop)`
|
|
61
|
+
> * {
|
|
62
|
+
margin: 25px 5px;
|
|
63
|
+
}
|
|
64
|
+
`;
|
|
@@ -10,8 +10,10 @@ import type { ResolvedConfigLinks, ResolvedNavItem } from '@theme/types/portal';
|
|
|
10
10
|
export function MobileNavbarMenu({
|
|
11
11
|
menuItems,
|
|
12
12
|
closeMenu,
|
|
13
|
+
search,
|
|
13
14
|
}: {
|
|
14
15
|
menuItems: ResolvedConfigLinks;
|
|
16
|
+
search: React.ReactNode | undefined;
|
|
15
17
|
closeMenu: () => void;
|
|
16
18
|
}): JSX.Element | null {
|
|
17
19
|
if (isPrimitive(menuItems) || isEmptyArray(menuItems)) {
|
|
@@ -21,6 +23,7 @@ export function MobileNavbarMenu({
|
|
|
21
23
|
return (
|
|
22
24
|
<NavbarItemsWrapper data-component-name="Navbar/MobileNavbarMenu">
|
|
23
25
|
<NavbarItemsContainer>
|
|
26
|
+
<MobileSearchWrapper>{search || null}</MobileSearchWrapper>
|
|
24
27
|
{(menuItems as ResolvedNavItem[]).map((navItem, index) => {
|
|
25
28
|
return (
|
|
26
29
|
<MobileNavbarItem
|
|
@@ -106,3 +109,14 @@ const NavbarItemsContainer = styled.ul`
|
|
|
106
109
|
position: relative;
|
|
107
110
|
}
|
|
108
111
|
`;
|
|
112
|
+
|
|
113
|
+
const MobileSearchWrapper = styled.div`
|
|
114
|
+
padding: var(--navbar-item-padding-horizontal);
|
|
115
|
+
> div {
|
|
116
|
+
display: block;
|
|
117
|
+
width: 100%;
|
|
118
|
+
}
|
|
119
|
+
input {
|
|
120
|
+
width: 100%;
|
|
121
|
+
}
|
|
122
|
+
`;
|
package/src/Navbar/Navbar.tsx
CHANGED
|
@@ -18,9 +18,10 @@ interface NavbarProps {
|
|
|
18
18
|
|
|
19
19
|
export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Element | null {
|
|
20
20
|
const [isOpen, setIsOpen] = useMobileMenu(false);
|
|
21
|
-
const { search: searchSettings, navbar } = useThemeConfig();
|
|
21
|
+
const { search: searchSettings, navbar, userProfile: userProfileSettings } = useThemeConfig();
|
|
22
22
|
const hideSearch =
|
|
23
23
|
searchSettings?.hide || (searchSettings?.placement && searchSettings?.placement !== 'navbar');
|
|
24
|
+
const hideUserProfile = userProfileSettings?.hide;
|
|
24
25
|
|
|
25
26
|
if (navbar?.hide) {
|
|
26
27
|
return null;
|
|
@@ -32,12 +33,18 @@ export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Elemen
|
|
|
32
33
|
return (
|
|
33
34
|
<NavbarContainer data-component-name="Navbar/Navbar">
|
|
34
35
|
<MobileNavbarMenuButton onClick={openMobileMenu} />
|
|
35
|
-
{isOpen &&
|
|
36
|
+
{isOpen && (
|
|
37
|
+
<MobileNavbarMenu
|
|
38
|
+
closeMenu={closeMobileMenu}
|
|
39
|
+
menuItems={menu}
|
|
40
|
+
search={hideSearch ? null : search}
|
|
41
|
+
/>
|
|
42
|
+
)}
|
|
36
43
|
<NavbarRow>
|
|
37
44
|
{logo}
|
|
38
45
|
<NavbarMenu menuItems={menu} />
|
|
39
46
|
{hideSearch ? null : search}
|
|
40
|
-
{profile}
|
|
47
|
+
{hideUserProfile ? null : profile}
|
|
41
48
|
<ColorModeSwitcher />
|
|
42
49
|
</NavbarRow>
|
|
43
50
|
</NavbarContainer>
|
|
@@ -45,6 +52,8 @@ export function Navbar({ menu, logo, search, profile }: NavbarProps): JSX.Elemen
|
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
export const NavbarContainer = styled.nav`
|
|
55
|
+
--text-color: var(--navbar-text-color);
|
|
56
|
+
|
|
48
57
|
height: var(--navbar-height);
|
|
49
58
|
box-sizing: border-box;
|
|
50
59
|
display: flex;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
5
|
+
|
|
6
|
+
export interface LoginLinkProps {
|
|
7
|
+
href: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function LoginLink({ href }: LoginLinkProps): JSX.Element {
|
|
11
|
+
const { userProfile } = useThemeConfig();
|
|
12
|
+
return <StyledLink href={href}>{userProfile?.loginLabel || 'Login'}</StyledLink>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const StyledLink = styled.a.attrs(() => ({
|
|
16
|
+
'data-component-name': 'Profile/LoginLink',
|
|
17
|
+
}))`
|
|
18
|
+
display: inline-block;
|
|
19
|
+
color: var(--navbar-text-color);
|
|
20
|
+
text-decoration: none;
|
|
21
|
+
padding: 0 var(--navbar-item-padding-horizontal);
|
|
22
|
+
text-align: center;
|
|
23
|
+
line-height: 1;
|
|
24
|
+
font-size: var(--navbar-item-font-size);
|
|
25
|
+
font-weight: var(--navbar-item-font-weight);
|
|
26
|
+
&:hover {
|
|
27
|
+
color: var(--navbar-item-hover-text-color);
|
|
28
|
+
}
|
|
29
|
+
`;
|
package/src/Profile/Profile.tsx
CHANGED
|
@@ -44,7 +44,9 @@ function ProfileComponent({ name, imageUrl, onClick, color }: ProfileProps): JSX
|
|
|
44
44
|
|
|
45
45
|
export const Profile = memo<ProfileProps>(ProfileComponent);
|
|
46
46
|
|
|
47
|
-
const ProfileWrapper = styled.div
|
|
47
|
+
const ProfileWrapper = styled.div.attrs(() => ({
|
|
48
|
+
'data-component-name': 'Profile/Profile',
|
|
49
|
+
}))`
|
|
48
50
|
display: flex;
|
|
49
51
|
align-items: center;
|
|
50
52
|
cursor: pointer;
|
|
@@ -54,6 +56,11 @@ const ProfileWrapper = styled.div`
|
|
|
54
56
|
|
|
55
57
|
const StyledUserName = styled.span`
|
|
56
58
|
color: ${({ color }) => color || 'var(--navbar-text-color)'};
|
|
59
|
+
display: none;
|
|
60
|
+
|
|
61
|
+
${({ theme }) => theme.mediaQueries?.medium} {
|
|
62
|
+
display: block;
|
|
63
|
+
}
|
|
57
64
|
`;
|
|
58
65
|
|
|
59
66
|
const AvatarWrapper = styled.div<{ background?: string }>`
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
|
|
4
|
+
import { Link } from '@portal/Link';
|
|
5
|
+
import { Profile } from '@theme/Profile/Profile';
|
|
6
|
+
import { Tooltip } from '@theme/Tooltip/Tooltip';
|
|
7
|
+
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
8
|
+
|
|
9
|
+
export interface UserProfileProps {
|
|
10
|
+
userInfo: {
|
|
11
|
+
isAuthenticated: boolean;
|
|
12
|
+
name: string;
|
|
13
|
+
picture: string;
|
|
14
|
+
logoutDisabled?: boolean;
|
|
15
|
+
};
|
|
16
|
+
handleLogout: (logoutRedirect?: string) => void;
|
|
17
|
+
hasDeveloperOnboarding?: boolean;
|
|
18
|
+
hasApiLogs?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function UserProfile({
|
|
22
|
+
userInfo,
|
|
23
|
+
handleLogout,
|
|
24
|
+
hasDeveloperOnboarding = false,
|
|
25
|
+
hasApiLogs = false,
|
|
26
|
+
}: UserProfileProps): JSX.Element {
|
|
27
|
+
const [isOpened, setIsOpened] = useState<boolean>(false);
|
|
28
|
+
|
|
29
|
+
const { userProfile: userProfileSettings } = useThemeConfig();
|
|
30
|
+
|
|
31
|
+
const logoutRedirect = userProfileSettings?.logoutRedirect || '/';
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<StyledTooltip
|
|
35
|
+
isOpen={isOpened}
|
|
36
|
+
withArrow={false}
|
|
37
|
+
className="copy-button"
|
|
38
|
+
placement="bottom"
|
|
39
|
+
width="100%"
|
|
40
|
+
tip={
|
|
41
|
+
<StyledUl>
|
|
42
|
+
{hasDeveloperOnboarding ? (
|
|
43
|
+
<Link to="/apps">
|
|
44
|
+
<StyledLi>My Apps</StyledLi>
|
|
45
|
+
</Link>
|
|
46
|
+
) : null}
|
|
47
|
+
{hasApiLogs ? (
|
|
48
|
+
<Link to="/api-logs">
|
|
49
|
+
<StyledLi>API logs</StyledLi>
|
|
50
|
+
</Link>
|
|
51
|
+
) : null}
|
|
52
|
+
<StyledLi onClick={() => handleLogout(logoutRedirect)}>
|
|
53
|
+
{userProfileSettings?.logoutLabel || 'Log out'}
|
|
54
|
+
</StyledLi>
|
|
55
|
+
</StyledUl>
|
|
56
|
+
}
|
|
57
|
+
>
|
|
58
|
+
<Profile
|
|
59
|
+
name={userInfo.name}
|
|
60
|
+
imageUrl={userInfo.picture}
|
|
61
|
+
onClick={userInfo.logoutDisabled ? undefined : () => setIsOpened(!isOpened)}
|
|
62
|
+
/>
|
|
63
|
+
</StyledTooltip>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const StyledTooltip = styled(Tooltip)`
|
|
68
|
+
> span {
|
|
69
|
+
padding: 0;
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
const StyledUl = styled.ul`
|
|
74
|
+
margin: 0;
|
|
75
|
+
padding: 0;
|
|
76
|
+
list-style: none;
|
|
77
|
+
text-align: left;
|
|
78
|
+
background-color: var(--search-modal-background);
|
|
79
|
+
color: var(--search-modal-text-color);
|
|
80
|
+
min-width: 100px;
|
|
81
|
+
a {
|
|
82
|
+
text-decoration: none;
|
|
83
|
+
&:hover {
|
|
84
|
+
color: inherit;
|
|
85
|
+
}
|
|
86
|
+
&:visited {
|
|
87
|
+
color: inherit;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
const StyledLi = styled.li`
|
|
93
|
+
cursor: pointer;
|
|
94
|
+
font-size: 16px;
|
|
95
|
+
list-style: none;
|
|
96
|
+
padding: 15px 20px;
|
|
97
|
+
transition: background-color 0.25s ease 0s;
|
|
98
|
+
&:hover {
|
|
99
|
+
background-color: rgba(0, 0, 0, 0.1);
|
|
100
|
+
}
|
|
101
|
+
`;
|
package/src/Profile/index.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
1
|
export { Profile } from './Profile';
|
|
2
2
|
export type { ProfileProps } from './Profile';
|
|
3
|
+
export { LoginLink } from './LoginLink';
|
|
4
|
+
export type { LoginLinkProps } from './LoginLink';
|
|
5
|
+
export { UserProfile } from './UserProfile';
|
|
6
|
+
export type { UserProfileProps } from './UserProfile';
|
package/src/Search/Popover.tsx
CHANGED
|
@@ -8,7 +8,6 @@ export const Popover = styled.div.attrs(() => ({
|
|
|
8
8
|
right: 0;
|
|
9
9
|
z-index: 100;
|
|
10
10
|
min-width: 100%;
|
|
11
|
-
width: 550px;
|
|
12
11
|
max-width: 90vw;
|
|
13
12
|
max-height: 400px;
|
|
14
13
|
overflow-y: auto;
|
|
@@ -17,4 +16,13 @@ export const Popover = styled.div.attrs(() => ({
|
|
|
17
16
|
list-style: none;
|
|
18
17
|
border-radius: var(--search-popover-border-radius);
|
|
19
18
|
border: var(--search-popover-border);
|
|
19
|
+
width: 100%;
|
|
20
|
+
|
|
21
|
+
${({ theme }) => theme.mediaQueries?.small} {
|
|
22
|
+
width: 400px;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
${({ theme }) => theme.mediaQueries?.medium} {
|
|
26
|
+
width: 550px;
|
|
27
|
+
}
|
|
20
28
|
`;
|
package/src/Search/Search.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import * as React from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import { usePreloadHistory } from '@portal/usePreloadHistory';
|
|
@@ -33,22 +33,32 @@ export function Search(): JSX.Element {
|
|
|
33
33
|
// TODO: ask somebody about typings
|
|
34
34
|
const navigate = (item: SearchDocument) => history.push(item.url);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
</Autocomplete>
|
|
48
|
-
</Wrapper>
|
|
36
|
+
const renderAutocomplete = () => (
|
|
37
|
+
<Autocomplete
|
|
38
|
+
items={items}
|
|
39
|
+
value={query}
|
|
40
|
+
change={setQuery}
|
|
41
|
+
select={navigate}
|
|
42
|
+
placeholder="Search the docs"
|
|
43
|
+
renderItem={(item) => <SearchItem key={item.id} item={item} />}
|
|
44
|
+
>
|
|
45
|
+
{(isOpen, reset) => (isOpen ? <ClearIcon onClick={reset} /> : <SearchIcon />)}
|
|
46
|
+
</Autocomplete>
|
|
49
47
|
);
|
|
48
|
+
|
|
49
|
+
return <Wrapper data-component-name="Search/Search">{renderAutocomplete()}</Wrapper>;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
const Wrapper = styled.div`
|
|
53
|
-
margin:
|
|
53
|
+
margin-left: auto;
|
|
54
|
+
|
|
55
|
+
display: none;
|
|
56
|
+
|
|
57
|
+
${({ theme }) => theme.mediaQueries?.small} {
|
|
58
|
+
display: block;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
${({ theme }) => theme.mediaQueries?.medium} {
|
|
62
|
+
margin: 0 auto;
|
|
63
|
+
}
|
|
54
64
|
`;
|
|
@@ -7,7 +7,7 @@ const Arrow = ({ className }: { className?: string }): JSX.Element => (
|
|
|
7
7
|
fill="none"
|
|
8
8
|
xmlns="http://www.w3.org/2000/svg"
|
|
9
9
|
viewBox="0 0 12 10"
|
|
10
|
-
width="
|
|
10
|
+
width="12px"
|
|
11
11
|
height="10px"
|
|
12
12
|
className={className}
|
|
13
13
|
>
|
|
@@ -19,7 +19,7 @@ const Arrow = ({ className }: { className?: string }): JSX.Element => (
|
|
|
19
19
|
|
|
20
20
|
export const ArrowBack = styled(Arrow)`
|
|
21
21
|
fill: var(--sidebar-back-button-icon-color);
|
|
22
|
-
margin-right: calc(var(--sidebar-spacing-unit));
|
|
22
|
+
margin-right: calc(var(--sidebar-spacing-unit) * 1.5);
|
|
23
23
|
|
|
24
24
|
background-image: var(--sidebar-back-button-icon);
|
|
25
25
|
background-repeat: no-repeat;
|
|
@@ -7,23 +7,13 @@ import { MobileSidebarButton } from '@theme/Sidebar/MobileSidebarButton';
|
|
|
7
7
|
import { MenuContainer } from '@theme/Sidebar/MenuContainer';
|
|
8
8
|
import { SidebarSearch } from '@theme/Search/SidebarSearch';
|
|
9
9
|
import { useThemeConfig } from '@theme/hooks/useThemeConfig';
|
|
10
|
-
import { ArrowBack } from '@theme/Sidebar/ArrowBack';
|
|
11
|
-
import { Link } from '@portal/Link';
|
|
12
10
|
|
|
13
11
|
interface SidebarLayoutProps {
|
|
14
12
|
versions: React.ReactNode;
|
|
15
13
|
menu: React.ReactNode;
|
|
16
|
-
backLink?: {
|
|
17
|
-
label: string;
|
|
18
|
-
slug: string;
|
|
19
|
-
};
|
|
20
14
|
}
|
|
21
15
|
|
|
22
|
-
export function SidebarLayout({
|
|
23
|
-
versions,
|
|
24
|
-
menu,
|
|
25
|
-
backLink,
|
|
26
|
-
}: SidebarLayoutProps): JSX.Element | null {
|
|
16
|
+
export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Element | null {
|
|
27
17
|
const [isOpen, setIsOpen] = useMobileMenu();
|
|
28
18
|
const toggleMenu = () => setIsOpen(!isOpen);
|
|
29
19
|
const { search, sidebar } = useThemeConfig();
|
|
@@ -38,15 +28,6 @@ export function SidebarLayout({
|
|
|
38
28
|
|
|
39
29
|
{!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
|
|
40
30
|
<Sidebar animate={true} opened={isOpen}>
|
|
41
|
-
{(backLink && (
|
|
42
|
-
<BackLinkWrapper>
|
|
43
|
-
<Link to={backLink.slug}>
|
|
44
|
-
<ArrowBack />
|
|
45
|
-
Back to {backLink.label}
|
|
46
|
-
</Link>
|
|
47
|
-
</BackLinkWrapper>
|
|
48
|
-
)) ||
|
|
49
|
-
null}
|
|
50
31
|
{versions}
|
|
51
32
|
<MenuContainer>{menu}</MenuContainer>
|
|
52
33
|
</Sidebar>
|
|
@@ -54,22 +35,4 @@ export function SidebarLayout({
|
|
|
54
35
|
);
|
|
55
36
|
}
|
|
56
37
|
|
|
57
|
-
const BackLinkWrapper = styled.div`
|
|
58
|
-
padding: var(--sidebar-offset-top) var(--sidebar-item-padding-horizontal)
|
|
59
|
-
var(--sidebar-item-padding-horizontal)
|
|
60
|
-
calc(var(--sidebar-offset-left) + var(--sidebar-item-padding-horizontal));
|
|
61
|
-
|
|
62
|
-
a {
|
|
63
|
-
color: var(--sidebar-back-button-text-color);
|
|
64
|
-
font-size: var(--sidebar-back-button-font-size);
|
|
65
|
-
font-family: var(--sidebar-back-button-font-family);
|
|
66
|
-
text-decoration: none;
|
|
67
|
-
&:hover {
|
|
68
|
-
color: var(--sidebar-back-button-hover-text-color);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
border-bottom: 1px solid var(--sidebar-border-color);
|
|
73
|
-
`;
|
|
74
|
-
|
|
75
38
|
const Wrapper = styled.div``;
|
package/src/config.ts
CHANGED
|
@@ -81,6 +81,16 @@ export const ThemeConfig = z
|
|
|
81
81
|
.optional(),
|
|
82
82
|
links: z.array(LinksConfig).optional(),
|
|
83
83
|
|
|
84
|
+
feedback: z
|
|
85
|
+
.object({
|
|
86
|
+
type: z.string().default('sentiment'),
|
|
87
|
+
settings: z.string().optional(),
|
|
88
|
+
})
|
|
89
|
+
.extend(HideConfig.shape)
|
|
90
|
+
.strict()
|
|
91
|
+
.default({})
|
|
92
|
+
.optional(),
|
|
93
|
+
|
|
84
94
|
search: z
|
|
85
95
|
.object({
|
|
86
96
|
placement: z.string().default('navbar').optional(),
|
|
@@ -159,6 +169,15 @@ export const ThemeConfig = z
|
|
|
159
169
|
.optional(),
|
|
160
170
|
openapi: z.object({}).passthrough().optional(),
|
|
161
171
|
graphql: z.object({}).passthrough().optional(),
|
|
172
|
+
userProfile: z
|
|
173
|
+
.object({
|
|
174
|
+
loginLabel: z.string().default('Login').optional(),
|
|
175
|
+
logoutLabel: z.string().default('Logout').optional(),
|
|
176
|
+
logoutRedirect: z.string().default('/').optional(),
|
|
177
|
+
})
|
|
178
|
+
.extend(HideConfig.shape)
|
|
179
|
+
.optional()
|
|
180
|
+
.default({}),
|
|
162
181
|
})
|
|
163
182
|
.passthrough()
|
|
164
183
|
.default({});
|