@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.
Files changed (84) hide show
  1. package/lib/Button/Button.d.ts +3 -3
  2. package/lib/Feedback/Rating.d.ts +3 -0
  3. package/lib/Feedback/Rating.js +69 -0
  4. package/lib/Feedback/Sentiment.d.ts +3 -0
  5. package/lib/Feedback/Sentiment.js +55 -0
  6. package/lib/Feedback/Thumbs.d.ts +7 -0
  7. package/lib/Feedback/Thumbs.js +79 -0
  8. package/lib/Feedback/index.d.ts +3 -0
  9. package/lib/Feedback/index.js +23 -0
  10. package/lib/Feedback/types.d.ts +26 -0
  11. package/lib/Feedback/types.js +3 -0
  12. package/lib/Markdown/CodeSample/CodeSample.d.ts +1 -1
  13. package/lib/Markdown/Details.d.ts +1 -1
  14. package/lib/Markdown/MarkdownLayout.d.ts +3 -2
  15. package/lib/Markdown/MarkdownLayout.js +8 -2
  16. package/lib/Markdown/Mermaid.d.ts +1 -1
  17. package/lib/Markdown/Tabs/Tab.d.ts +1 -1
  18. package/lib/Markdown/Tabs/Tabs.d.ts +1 -1
  19. package/lib/Navbar/MobileNavbarMenu.d.ts +3 -2
  20. package/lib/Navbar/MobileNavbarMenu.js +16 -4
  21. package/lib/Navbar/Navbar.js +6 -3
  22. package/lib/NavbarLogo/NavbarLogo.d.ts +1 -1
  23. package/lib/Profile/LoginLink.d.ts +5 -0
  24. package/lib/Profile/LoginLink.js +30 -0
  25. package/lib/Profile/Profile.js +8 -1
  26. package/lib/Profile/UserProfile.d.ts +13 -0
  27. package/lib/Profile/UserProfile.js +82 -0
  28. package/lib/Profile/index.d.ts +4 -0
  29. package/lib/Profile/index.js +5 -1
  30. package/lib/Search/Autocomplete.js +1 -0
  31. package/lib/Search/Popover.js +9 -1
  32. package/lib/Search/Search.js +37 -4
  33. package/lib/Sidebar/ArrowBack.js +2 -2
  34. package/lib/Sidebar/SidebarLayout.d.ts +1 -5
  35. package/lib/Sidebar/SidebarLayout.js +1 -26
  36. package/lib/SourceCode/SourceCode.d.ts +1 -1
  37. package/lib/config.d.ts +55 -2
  38. package/lib/config.js +18 -0
  39. package/lib/globalStyle.js +6 -2
  40. package/lib/hooks/useActiveHeading.d.ts +1 -1
  41. package/lib/hooks/useActiveSectionId.d.ts +1 -1
  42. package/lib/hooks/useActiveSectionId.js +0 -3
  43. package/lib/hooks/useControl.d.ts +1 -1
  44. package/lib/hooks/useNavbarHeight.d.ts +1 -1
  45. package/lib/index.d.ts +1 -0
  46. package/lib/index.js +1 -0
  47. package/lib/mocks/hooks/index.js +6 -0
  48. package/lib/mocks/types/index.d.ts +1 -1
  49. package/lib/mocks/types.d.ts +3 -3
  50. package/lib/types/config.d.ts +1 -1
  51. package/lib/ui/Box.d.ts +2 -2
  52. package/lib/ui/Box.js +1 -0
  53. package/lib/ui/Burger.js +3 -2
  54. package/lib/ui/Dropdown.d.ts +1 -1
  55. package/lib/ui/Tiles/ThinTile.d.ts +1 -1
  56. package/lib/utils/jsonToHtml.d.ts +1 -1
  57. package/lib/utils/media-css.d.ts +1 -1
  58. package/package.json +7 -4
  59. package/src/Feedback/Rating.tsx +79 -0
  60. package/src/Feedback/Sentiment.tsx +36 -0
  61. package/src/Feedback/Thumbs.tsx +116 -0
  62. package/src/Feedback/index.ts +3 -0
  63. package/src/Feedback/types.ts +29 -0
  64. package/src/Markdown/MarkdownLayout.tsx +10 -1
  65. package/src/Navbar/MobileNavbarMenu.tsx +14 -0
  66. package/src/Navbar/Navbar.tsx +12 -3
  67. package/src/Profile/LoginLink.tsx +29 -0
  68. package/src/Profile/Profile.tsx +8 -1
  69. package/src/Profile/UserProfile.tsx +101 -0
  70. package/src/Profile/index.ts +4 -0
  71. package/src/Search/Autocomplete.tsx +1 -0
  72. package/src/Search/Popover.tsx +9 -1
  73. package/src/Search/Search.tsx +25 -15
  74. package/src/Sidebar/ArrowBack.tsx +2 -2
  75. package/src/Sidebar/SidebarLayout.tsx +1 -38
  76. package/src/config.ts +19 -0
  77. package/src/globalStyle.ts +7 -2
  78. package/src/hooks/useActiveSectionId.ts +0 -2
  79. package/src/index.ts +1 -0
  80. package/src/mocks/hooks/index.ts +6 -0
  81. package/src/settings.yaml +2 -0
  82. package/src/types/portal/index.d.ts +1 -1
  83. package/src/ui/Box.tsx +5 -2
  84. 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,3 @@
1
+ export { Rating } from './Rating';
2
+ export { Sentiment } from './Sentiment';
3
+ export * from './types';
@@ -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 ? { ...editPage, ...themeEditPage } : undefined;
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
+ `;
@@ -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 && <MobileNavbarMenu closeMenu={closeMobileMenu} menuItems={menu} />}
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
+ `;
@@ -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
+ `;
@@ -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';
@@ -46,6 +46,7 @@ export function Autocomplete<T>({
46
46
 
47
47
  const onChange = (event: ChangeEvent<HTMLInputElement>) => {
48
48
  setActiveIdx(-1);
49
+ setIsOpen(true);
49
50
  change(event.target.value);
50
51
  };
51
52
 
@@ -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
  `;
@@ -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
- return (
37
- <Wrapper data-component-name="Search/Search">
38
- <Autocomplete
39
- items={items}
40
- value={query}
41
- change={setQuery}
42
- select={navigate}
43
- placeholder="Search the docs"
44
- renderItem={(item) => <SearchItem key={item.id} item={item} />}
45
- >
46
- {(isOpen, reset) => (isOpen ? <ClearIcon onClick={reset} /> : <SearchIcon />)}
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: 0 auto;
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="10px"
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({});