@redocly/theme 0.7.4 → 0.8.0

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 (65) hide show
  1. package/lib/Catalog/Catalog.d.ts +8 -0
  2. package/lib/Catalog/Catalog.js +167 -0
  3. package/lib/Catalog/CatalogCard.d.ts +5 -0
  4. package/lib/Catalog/CatalogCard.js +113 -0
  5. package/lib/Catalog/Filter.d.ts +5 -0
  6. package/lib/Catalog/Filter.js +88 -0
  7. package/lib/Catalog/Tags.d.ts +4 -0
  8. package/lib/Catalog/Tags.js +32 -0
  9. package/lib/Feedback/ReportDialog.d.ts +3 -0
  10. package/lib/Feedback/ReportDialog.js +66 -0
  11. package/lib/Feedback/index.d.ts +2 -0
  12. package/lib/Feedback/index.js +5 -1
  13. package/lib/Feedback/types.d.ts +5 -3
  14. package/lib/Feedback/useReportDialog.d.ts +7 -0
  15. package/lib/Feedback/useReportDialog.js +28 -0
  16. package/lib/Markdown/CodeSample/CodeSample.js +5 -38
  17. package/lib/Sidebar/ArrowBack.js +2 -2
  18. package/lib/Sidebar/MenuGroup.js +1 -1
  19. package/lib/Sidebar/Sidebar.d.ts +1 -0
  20. package/lib/Sidebar/Sidebar.js +5 -2
  21. package/lib/Sidebar/SidebarLayout.d.ts +6 -1
  22. package/lib/Sidebar/SidebarLayout.js +27 -2
  23. package/lib/TableOfContent/TableOfContent.js +1 -1
  24. package/lib/config.js +2 -2
  25. package/lib/globalStyle.js +13 -11
  26. package/lib/hooks/useActiveHeading.js +2 -7
  27. package/lib/hooks/useActiveSectionId.js +1 -1
  28. package/lib/hooks/useMobileMenu.js +1 -1
  29. package/lib/mocks/hooks/index.d.ts +4 -0
  30. package/lib/mocks/hooks/index.js +9 -1
  31. package/lib/ui/Checkbox.d.ts +1 -0
  32. package/lib/ui/Checkbox.js +70 -0
  33. package/lib/ui/Highlight.d.ts +5 -0
  34. package/lib/ui/Highlight.js +63 -0
  35. package/lib/ui/Jumbotron.js +2 -2
  36. package/lib/ui/darkColors.js +4 -2
  37. package/package.json +6 -8
  38. package/src/Catalog/Catalog.tsx +198 -0
  39. package/src/Catalog/CatalogCard.tsx +95 -0
  40. package/src/Catalog/Filter.tsx +103 -0
  41. package/src/Catalog/Tags.tsx +36 -0
  42. package/src/Feedback/ReportDialog.tsx +51 -0
  43. package/src/Feedback/index.ts +2 -0
  44. package/src/Feedback/types.ts +5 -3
  45. package/src/Feedback/useReportDialog.ts +34 -0
  46. package/src/Markdown/CodeSample/CodeSample.tsx +7 -50
  47. package/src/Sidebar/ArrowBack.tsx +2 -2
  48. package/src/Sidebar/MenuGroup.tsx +1 -1
  49. package/src/Sidebar/Sidebar.tsx +6 -2
  50. package/src/Sidebar/SidebarLayout.tsx +41 -2
  51. package/src/TableOfContent/TableOfContent.tsx +1 -1
  52. package/src/config.ts +2 -2
  53. package/src/globalStyle.ts +13 -11
  54. package/src/hooks/useActiveHeading.ts +3 -12
  55. package/src/hooks/useActiveSectionId.ts +1 -1
  56. package/src/hooks/useMobileMenu.ts +2 -2
  57. package/src/mocks/hooks/index.ts +10 -1
  58. package/src/types/portal/src/shared/types/catalog.d.ts +55 -0
  59. package/src/ui/Checkbox.tsx +64 -0
  60. package/src/ui/Highlight.tsx +48 -0
  61. package/src/ui/Jumbotron.tsx +2 -2
  62. package/src/ui/darkColors.tsx +4 -2
  63. package/lib/hooks/useReportDialog.d.ts +0 -1
  64. package/lib/hooks/useReportDialog.js +0 -16
  65. package/src/hooks/useReportDialog.ts +0 -14
@@ -7,13 +7,25 @@ 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';
10
12
 
11
13
  interface SidebarLayoutProps {
12
14
  versions: React.ReactNode;
13
15
  menu: React.ReactNode;
16
+ backLink?: {
17
+ label: string;
18
+ slug: string;
19
+ };
20
+ isNavbar: boolean;
14
21
  }
15
22
 
16
- export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Element | null {
23
+ export function SidebarLayout({
24
+ versions,
25
+ menu,
26
+ backLink,
27
+ isNavbar,
28
+ }: SidebarLayoutProps): JSX.Element | null {
17
29
  const [isOpen, setIsOpen] = useMobileMenu();
18
30
  const toggleMenu = () => setIsOpen(!isOpen);
19
31
  const { search, sidebar } = useThemeConfig();
@@ -27,7 +39,16 @@ export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Eleme
27
39
  <MobileSidebarButton opened={isOpen} onClick={toggleMenu} />
28
40
 
29
41
  {!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
30
- <Sidebar animate={true} opened={isOpen}>
42
+ <Sidebar animate={true} opened={isOpen} isNavbar={isNavbar}>
43
+ {(backLink && (
44
+ <BackLinkWrapper>
45
+ <Link to={backLink.slug}>
46
+ <ArrowBack />
47
+ Back to {backLink.label}
48
+ </Link>
49
+ </BackLinkWrapper>
50
+ )) ||
51
+ null}
31
52
  {versions}
32
53
  <MenuContainer>{menu}</MenuContainer>
33
54
  </Sidebar>
@@ -35,4 +56,22 @@ export function SidebarLayout({ versions, menu }: SidebarLayoutProps): JSX.Eleme
35
56
  );
36
57
  }
37
58
 
59
+ const BackLinkWrapper = styled.div`
60
+ padding: var(--sidebar-offset-top) var(--sidebar-item-padding-horizontal)
61
+ var(--sidebar-item-padding-horizontal)
62
+ calc(var(--sidebar-offset-left) + var(--sidebar-item-padding-horizontal));
63
+
64
+ a {
65
+ color: var(--sidebar-back-button-text-color);
66
+ font-size: var(--sidebar-back-button-font-size);
67
+ font-family: var(--sidebar-back-button-font-family);
68
+ text-decoration: none;
69
+ &:hover {
70
+ color: var(--sidebar-back-button-hover-text-color);
71
+ }
72
+ }
73
+
74
+ border-bottom: 1px solid var(--sidebar-border-color);
75
+ `;
76
+
38
77
  const Wrapper = styled.div``;
@@ -109,5 +109,5 @@ const TableOfContentItems = styled.div`
109
109
  max-height: calc(100vh - var(--navbar-height) - var(--toc-offset-top));
110
110
  border-left: 1px solid var(--toc-border-color);
111
111
  overflow-y: auto;
112
- width: var(--toc-width); ;
112
+ width: var(--toc-width);
113
113
  `;
package/src/config.ts CHANGED
@@ -132,7 +132,7 @@ export const ThemeConfig = z
132
132
  .object({
133
133
  buttonText: z.string().default('Copy').optional(),
134
134
  tooltipText: z.string().default('Copy to clipboard').optional(),
135
- toasterText: z.string().default('Copied').optional(),
135
+ toasterText: z.string().default('Copied!').optional(),
136
136
  toasterDuration: z.number().default(1500).optional(),
137
137
  })
138
138
  .extend(HideConfig.shape)
@@ -145,7 +145,7 @@ export const ThemeConfig = z
145
145
  })
146
146
  .extend(HideConfig.shape)
147
147
  .optional()
148
- .default({}),
148
+ .default({ hide: true }),
149
149
  })
150
150
  .strict()
151
151
  .default({})
@@ -204,7 +204,7 @@ const headingsTypography = css`
204
204
  --h2-font-weight: var(--heading-font-weight); // @presenter FontWeight
205
205
  --h2-font-size: 28px; // @presenter FontSize
206
206
  --h2-line-height: 28px; // @presenter LineHeight
207
- --h2-margin-top: 0; // @presenter Spacing
207
+ --h2-margin-top: var(--heading-margin-top); // @presenter Spacing
208
208
  --h2-margin-bottom: var(--heading-margin-bottom); // @presenter Spacing
209
209
  --h2-text-color: var(--heading-text-color); // @presenter Color
210
210
 
@@ -517,7 +517,7 @@ const sidebar = css`
517
517
  --sidebar-back-button-font-family: var(--sidebar-item-font-family);
518
518
  --sidebar-back-button-font-size: var(--sidebar-item-font-size);
519
519
  --sidebar-back-button-transform: inherit;
520
- --sidebar-back-button-text-color: var(--sidebar-item-text-color);
520
+ --sidebar-back-button-text-color: var(--link-text-color);
521
521
  --sidebar-back-button-background-color: transparent;
522
522
  --sidebar-back-button-hover-text-color: var(--sidebar-item-active-color);
523
523
  --sidebar-back-button-hover-background-color: transparent;
@@ -1706,7 +1706,7 @@ const inputs = css`
1706
1706
  --input-border: none; // @presenter Border
1707
1707
  --input-border-radius: var(--border-radius); // @presenter BorderRadius
1708
1708
  --input-font-size: var(--font-size-base); // @presenter FontSize
1709
- --input-font-family: var(--code-font-family); // @presenter FontFamily
1709
+ --input-font-family: var(--font-family-base); // @presenter FontFamily
1710
1710
  --input-line-height: 1.15em; // @presenter LineHeight
1711
1711
  --input-padding: 8px;
1712
1712
 
@@ -1716,6 +1716,8 @@ const inputs = css`
1716
1716
  --input-focus-text-color: var(--text-color-inverse); // @presenter Color
1717
1717
  --input-placeholder-text-color: var(--text-color-inverse); // @presenter Color
1718
1718
 
1719
+ --checkbox-backround-color: var(--background-color);
1720
+ --checkbox-checked-backround-color: var(--color-primary-500);
1719
1721
  // @tokens End
1720
1722
  `;
1721
1723
 
@@ -1925,7 +1927,7 @@ const pages = css`
1925
1927
 
1926
1928
  --page-404-font-family: var(--font-family-base); // @presenter FontFamily
1927
1929
 
1928
- --page-404-header-text-color: #000;
1930
+ --page-404-header-text-color: #000;
1929
1931
  --page-404-header-font-size: 14em; // @presenter FontSize
1930
1932
  --page-404-header-font-weight: 600; // @presenter FontWeight
1931
1933
  --page-404-header-line-height: 1.2; // @presenter LineHeight
@@ -1945,31 +1947,31 @@ const pages = css`
1945
1947
  * @tokens 403 Page
1946
1948
  * @presenter Color
1947
1949
  */
1948
-
1950
+
1949
1951
  --page-403-font-family: var(--font-family-base); // @presenter FontFamily
1950
-
1952
+
1951
1953
  --page-403-header-text-color: #000;
1952
1954
  --page-403-header-font-size: 14em; // @presenter FontSize
1953
1955
  --page-403-header-font-weight: 600; // @presenter FontWeight
1954
1956
  --page-403-header-line-height: 1.2; // @presenter LineHeight
1955
1957
  --page-403-header-margin: 0; // @presenter Spacing
1956
-
1958
+
1957
1959
  --page-403-description-text-color: #000;
1958
1960
  --page-403-description-font-size: 2em; // @presenter FontSize
1959
1961
  --page-403-description-font-weight: 400; // @presenter FontWeight
1960
1962
  --page-403-description-line-height: 1; // @presenter LineHeight
1961
1963
  --page-403-description-margin: 0; // @presenter Spacing
1962
-
1964
+
1963
1965
  --page-403-button-margin: 4em; // @presenter Spacing
1964
-
1966
+
1965
1967
  // @tokens End
1966
1968
  `
1967
1969
 
1968
1970
  const modal = css`
1969
- body:has([id='modal']) {
1971
+ body:has(.modal) {
1970
1972
  overflow: hidden;
1971
1973
  }
1972
-
1974
+
1973
1975
  --modal-box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.6);
1974
1976
  --modal-overlay-background-color: rgba(206, 206, 206, 0.49);
1975
1977
  --modal-background-color: var(--background-color);
@@ -1,6 +1,5 @@
1
1
  import { useState, useEffect, useRef, useCallback } from 'react';
2
- import { useHistory } from 'react-router-dom';
3
-
2
+ import { useLocation } from 'react-router-dom';
4
3
  export type UseActiveHeadingReturnType = string | undefined;
5
4
 
6
5
  type HeadingEntry = {
@@ -16,8 +15,7 @@ export function useActiveHeading(
16
15
  );
17
16
  const [headingElements, setHeadingElements] = useState<HTMLElement[]>([]);
18
17
  const headingElementsRef = useRef<HeadingEntry>({});
19
-
20
- const history = useHistory();
18
+ const location = useLocation();
21
19
 
22
20
  const getVisibleHeadings = () => {
23
21
  const visibleHeadings: IntersectionObserverEntry[] = [];
@@ -86,14 +84,7 @@ export function useActiveHeading(
86
84
  return;
87
85
  }
88
86
  setHeadingElements(findHeaders(contentElement));
89
-
90
- const unlisten = history.listen(() => {
91
- setHeadingElements(findHeaders(contentElement));
92
- });
93
-
94
- return () => unlisten();
95
- // eslint-disable-next-line react-hooks/exhaustive-deps
96
- }, [contentElement]);
87
+ }, [contentElement, location]);
97
88
 
98
89
  useEffect(() => {
99
90
  if (!headingElements?.length) {
@@ -41,7 +41,7 @@ export function useActiveSectionId(
41
41
  window.addEventListener('scroll', scrollListener, { capture: false });
42
42
  setTimeout(() => {
43
43
  scrollListener();
44
- }, 0);
44
+ }, 10);
45
45
 
46
46
  return () => {
47
47
  window.removeEventListener('scroll', scrollListener);
@@ -1,10 +1,10 @@
1
1
  import { useEffect, useState } from 'react';
2
- import { useHistory } from 'react-router-dom';
2
+ import { useLocation } from 'react-router-dom';
3
3
 
4
4
  import type { Dispatch, SetStateAction } from 'react';
5
5
 
6
6
  export function useMobileMenu(initialState = false): [boolean, Dispatch<SetStateAction<boolean>>] {
7
- const { location } = useHistory();
7
+ const location = useLocation();
8
8
  const [isOpen, setIsOpen] = useState(initialState);
9
9
 
10
10
  useEffect(() => setIsOpen(false), [location.pathname]);
@@ -1,5 +1,6 @@
1
1
  import type { ThemeUIConfig } from '@theme/config';
2
-
2
+ import type { ResolvedNavItem } from '@theme/types/portal';
3
+ import type { CatalogConfig, FilteredCatalog } from '@theme/types/portal/src/shared/types/catalog';
3
4
  interface PageLink {
4
5
  label: string;
5
6
  link: string;
@@ -65,3 +66,11 @@ export function useSidebarSiblingsData(): { nextPage: PageLink | null; prevPage:
65
66
  },
66
67
  };
67
68
  }
69
+
70
+ export function usePageSharedData<T = unknown>(_id: string): T {
71
+ throw new Error('Mock not implemented yet.');
72
+ }
73
+
74
+ export function useCatalog(_items: ResolvedNavItem[], _config: CatalogConfig): FilteredCatalog {
75
+ throw new Error('Mock not implemented yet.');
76
+ }
@@ -0,0 +1,55 @@
1
+ import type { NavItem } from './nav';
2
+
3
+ export type FilteredCatalog = {
4
+ groups: { title: string; items: CatalogItem[] }[];
5
+ filters: ResolvedFilter[];
6
+ filterTerm: string;
7
+ setFilterTerm: (term: string) => void;
8
+ };
9
+
10
+ export type Filter = {
11
+ type?: 'select' | 'checkboxes';
12
+ title: string;
13
+ property: string;
14
+ parentFilter?: string;
15
+ missingCategoryName?: string;
16
+ options?: string[];
17
+ };
18
+
19
+ export type CatalogConfig = {
20
+ slug: string;
21
+ filters: Filter[];
22
+ groupByFirstFilter: boolean;
23
+ items: NavItem[];
24
+ requiredPermission?: string;
25
+ separateVersions?: boolean;
26
+ title?: string;
27
+ description?: string;
28
+ };
29
+
30
+ export type CatalogThemeConfig = {
31
+ catalog: Record<string, CatalogConfig>;
32
+ };
33
+
34
+ export type ResolvedFilter = Omit<Filter, 'options'> & {
35
+ options: {
36
+ value: string;
37
+ count: number;
38
+ }[];
39
+ filteredOptions: {
40
+ value: string;
41
+ count: number;
42
+ }[];
43
+ toggleOption: (option: string) => void;
44
+ selectOption: (option: string) => void;
45
+ parentUsed: boolean;
46
+ selectedOptions: Set<string>;
47
+ };
48
+
49
+ export type CatalogItem = {
50
+ title: string;
51
+ link: string;
52
+ description?: string;
53
+ image?: string;
54
+ [k: string]: unknown;
55
+ };
@@ -0,0 +1,64 @@
1
+ import styled from 'styled-components';
2
+
3
+ export const Checkbox = styled.input`
4
+ position: absolute;
5
+ opacity: 0;
6
+
7
+ & + label {
8
+ position: relative;
9
+ cursor: pointer;
10
+ padding: 0;
11
+ display: flex;
12
+
13
+ div {
14
+ margin-top: 1px;
15
+ }
16
+ p {
17
+ line-height: 1.2;
18
+ }
19
+
20
+ &::before {
21
+ content: '';
22
+ margin-right: 10px;
23
+ display: inline-block;
24
+ vertical-align: top;
25
+ width: 18px;
26
+ height: 18px;
27
+ background: var(--checkbox-backround-color);
28
+ border: 1px solid rgba(0, 0, 0, 0.23);
29
+ border-radius: 2px;
30
+ flex-shrink: 0;
31
+ }
32
+ }
33
+
34
+ &:focus + label:before {
35
+ box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.12);
36
+ }
37
+
38
+ &:checked + label {
39
+ &::before {
40
+ background: var(--checkbox-checked-backround-color);
41
+ }
42
+ &::after {
43
+ content: '';
44
+ position: absolute;
45
+ left: 5px;
46
+ top: 10px;
47
+ background: var(--checkbox-backround-color);
48
+ width: 2px;
49
+ height: 2px;
50
+ box-shadow: 2px 0 0 white, 4px 0 0 white, 4px -2px 0 white, 4px -4px 0 white, 4px -6px 0 white,
51
+ 4px -8px 0 white;
52
+ transform: rotate(45deg);
53
+ }
54
+ }
55
+
56
+ &:disabled + label {
57
+ color: #b8b8b8;
58
+ cursor: auto;
59
+ &::before {
60
+ box-shadow: none;
61
+ background: #ddd;
62
+ }
63
+ }
64
+ `;
@@ -0,0 +1,48 @@
1
+ import * as React from 'react';
2
+ import { findAll } from 'highlight-words-core';
3
+
4
+ export const HighlightContext = React.createContext<string[]>([]);
5
+
6
+ export function Highlight(props: { children: React.ReactChildren | string }): JSX.Element {
7
+ const { children } = props;
8
+ const searchWords = React.useContext(HighlightContext);
9
+
10
+ if (!searchWords.length) {
11
+ return <>children</> || null;
12
+ }
13
+
14
+ function highlight(str: string, childIdx: number = 0) {
15
+ const chunks = findAll({
16
+ searchWords,
17
+ textToHighlight: str,
18
+ });
19
+
20
+ return (
21
+ <React.Fragment key={childIdx}>
22
+ {chunks.map((chunk, idx) => {
23
+ const { end, highlight, start } = chunk;
24
+ const text = str.substr(start, end - start);
25
+ if (highlight) {
26
+ return <mark key={idx}>{text}</mark>;
27
+ } else {
28
+ return text;
29
+ }
30
+ })}
31
+ </React.Fragment>
32
+ );
33
+ }
34
+
35
+ if (typeof children === 'string') {
36
+ return highlight(children);
37
+ } else if (Array.isArray(children)) {
38
+ return (
39
+ <>
40
+ {children.map((child, idx) =>
41
+ typeof children === 'string' ? highlight(child, idx) : child || null,
42
+ )}
43
+ </>
44
+ );
45
+ } else {
46
+ return <>children</> || null;
47
+ }
48
+ }
@@ -19,8 +19,8 @@ export const Jumbotron = styled(Background).attrs(() => ({
19
19
  flex-direction: column;
20
20
  padding-top: ${({ pt }) => pt || '0'};
21
21
  padding-bottom: ${({ pb }) => pb || '8em'};
22
- padding-left: ${({ pl }) => pl || '0'};
23
- padding-right: ${({ pr }) => pr || '0'};
22
+ padding-left: ${({ pl }) => pl || '1rem'};
23
+ padding-right: ${({ pr }) => pr || '1rem'};
24
24
  ${({ bgColor }) => bgColor && `background: ${bgColor}`};
25
25
  ${({ bgImage, bgColor }) =>
26
26
  bgImage &&
@@ -60,12 +60,14 @@ export const darkMode = css`
60
60
  --tooltip-text-color: #fff;
61
61
  --md-table-head-background-color: var(--color-secondary-300);
62
62
  --md-tabs-hover-tab-border-color: var(--color-secondary-500);
63
- --wide-tile-background-color: #000000;
64
- --thin-tile-background-color: #000000;
63
+ --thin-tile-background-color: #1d242d;
64
+ --wide-tile-background-color: #1d242d;
65
65
  --page-404-header-text-color: #fff;
66
66
  --page-404-description-text-color: #fff;
67
67
  --page-403-header-text-color: #fff;
68
68
  --page-403-description-text-color: #fff;
69
+ --checkbox-backround-color: var(--color-secondary-900);
70
+ --checkbox-checked-backround-color: var(--color-primary-400);
69
71
 
70
72
  background-color: var(--background-color);
71
73
  color: var(--text-color);
@@ -1 +0,0 @@
1
- export declare function useReportDialog(initialState?: boolean): [boolean, () => void, () => void];
@@ -1,16 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useReportDialog = void 0;
4
- const react_1 = require("react");
5
- function useReportDialog(initialState = false) {
6
- const [isDialogShown, setIsShown] = (0, react_1.useState)(initialState);
7
- const showDialog = () => {
8
- setIsShown(true);
9
- };
10
- const hideDialog = () => {
11
- setIsShown(false);
12
- };
13
- return [isDialogShown, showDialog, hideDialog];
14
- }
15
- exports.useReportDialog = useReportDialog;
16
- //# sourceMappingURL=useReportDialog.js.map
@@ -1,14 +0,0 @@
1
- import { useState } from 'react';
2
-
3
- export function useReportDialog(initialState = false): [boolean, () => void, () => void] {
4
- const [isDialogShown, setIsShown] = useState(initialState);
5
-
6
- const showDialog = () => {
7
- setIsShown(true);
8
- };
9
- const hideDialog = () => {
10
- setIsShown(false);
11
- };
12
-
13
- return [isDialogShown, showDialog, hideDialog];
14
- }