@redocly/theme 0.9.12-alpha.0 → 0.9.13

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 (39) hide show
  1. package/lib/Catalog/Filter.d.ts +1 -3
  2. package/lib/Catalog/Filter.js +4 -83
  3. package/lib/ReferenceDocs/DevOnboardingTryItSecurity.js +17 -3
  4. package/lib/Sidebar/FooterWrapper.d.ts +2 -2
  5. package/lib/Sidebar/FooterWrapper.js +6 -3
  6. package/lib/Sidebar/SidebarLayout.d.ts +6 -3
  7. package/lib/Sidebar/SidebarLayout.js +55 -7
  8. package/lib/Sidebar/index.d.ts +1 -0
  9. package/lib/Sidebar/index.js +1 -0
  10. package/lib/SidebarActions/ChangeViewButton.d.ts +8 -0
  11. package/lib/SidebarActions/ChangeViewButton.js +14 -0
  12. package/lib/SidebarActions/CollapseSidebarButton.d.ts +7 -0
  13. package/lib/SidebarActions/CollapseSidebarButton.js +13 -0
  14. package/lib/SidebarActions/SidebarActions.d.ts +20 -0
  15. package/lib/SidebarActions/SidebarActions.js +26 -0
  16. package/lib/SidebarActions/ToggleRightPanelButton.d.ts +7 -0
  17. package/lib/SidebarActions/ToggleRightPanelButton.js +13 -0
  18. package/lib/SidebarActions/index.d.ts +1 -0
  19. package/lib/SidebarActions/index.js +7 -0
  20. package/lib/SidebarActions/styled.d.ts +17 -0
  21. package/lib/SidebarActions/styled.js +124 -0
  22. package/lib/globalStyle.js +1 -2
  23. package/lib/index.d.ts +1 -0
  24. package/lib/index.js +1 -0
  25. package/package.json +1 -2
  26. package/src/Catalog/Filter.tsx +3 -123
  27. package/src/ReferenceDocs/DevOnboardingTryItSecurity.tsx +19 -5
  28. package/src/Sidebar/FooterWrapper.tsx +7 -4
  29. package/src/Sidebar/SidebarLayout.tsx +66 -18
  30. package/src/Sidebar/index.ts +1 -0
  31. package/src/SidebarActions/ChangeViewButton.tsx +20 -0
  32. package/src/SidebarActions/CollapseSidebarButton.tsx +21 -0
  33. package/src/SidebarActions/SidebarActions.tsx +64 -0
  34. package/src/SidebarActions/ToggleRightPanelButton.tsx +21 -0
  35. package/src/SidebarActions/index.tsx +1 -0
  36. package/src/SidebarActions/styled.tsx +157 -0
  37. package/src/globalStyle.ts +1 -2
  38. package/src/index.ts +1 -0
  39. package/src/types/portal/src/shared/types/catalog.d.ts +1 -3
package/lib/index.js CHANGED
@@ -40,4 +40,5 @@ __exportStar(require("./config"), exports);
40
40
  __exportStar(require("./Pages"), exports);
41
41
  __exportStar(require("./Markdown"), exports);
42
42
  __exportStar(require("./ReferenceDocs"), exports);
43
+ __exportStar(require("./SidebarActions"), exports);
43
44
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.9.12-alpha.0",
3
+ "version": "0.9.13",
4
4
  "description": "Shared UI components lib",
5
5
  "author": "team@redocly.com",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -83,7 +83,6 @@
83
83
  "dependencies": {
84
84
  "copy-to-clipboard": "^3.3.3",
85
85
  "highlight-words-core": "^1.2.2",
86
- "react-date-picker": "9.2.0",
87
86
  "hotkeys-js": "^3.10.1",
88
87
  "timeago.js": "^4.0.2"
89
88
  },
@@ -1,31 +1,18 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
- import type { DatePickerProps } from 'react-date-picker';
5
-
6
4
  import type { ResolvedFilter } from '@theme/types/portal/src/shared/types/catalog';
7
5
  import { Checkbox } from '@theme/ui/Checkbox';
8
6
 
9
- // TODO: figure out how to fix this
10
- const DatePicker: React.FC<DatePickerProps> =
11
- // eslint-disable-next-line @typescript-eslint/no-var-requires, import/order, import/no-extraneous-dependencies
12
- require('react-date-picker').default;
13
-
14
- // disabled type checking for selectedOptions
15
- export function Filter({ filter }: { filter: ResolvedFilter & { selectedOptions: any } }) {
7
+ export function Filter({ filter }: { filter: ResolvedFilter }) {
16
8
  if (!filter.parentUsed) return null;
17
9
  return (
18
10
  <FilterGroup key={filter.property + filter.title}>
19
- <FilterTitle>
20
- {filter.title}
21
- {filter.selectedOptions?.size ? (
22
- <a onClick={() => filter.selectOption('')}> Clear </a>
23
- ) : null}
24
- </FilterTitle>
11
+ <FilterTitle>{filter.title}</FilterTitle>
25
12
  {filter.type === 'select' ? (
26
13
  <StyledSelect
27
14
  onChange={(e) => filter.selectOption(e.target.value)}
28
- value={(filter.selectedOptions as Set<any>).values().next()?.value || ''}
15
+ value={filter.selectedOptions.values().next()?.value || ''}
29
16
  >
30
17
  <option key="none" value="">
31
18
  All
@@ -36,49 +23,6 @@ export function Filter({ filter }: { filter: ResolvedFilter & { selectedOptions:
36
23
  </option>
37
24
  ))}
38
25
  </StyledSelect>
39
- ) : filter.type === 'date-range' ? (
40
- <>
41
- <DatePickerWrapper>
42
- <span> From: </span>
43
- <DatePicker
44
- closeCalendar={true}
45
- format="y-MM-dd"
46
- dayPlaceholder="DD"
47
- monthPlaceholder="MM"
48
- yearPlaceholder="YYYY"
49
- value={filter.selectedOptions.from ? new Date(filter.selectedOptions.from) : null}
50
- minDetail="decade"
51
- maxDate={new Date()}
52
- onChange={(from: Date) => {
53
- filter.selectOption({
54
- ...(filter.selectedOptions as any),
55
- from: formatDateWithNoTimeZone(from),
56
- });
57
- }}
58
- />
59
- </DatePickerWrapper>
60
- <DatePickerWrapper>
61
- <span> To: </span>
62
- <DatePicker
63
- closeCalendar={true}
64
- dayPlaceholder="DD"
65
- monthPlaceholder="MM"
66
- yearPlaceholder="YYYY"
67
- format="y-MM-dd"
68
- minDate={
69
- filter.selectedOptions.from ? new Date(filter.selectedOptions.from) : undefined
70
- }
71
- value={filter.selectedOptions.to ? new Date(filter.selectedOptions.to) : null}
72
- minDetail="decade"
73
- onChange={(to: Date) => {
74
- filter.selectOption({
75
- ...filter.selectedOptions,
76
- to: formatDateWithNoTimeZone(to),
77
- });
78
- }}
79
- />
80
- </DatePickerWrapper>
81
- </>
82
26
  ) : (
83
27
  filter.filteredOptions.map((value: any) => {
84
28
  const id = 'filter--' + filter.property + '--' + slug(value.value);
@@ -115,11 +59,6 @@ const FilterTitle = styled.h4`
115
59
  font-weight: var(--font-weight-bold);
116
60
  margin: 0;
117
61
  margin-bottom: 16px;
118
-
119
- > a {
120
- font-size: 14px;
121
- cursor: pointer;
122
- }
123
62
  `;
124
63
 
125
64
  const FilterValue = styled.label`
@@ -156,68 +95,9 @@ const StyledSelect = styled.select`
156
95
  background-position: right 10px center;
157
96
  background-size: 1em;
158
97
  width: 100%;
159
- padding-right: 25px;
160
98
  `;
161
99
 
162
100
  // TODO: import from portal
163
101
  function slug(str: string): string {
164
102
  return str.replace(/\s/g, '-').toLowerCase();
165
103
  }
166
-
167
- const DatePickerWrapper = styled.div`
168
- display: flex;
169
- flex-direction: row;
170
- margin-bottom: 5px;
171
-
172
- align-items: center;
173
- gap: 10px;
174
-
175
- > span {
176
- width: 50px;
177
- }
178
-
179
- .react-date-picker {
180
- flex: 1;
181
- }
182
-
183
- .react-calendar__tile--now {
184
- background: #cbf7f1;
185
- color: black;
186
-
187
- &:enabled:hover,
188
- &:enabled:focus {
189
- background: #b1efe7;
190
- color: black;
191
- }
192
- }
193
-
194
- .react-date-picker__inputGroup__input:invalid {
195
- background: rgb(255 125 0 / 10%);
196
- }
197
-
198
- .react-date-picker__button {
199
- padding: 4px 4px;
200
- svg {
201
- width: 12px;
202
- }
203
- }
204
-
205
- .react-date-picker__wrapper {
206
- border: 1px solid rgba(0, 0, 0, 0.23);
207
- border-radius: var(--border-radius);
208
- padding: var(--input-padding);
209
- }
210
-
211
- .react-date-picker__inputGroup__input {
212
- width: 20px;
213
- }
214
- `;
215
-
216
- function padZero(num: number) {
217
- return num < 10 ? '0' + num : num;
218
- }
219
-
220
- function formatDateWithNoTimeZone(date?: Date | null) {
221
- if (!date) return date;
222
- return `${date.getFullYear()}-${padZero(date.getMonth() + 1)}-${padZero(date.getDate())}`;
223
- }
@@ -5,6 +5,7 @@ import type { TryItSecurityAppsProps } from './TryItSecurityApps';
5
5
 
6
6
  import { Button } from '@theme/Button/Button';
7
7
  import { usePageData } from '@portal/hooks/usePageData';
8
+ import { usePreloadHistory } from '@portal/usePreloadHistory';
8
9
 
9
10
  import { Dropdown } from './Dropdown';
10
11
 
@@ -13,6 +14,7 @@ export function DevOnboardingTryItSecurity(props: TryItSecurityAppsProps) {
13
14
 
14
15
  // @ts-ignore
15
16
  const { userData } = usePageData('userData') || {};
17
+ const history = usePreloadHistory();
16
18
 
17
19
  // TODO: handle error
18
20
  const [_error, setError] = React.useState<string | undefined>();
@@ -26,6 +28,8 @@ export function DevOnboardingTryItSecurity(props: TryItSecurityAppsProps) {
26
28
  const [acceptValueUpdate, setAcceptValueUpdate] = React.useState(true);
27
29
  const initialRender = React.useRef(true);
28
30
 
31
+ const hasApiAccess = !loading && !!apps?.length;
32
+
29
33
  React.useEffect(() => {
30
34
  if (!props.value && initialRender.current) return;
31
35
  if (acceptValueUpdate) {
@@ -92,7 +96,11 @@ export function DevOnboardingTryItSecurity(props: TryItSecurityAppsProps) {
92
96
  });
93
97
  };
94
98
 
95
- const placeholder = loading ? 'Loading your apps...' : undefined;
99
+ const placeholder = loading
100
+ ? 'Loading your apps...'
101
+ : hasApiAccess
102
+ ? undefined
103
+ : 'You do not have access to this API';
96
104
 
97
105
  return (
98
106
  <>
@@ -109,11 +117,17 @@ export function DevOnboardingTryItSecurity(props: TryItSecurityAppsProps) {
109
117
  }}
110
118
  />
111
119
  <UseKeyButton
112
- disabled={!selectedAppSecret || appLoading}
120
+ disabled={(!selectedAppSecret && hasApiAccess) || appLoading}
113
121
  color="secondary"
114
- onClick={() => selectedAppSecret && props.onChange(selectedAppSecret)}
122
+ onClick={() => {
123
+ if (selectedAppSecret) {
124
+ props.onChange(selectedAppSecret);
125
+ } else if (!hasApiAccess) {
126
+ history.push('/apps/?createApp&apiId=' + apiId);
127
+ }
128
+ }}
115
129
  >
116
- {appLoading ? 'Loading...' : 'Use key'}
130
+ {appLoading ? 'Loading...' : hasApiAccess ? 'Use key' : 'Request access'}
117
131
  </UseKeyButton>
118
132
  </FormControl>
119
133
  </>
@@ -159,7 +173,7 @@ const FormLabel = styled.label<{ required?: boolean }>`
159
173
  `;
160
174
 
161
175
  const UseKeyButton = styled(Button)`
162
- width: 100px;
176
+ min-width: 100px;
163
177
  align-self: flex-end;
164
178
  margin-top: 10px;
165
179
  margin-right: 0;
@@ -1,8 +1,11 @@
1
1
  import styled from 'styled-components';
2
-
3
- export const FooterWrapper = styled.div.attrs(() => ({
4
- 'data-component-name': 'Sidebar/FooterWrapper',
5
- }))`
2
+ export const FooterWrapper = styled.div<{ collapsed?: boolean }>`
3
+ ${({ collapsed }) =>
4
+ collapsed &&
5
+ `
6
+ position: sticky;
7
+ bottom: 0;
8
+ `}
6
9
  margin: var(--sidebar-offset-top) 0 var(--sidebar-spacing-unit) var(--sidebar-offset-left);
7
10
  padding-top: var(--sidebar-spacing-unit);
8
11
  padding-right: var(--sidebar-spacing-unit);
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import { Sidebar } from '@theme/Sidebar/Sidebar';
@@ -13,26 +13,49 @@ import { useThemeConfig } from '@theme/hooks/useThemeConfig';
13
13
  import { MobileSidebarIcon } from './MobileSidebarIcon';
14
14
 
15
15
  interface SidebarLayoutProps {
16
- versions: React.ReactNode;
17
- menu: React.ReactNode;
16
+ versions?: React.ReactNode;
17
+ menu?: React.ReactNode;
18
18
  backLink?: {
19
19
  label: string;
20
20
  slug: string;
21
21
  };
22
22
  footer?: React.ReactNode;
23
+ actions?: React.ReactNode;
23
24
  header?: React.ReactNode;
24
25
  growContent?: boolean;
26
+ collapsed?: boolean;
27
+ onToggleMenu?: (isOpen: boolean) => void;
25
28
  }
26
29
 
30
+ const StyledFooterWrapper = styled(FooterWrapper)`
31
+ display: none;
32
+
33
+ ${({ theme }) => theme.mediaQueries?.medium} {
34
+ display: flex;
35
+ }
36
+ `;
37
+
27
38
  export function SidebarLayout({
28
39
  versions,
29
40
  menu,
30
41
  footer,
31
42
  header,
32
43
  growContent,
44
+ collapsed,
45
+ onToggleMenu,
33
46
  }: SidebarLayoutProps): JSX.Element | null {
34
47
  const [isOpen, setIsOpen] = useMobileMenu();
35
- const toggleMenu = () => setIsOpen(!isOpen);
48
+ const [mappedCollapsed, setMappedCollapsed] = React.useState(collapsed);
49
+
50
+ useEffect(() => {
51
+ setMappedCollapsed(collapsed && !isOpen);
52
+ }, [isOpen, collapsed, setMappedCollapsed]);
53
+
54
+ const toggleMenu = () => {
55
+ onToggleMenu?.(!isOpen);
56
+ setIsOpen(!isOpen);
57
+ };
58
+
36
59
  const { search, sidebar } = useThemeConfig();
37
60
 
38
61
  if (sidebar?.hide) {
@@ -40,20 +63,45 @@ export function SidebarLayout({
40
63
  }
41
64
 
42
65
  return (
43
- <Wrapper data-component-name="Sidebar/SidebarLayout">
44
- <MobileSidebarButton opened={isOpen} onClick={toggleMenu}>
45
- <MobileSidebarIcon />
46
- </MobileSidebarButton>
47
-
48
- {!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
49
- <Sidebar animate={true} opened={isOpen}>
50
- {header ? <HeaderWrapper>{header}</HeaderWrapper> : null}
51
- {versions}
52
- <MenuContainer growContent={growContent}>{menu}</MenuContainer>
53
- {footer ? <FooterWrapper>{footer}</FooterWrapper> : null}
54
- </Sidebar>
55
- </Wrapper>
66
+ <>
67
+ {mappedCollapsed ? (
68
+ footer ? (
69
+ <Wrapper collapsed={true}>
70
+ <StyledFooterWrapper data-component-name="Sidebar/FooterWrapper" collapsed={true}>
71
+ {footer}
72
+ </StyledFooterWrapper>
73
+ <MobileSidebarButton opened={isOpen} onClick={toggleMenu}>
74
+ <MobileSidebarIcon />
75
+ </MobileSidebarButton>
76
+ </Wrapper>
77
+ ) : null
78
+ ) : (
79
+ <Wrapper data-component-name="Sidebar/SidebarLayout">
80
+ <MobileSidebarButton opened={isOpen} onClick={toggleMenu}>
81
+ <MobileSidebarIcon />
82
+ </MobileSidebarButton>
83
+ {!search?.hide && search?.placement === 'sidebar' ? <SidebarSearch /> : null}
84
+ <Sidebar animate={true} opened={isOpen}>
85
+ {header ? <HeaderWrapper>{header}</HeaderWrapper> : null}
86
+ {versions ? <>{versions}</> : null}
87
+ <MenuContainer growContent={growContent}>{menu}</MenuContainer>
88
+ {footer && !isOpen ? (
89
+ <FooterWrapper data-component-name="Sidebar/FooterWrapper">{footer}</FooterWrapper>
90
+ ) : null}
91
+ </Sidebar>
92
+ </Wrapper>
93
+ )}
94
+ </>
56
95
  );
57
96
  }
58
97
 
59
- const Wrapper = styled.div``;
98
+ const Wrapper = styled.div<{ collapsed?: boolean }>`
99
+ ${({ collapsed }) =>
100
+ collapsed &&
101
+ `
102
+ display: flex;
103
+ flex-direction: column;
104
+ justify-content: flex-end;
105
+ border-right: 1px solid var(--sidebar-border-color);
106
+ `}
107
+ `;
@@ -13,6 +13,7 @@ export * from '@theme/Sidebar/MenuItemLabel';
13
13
  export * from '@theme/Sidebar/MenuLink';
14
14
  export * from '@theme/Sidebar/MenuLinkItem';
15
15
  export * from '@theme/Sidebar/MobileSidebarButton';
16
+ export * from '@theme/Sidebar/MobileSidebarIcon';
16
17
  export * from '@theme/Sidebar/Separator';
17
18
  export * from '@theme/Sidebar/SeparatorItem';
18
19
  export * from '@theme/Sidebar/Sidebar';
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+
3
+ import { LayoutVariant } from './SidebarActions';
4
+ import { StyledChangeViewButton, ChangeViewSvgRow, ChangeViewSvgColumn } from './styled';
5
+
6
+ interface ChangeViewButtonProps {
7
+ layout: LayoutVariant;
8
+ onClick: () => void;
9
+ }
10
+
11
+ export const ChangeViewButton = ({
12
+ layout,
13
+ onClick,
14
+ }: ChangeViewButtonProps): JSX.Element | null => {
15
+ return (
16
+ <StyledChangeViewButton title="Change layout" onClick={onClick}>
17
+ {layout === LayoutVariant.STACKED ? <ChangeViewSvgRow /> : <ChangeViewSvgColumn />}
18
+ </StyledChangeViewButton>
19
+ );
20
+ };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ import { StyledToggleRightPanelButton, HideLeftPanelSVG, ShowLeftPanelSVG } from './styled';
4
+
5
+ interface CollapseSidebarButtonProps {
6
+ initialValue: boolean;
7
+ onClick: () => void;
8
+ }
9
+ export const CollapseSidebarButton = ({
10
+ initialValue,
11
+ onClick,
12
+ }: CollapseSidebarButtonProps): JSX.Element => {
13
+ return (
14
+ <StyledToggleRightPanelButton
15
+ title={initialValue ? 'Show sidebar' : 'Hide sidebar'}
16
+ onClick={onClick}
17
+ >
18
+ {initialValue ? <HideLeftPanelSVG /> : <ShowLeftPanelSVG />}
19
+ </StyledToggleRightPanelButton>
20
+ );
21
+ };
@@ -0,0 +1,64 @@
1
+ import React from 'react';
2
+
3
+ import { ChangeViewButton } from './ChangeViewButton';
4
+ import { ToggleRightPanelButton } from './ToggleRightPanelButton';
5
+ import { CollapseSidebarButton } from './CollapseSidebarButton';
6
+ import { ControlsWrap, ControlsWrapChangeLayoutButtons } from './styled';
7
+
8
+ export enum LayoutVariant {
9
+ STACKED = 'stacked',
10
+ THREE_PANEL = 'three-panel',
11
+ }
12
+
13
+ interface SidebarActionsProps {
14
+ showChangeLayoutButton: boolean;
15
+ showRightPanelToggle: boolean;
16
+ layout: LayoutVariant;
17
+ initialShowRightPanelToggle: boolean;
18
+ collapsedSidebar: boolean;
19
+ isOpenapiDocs: boolean;
20
+ hideCollapseSidebarButton?: boolean;
21
+ onChangeRightPanelViewClick: () => void;
22
+ onChangeViewClick: () => void;
23
+ onChangeCollapseSidebarClick: () => void;
24
+ requestAccessButton?: React.ReactElement | null;
25
+ }
26
+
27
+ export const SidebarActions = ({
28
+ showChangeLayoutButton,
29
+ showRightPanelToggle,
30
+ layout,
31
+ initialShowRightPanelToggle,
32
+ hideCollapseSidebarButton = false,
33
+ collapsedSidebar,
34
+ isOpenapiDocs,
35
+ onChangeRightPanelViewClick,
36
+ onChangeViewClick,
37
+ onChangeCollapseSidebarClick,
38
+ requestAccessButton,
39
+ }: SidebarActionsProps) => {
40
+ return (
41
+ <ControlsWrap isOpenapiDocs={isOpenapiDocs} isCollapsed={collapsedSidebar}>
42
+ {isOpenapiDocs && (
43
+ <ControlsWrapChangeLayoutButtons isCollapsed={collapsedSidebar}>
44
+ {initialShowRightPanelToggle && (
45
+ <ToggleRightPanelButton
46
+ showRightPanelToggle={showRightPanelToggle}
47
+ onClick={onChangeRightPanelViewClick}
48
+ />
49
+ )}
50
+ {showChangeLayoutButton && showRightPanelToggle && (
51
+ <ChangeViewButton layout={layout} onClick={onChangeViewClick} />
52
+ )}
53
+ </ControlsWrapChangeLayoutButtons>
54
+ )}
55
+ {!collapsedSidebar && requestAccessButton}
56
+ {!hideCollapseSidebarButton && (
57
+ <CollapseSidebarButton
58
+ initialValue={collapsedSidebar}
59
+ onClick={onChangeCollapseSidebarClick}
60
+ />
61
+ )}
62
+ </ControlsWrap>
63
+ );
64
+ };
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+
3
+ import { StyledToggleRightPanelButton, HideRightPanelSVG, ShowRightPanelSVG } from './styled';
4
+
5
+ interface ToggleRightPanelButtonProps {
6
+ showRightPanelToggle: boolean;
7
+ onClick: () => void;
8
+ }
9
+ export const ToggleRightPanelButton = ({
10
+ showRightPanelToggle,
11
+ onClick,
12
+ }: ToggleRightPanelButtonProps): JSX.Element => {
13
+ return (
14
+ <StyledToggleRightPanelButton
15
+ title={showRightPanelToggle ? 'Hide samples' : 'Show samples'}
16
+ onClick={onClick}
17
+ >
18
+ {showRightPanelToggle ? <HideRightPanelSVG /> : <ShowRightPanelSVG />}
19
+ </StyledToggleRightPanelButton>
20
+ );
21
+ };
@@ -0,0 +1 @@
1
+ export { SidebarActions, LayoutVariant } from './SidebarActions';