@redocly/theme 0.68.0-next.4 → 0.68.0-next.6
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/components/Button/ShowMoreToggleButton.d.ts +11 -0
- package/lib/components/Button/ShowMoreToggleButton.js +21 -0
- package/lib/components/CodeBlock/CodeBlock.d.ts +7 -1
- package/lib/components/CodeBlock/CodeBlock.js +30 -7
- package/lib/components/Dropdown/Dropdown.d.ts +2 -0
- package/lib/components/Dropdown/Dropdown.js +59 -14
- package/lib/components/Dropdown/variables.js +3 -1
- package/lib/components/Feedback/Feedback.js +1 -3
- package/lib/components/Feedback/Stars.js +1 -1
- package/lib/components/Footer/FooterItem.js +1 -1
- package/lib/components/JsonViewer/Helpers.d.ts +1 -0
- package/lib/components/JsonViewer/Helpers.js +20 -7
- package/lib/components/JsonViewer/JsonViewer.d.ts +2 -0
- package/lib/components/JsonViewer/JsonViewer.js +2 -2
- package/lib/components/LanguagePicker/LanguagePicker.js +3 -2
- package/lib/components/Markdown/Markdown.js +13 -6
- package/lib/components/Markdown/styles/links.d.ts +1 -0
- package/lib/components/Markdown/styles/links.js +19 -16
- package/lib/components/Menu/MenuItem.d.ts +7 -1
- package/lib/components/Menu/MenuItem.js +43 -2
- package/lib/components/Navbar/NavbarItem.js +8 -3
- package/lib/components/PageActions/PageActions.js +1 -4
- package/lib/components/Panel/variables.js +1 -0
- package/lib/components/ReplayGate/ReplayGate.d.ts +12 -0
- package/lib/components/ReplayGate/ReplayGate.js +11 -0
- package/lib/components/ReplayTopBarActions/ReplayTopBarActions.d.ts +13 -0
- package/lib/components/ReplayTopBarActions/ReplayTopBarActions.js +7 -0
- package/lib/components/Search/SearchDialog.js +4 -4
- package/lib/core/hooks/use-active-section-id.d.ts +2 -0
- package/lib/core/hooks/use-active-section-id.js +11 -1
- package/lib/core/hooks/use-page-actions.js +10 -9
- package/lib/core/hooks/use-theme-hooks.js +1 -0
- package/lib/core/openapi/index.d.ts +1 -1
- package/lib/core/openapi/index.js +2 -1
- package/lib/core/styles/global.js +8 -0
- package/lib/core/types/hooks.d.ts +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/layouts/ThreePanelLayout.d.ts +5 -2
- package/package.json +3 -3
- package/src/components/Button/ShowMoreToggleButton.tsx +33 -0
- package/src/components/CodeBlock/CodeBlock.tsx +56 -12
- package/src/components/Dropdown/Dropdown.tsx +93 -21
- package/src/components/Dropdown/variables.ts +3 -1
- package/src/components/Feedback/Feedback.tsx +1 -3
- package/src/components/Feedback/Stars.tsx +1 -0
- package/src/components/Footer/FooterItem.tsx +3 -0
- package/src/components/JsonViewer/Helpers.tsx +41 -3
- package/src/components/JsonViewer/JsonViewer.tsx +4 -0
- package/src/components/LanguagePicker/LanguagePicker.tsx +3 -1
- package/src/components/Markdown/Markdown.tsx +13 -6
- package/src/components/Markdown/styles/links.ts +19 -15
- package/src/components/Menu/MenuItem.tsx +47 -2
- package/src/components/Navbar/NavbarItem.tsx +10 -4
- package/src/components/PageActions/PageActions.tsx +3 -6
- package/src/components/Panel/variables.ts +1 -0
- package/src/components/ReplayGate/ReplayGate.tsx +18 -0
- package/src/components/ReplayTopBarActions/ReplayTopBarActions.tsx +14 -0
- package/src/components/Search/SearchDialog.tsx +4 -4
- package/src/core/hooks/use-active-section-id.ts +13 -1
- package/src/core/hooks/use-page-actions.ts +13 -11
- package/src/core/hooks/use-theme-hooks.ts +1 -0
- package/src/core/openapi/index.ts +1 -1
- package/src/core/styles/global.ts +8 -0
- package/src/core/types/hooks.ts +1 -0
- package/src/core/types/l10n.ts +39 -1
- package/src/layouts/ThreePanelLayout.tsx +1 -0
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import React, {
|
|
2
|
+
cloneElement,
|
|
3
|
+
useRef,
|
|
4
|
+
forwardRef,
|
|
5
|
+
useCallback,
|
|
6
|
+
useEffect,
|
|
7
|
+
useLayoutEffect,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { createPortal } from 'react-dom';
|
|
11
|
+
import styled, { css } from 'styled-components';
|
|
12
|
+
|
|
13
|
+
import type { CSSProperties, PropsWithChildren, ReactElement } from 'react';
|
|
5
14
|
|
|
6
15
|
import { useOutsideClick, useControlledState } from '@redocly/theme/core/hooks';
|
|
7
16
|
import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
|
|
8
17
|
import { ChevronUpIcon } from '@redocly/theme/icons/ChevronUpIcon/ChevronUpIcon';
|
|
9
18
|
|
|
19
|
+
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
20
|
+
|
|
10
21
|
interface TriggerProps {
|
|
11
22
|
onClick?: (event: React.UIEvent) => void;
|
|
12
23
|
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
@@ -27,6 +38,7 @@ export type DropdownProps = PropsWithChildren<{
|
|
|
27
38
|
dataAttributes?: Record<string, string>;
|
|
28
39
|
className?: string;
|
|
29
40
|
withArrow?: boolean;
|
|
41
|
+
portalled?: boolean;
|
|
30
42
|
|
|
31
43
|
onClick?: (event: React.UIEvent) => void;
|
|
32
44
|
onClose?: () => void;
|
|
@@ -46,6 +58,7 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
|
|
|
46
58
|
dataAttributes,
|
|
47
59
|
placement,
|
|
48
60
|
alignment,
|
|
61
|
+
portalled = false,
|
|
49
62
|
onClick,
|
|
50
63
|
onClose,
|
|
51
64
|
onOpen,
|
|
@@ -53,7 +66,11 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
|
|
|
53
66
|
ref,
|
|
54
67
|
) => {
|
|
55
68
|
const dropdownRef = useRef<HTMLDivElement | null>(null);
|
|
69
|
+
const menuRef = useRef<HTMLDivElement | null>(null);
|
|
56
70
|
const [isOpen, setIsOpen] = useControlledState<boolean>(false, active);
|
|
71
|
+
const [menuStyle, setMenuStyle] = useState<CSSProperties>({});
|
|
72
|
+
|
|
73
|
+
const wrapperRef = (ref as React.RefObject<HTMLDivElement | null>) || dropdownRef;
|
|
57
74
|
|
|
58
75
|
const handleOpen = () => {
|
|
59
76
|
setIsOpen(true);
|
|
@@ -85,7 +102,40 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
|
|
|
85
102
|
}
|
|
86
103
|
};
|
|
87
104
|
|
|
88
|
-
|
|
105
|
+
const updateMenuPosition = useCallback(() => {
|
|
106
|
+
const wrapper = wrapperRef.current;
|
|
107
|
+
if (!wrapper) return;
|
|
108
|
+
const rect = wrapper.getBoundingClientRect();
|
|
109
|
+
const style: CSSProperties = { minWidth: rect.width };
|
|
110
|
+
if (placement === 'top') {
|
|
111
|
+
style.bottom = window.innerHeight - rect.top;
|
|
112
|
+
} else {
|
|
113
|
+
style.top = rect.bottom;
|
|
114
|
+
}
|
|
115
|
+
if (alignment === 'end') {
|
|
116
|
+
style.right = window.innerWidth - rect.right;
|
|
117
|
+
} else {
|
|
118
|
+
style.left = rect.left;
|
|
119
|
+
}
|
|
120
|
+
const zIndex = getComputedStyle(wrapper).getPropertyValue('--dropdown-menu-z-index').trim();
|
|
121
|
+
if (zIndex) {
|
|
122
|
+
style.zIndex = zIndex;
|
|
123
|
+
}
|
|
124
|
+
setMenuStyle(style);
|
|
125
|
+
}, [placement, alignment, wrapperRef]);
|
|
126
|
+
|
|
127
|
+
useIsomorphicLayoutEffect(() => {
|
|
128
|
+
if (!portalled || !isOpen) return;
|
|
129
|
+
updateMenuPosition();
|
|
130
|
+
window.addEventListener('scroll', updateMenuPosition, true);
|
|
131
|
+
window.addEventListener('resize', updateMenuPosition);
|
|
132
|
+
return () => {
|
|
133
|
+
window.removeEventListener('scroll', updateMenuPosition, true);
|
|
134
|
+
window.removeEventListener('resize', updateMenuPosition);
|
|
135
|
+
};
|
|
136
|
+
}, [portalled, isOpen, updateMenuPosition]);
|
|
137
|
+
|
|
138
|
+
useOutsideClick(portalled ? [wrapperRef, menuRef] : wrapperRef, handleClose);
|
|
89
139
|
|
|
90
140
|
const triggerChild = React.Children.only(trigger) as ReactElement<TriggerProps>;
|
|
91
141
|
|
|
@@ -97,27 +147,37 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
|
|
|
97
147
|
onKeyDown: triggerEvent === 'click' ? handleKeyDown : undefined,
|
|
98
148
|
});
|
|
99
149
|
|
|
150
|
+
const menu = (
|
|
151
|
+
<ChildrenWrapper
|
|
152
|
+
ref={menuRef}
|
|
153
|
+
$placement={placement}
|
|
154
|
+
$alignment={alignment}
|
|
155
|
+
$isOpen={isOpen}
|
|
156
|
+
$portalled={portalled}
|
|
157
|
+
style={portalled ? menuStyle : undefined}
|
|
158
|
+
onClick={closeOnClick ? handleChildClick : undefined}
|
|
159
|
+
>
|
|
160
|
+
{children}
|
|
161
|
+
</ChildrenWrapper>
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const portalledMenu =
|
|
165
|
+
isOpen && typeof document !== 'undefined' ? createPortal(menu, document.body) : null;
|
|
166
|
+
|
|
100
167
|
return (
|
|
101
168
|
<DropdownWrapper
|
|
102
169
|
data-component-name="Dropdown/Dropdown"
|
|
103
170
|
data-testid="dropdown"
|
|
104
171
|
{...dataAttributes}
|
|
105
172
|
className={className}
|
|
106
|
-
ref={
|
|
173
|
+
ref={wrapperRef}
|
|
107
174
|
onPointerEnter={triggerEvent === 'hover' ? handleOpen : undefined}
|
|
108
175
|
onPointerLeave={triggerEvent === 'hover' ? handleClose : undefined}
|
|
109
176
|
onClick={onClick}
|
|
110
177
|
>
|
|
111
178
|
{dropdownTrigger}
|
|
112
179
|
|
|
113
|
-
|
|
114
|
-
$placement={placement}
|
|
115
|
-
$alignment={alignment}
|
|
116
|
-
$isOpen={isOpen}
|
|
117
|
-
onClick={closeOnClick ? handleChildClick : undefined}
|
|
118
|
-
>
|
|
119
|
-
{children}
|
|
120
|
-
</ChildrenWrapper>
|
|
180
|
+
{portalled ? portalledMenu : menu}
|
|
121
181
|
</DropdownWrapper>
|
|
122
182
|
);
|
|
123
183
|
},
|
|
@@ -140,14 +200,26 @@ const DropdownWrapper = styled.div`
|
|
|
140
200
|
text-decoration: none;
|
|
141
201
|
`;
|
|
142
202
|
|
|
143
|
-
const ChildrenWrapper = styled.div<{
|
|
203
|
+
const ChildrenWrapper = styled.div<{
|
|
204
|
+
$placement?: string;
|
|
205
|
+
$alignment?: string;
|
|
206
|
+
$isOpen?: boolean;
|
|
207
|
+
$portalled?: boolean;
|
|
208
|
+
}>`
|
|
144
209
|
padding-top: var(--dropdown-menu-padding-top);
|
|
145
|
-
position: absolute;
|
|
146
|
-
top: ${({ $placement }) => ($placement === 'top' ? 'auto' : '100%')};
|
|
147
|
-
bottom: ${({ $placement }) => ($placement === 'top' ? '100%' : 'auto')};
|
|
148
|
-
left: ${({ $alignment }) => ($alignment === 'start' ? '0' : 'auto')};
|
|
149
|
-
right: ${({ $alignment }) => ($alignment === 'end' ? '0' : 'auto')};
|
|
150
210
|
display: ${({ $isOpen }) => ($isOpen ? 'block' : 'none')};
|
|
151
|
-
|
|
152
211
|
z-index: var(--dropdown-menu-z-index);
|
|
212
|
+
|
|
213
|
+
${({ $portalled, $placement, $alignment }) =>
|
|
214
|
+
$portalled
|
|
215
|
+
? css`
|
|
216
|
+
position: fixed;
|
|
217
|
+
`
|
|
218
|
+
: css`
|
|
219
|
+
position: absolute;
|
|
220
|
+
top: ${$placement === 'top' ? 'auto' : '100%'};
|
|
221
|
+
bottom: ${$placement === 'top' ? '100%' : 'auto'};
|
|
222
|
+
left: ${$alignment === 'start' ? '0' : 'auto'};
|
|
223
|
+
right: ${$alignment === 'end' ? '0' : 'auto'};
|
|
224
|
+
`}
|
|
153
225
|
`;
|
|
@@ -8,7 +8,9 @@ export const dropdown = css`
|
|
|
8
8
|
--dropdown-menu-font-weight: var(--font-weight-regular); // @presenter FontWeight
|
|
9
9
|
--dropdown-menu-line-height: var(--line-height-base); // @presenter LineHeight
|
|
10
10
|
--dropdown-menu-text-color: var(--text-color-secondary); // @presenter Color
|
|
11
|
-
|
|
11
|
+
/* One below --z-index-raised (the sticky Navbar) so a portaled menu, which is
|
|
12
|
+
appended to <body> after the navbar, never paints over it on a z-index tie. */
|
|
13
|
+
--dropdown-menu-z-index: calc(var(--z-index-raised) - 1); // @presenter ZIndex
|
|
12
14
|
|
|
13
15
|
--dropdown-menu-padding-top: var(--spacing-xxs);
|
|
14
16
|
--dropdown-menu-min-width: 100px;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { useLocation } from 'react-router-dom';
|
|
3
2
|
import styled from 'styled-components';
|
|
4
3
|
|
|
5
4
|
import type { FeedbackProps } from '@redocly/theme/core/types';
|
|
@@ -28,7 +27,6 @@ export function Feedback(props: FeedbackProps & { path?: string }) {
|
|
|
28
27
|
const { useSubmitFeedback, useTelemetry } = useThemeHooks();
|
|
29
28
|
const { submitFeedback } = useSubmitFeedback();
|
|
30
29
|
const telemetry = useTelemetry();
|
|
31
|
-
const { pathname } = useLocation();
|
|
32
30
|
const { feedback: themeFeedbackConf } = useThemeConfig();
|
|
33
31
|
const feedbackConf = {
|
|
34
32
|
...themeFeedbackConf,
|
|
@@ -75,7 +73,7 @@ export function Feedback(props: FeedbackProps & { path?: string }) {
|
|
|
75
73
|
);
|
|
76
74
|
};
|
|
77
75
|
|
|
78
|
-
return <React.Fragment key={
|
|
76
|
+
return <React.Fragment key={feedbackConf.path}>{renderFeedbackComponent()}</React.Fragment>;
|
|
79
77
|
}
|
|
80
78
|
|
|
81
79
|
const FeedbackWrapper = styled.div`
|
|
@@ -22,6 +22,7 @@ export function Stars({ max = 5, onChange, value }: StarsProps): JSX.Element {
|
|
|
22
22
|
<Star
|
|
23
23
|
data-testid={`star-${index}`}
|
|
24
24
|
key={index}
|
|
25
|
+
aria-label={`${index} ${index === 1 ? 'star' : 'stars'}`}
|
|
25
26
|
onClick={() => onChange(index)}
|
|
26
27
|
onMouseOver={() => setHovered(index)}
|
|
27
28
|
onMouseLeave={() => !value && setHovered(0)}
|
|
@@ -48,6 +48,9 @@ export function FooterItem({ item, iconsOnly, className }: FooterItemProps): JSX
|
|
|
48
48
|
telemetry.sendFooterItemClickedMessage([getBaseDataAttributes('footerItem', 'footer')])
|
|
49
49
|
}
|
|
50
50
|
data-translation-key={item.labelTranslationKey}
|
|
51
|
+
aria-label={
|
|
52
|
+
iconWithoutLabel ? translate(item.labelTranslationKey, item.label) : undefined
|
|
53
|
+
}
|
|
51
54
|
>
|
|
52
55
|
{hasIcon ? (
|
|
53
56
|
<FooterLinkIcon $iconsOnly={iconsOnly}>
|
|
@@ -4,6 +4,7 @@ import styled from 'styled-components';
|
|
|
4
4
|
import type { JSX } from 'react';
|
|
5
5
|
|
|
6
6
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
7
|
+
import { ShowMoreToggleButton } from '@redocly/theme/components/Button/ShowMoreToggleButton';
|
|
7
8
|
import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
|
|
8
9
|
|
|
9
10
|
export function JsonValue(props: {
|
|
@@ -13,8 +14,9 @@ export function JsonValue(props: {
|
|
|
13
14
|
standalone?: boolean; // if true, will render the opening and closing punctuation (when it's an item inside array or top-level)
|
|
14
15
|
defaultExpandLevel: number;
|
|
15
16
|
expandAllSignal: boolean | undefined;
|
|
17
|
+
maxStringLength?: number;
|
|
16
18
|
}): React.ReactNode {
|
|
17
|
-
const { standalone, defaultExpandLevel, level, value, expandAllSignal } = props;
|
|
19
|
+
const { standalone, defaultExpandLevel, level, value, expandAllSignal, maxStringLength } = props;
|
|
18
20
|
const indent = ' '.repeat(level * 2);
|
|
19
21
|
|
|
20
22
|
const [isExpandedState, setExpanded] = React.useState(
|
|
@@ -37,10 +39,16 @@ export function JsonValue(props: {
|
|
|
37
39
|
const isPrimitive = (valueType !== 'object' && value !== null) || keys.length === 0;
|
|
38
40
|
|
|
39
41
|
if (isPrimitive) {
|
|
42
|
+
const isOversizedString =
|
|
43
|
+
typeof value === 'string' && maxStringLength !== undefined && value.length > maxStringLength;
|
|
40
44
|
return (
|
|
41
45
|
<>
|
|
42
46
|
{standalone ? indent : null}
|
|
43
|
-
|
|
47
|
+
{isOversizedString ? (
|
|
48
|
+
<TruncatedStringValue value={value} maxStringLength={maxStringLength} />
|
|
49
|
+
) : (
|
|
50
|
+
<span className={'token ' + valueType}>{JSON.stringify(value)}</span>
|
|
51
|
+
)}
|
|
44
52
|
</>
|
|
45
53
|
);
|
|
46
54
|
}
|
|
@@ -57,6 +65,7 @@ export function JsonValue(props: {
|
|
|
57
65
|
standalone={true}
|
|
58
66
|
defaultExpandLevel={defaultExpandLevel}
|
|
59
67
|
expandAllSignal={expandAllSignal}
|
|
68
|
+
maxStringLength={maxStringLength}
|
|
60
69
|
/>
|
|
61
70
|
{index < value.length - 1 ? ',\n' : null}
|
|
62
71
|
</React.Fragment>
|
|
@@ -69,6 +78,7 @@ export function JsonValue(props: {
|
|
|
69
78
|
level={level + 1}
|
|
70
79
|
defaultExpandLevel={defaultExpandLevel}
|
|
71
80
|
expandAllSignal={expandAllSignal}
|
|
81
|
+
maxStringLength={maxStringLength}
|
|
72
82
|
/>
|
|
73
83
|
{index < keys.length - 1 ? ',\n' : null}
|
|
74
84
|
</React.Fragment>
|
|
@@ -109,8 +119,9 @@ function JsonKeyValue(props: {
|
|
|
109
119
|
level: number;
|
|
110
120
|
defaultExpandLevel: number;
|
|
111
121
|
expandAllSignal: boolean | undefined;
|
|
122
|
+
maxStringLength?: number;
|
|
112
123
|
}): React.ReactNode {
|
|
113
|
-
const { defaultExpandLevel, level, name, value, expandAllSignal } = props;
|
|
124
|
+
const { defaultExpandLevel, level, name, value, expandAllSignal, maxStringLength } = props;
|
|
114
125
|
const indent = ' '.repeat(level * 2);
|
|
115
126
|
const keys: string[] =
|
|
116
127
|
(value && Object.keys(value).filter((key) => value[key] !== undefined)) || []; // filter out undefined properties
|
|
@@ -141,6 +152,7 @@ function JsonKeyValue(props: {
|
|
|
141
152
|
level={level}
|
|
142
153
|
defaultExpandLevel={defaultExpandLevel}
|
|
143
154
|
expandAllSignal={expandAllSignal}
|
|
155
|
+
maxStringLength={maxStringLength}
|
|
144
156
|
/>
|
|
145
157
|
{'\n'}
|
|
146
158
|
</>
|
|
@@ -156,6 +168,7 @@ function JsonKeyValue(props: {
|
|
|
156
168
|
level={level}
|
|
157
169
|
defaultExpandLevel={defaultExpandLevel}
|
|
158
170
|
expandAllSignal={expandAllSignal}
|
|
171
|
+
maxStringLength={maxStringLength}
|
|
159
172
|
/>
|
|
160
173
|
);
|
|
161
174
|
|
|
@@ -170,6 +183,27 @@ function JsonKeyValue(props: {
|
|
|
170
183
|
);
|
|
171
184
|
}
|
|
172
185
|
|
|
186
|
+
function TruncatedStringValue({
|
|
187
|
+
value,
|
|
188
|
+
maxStringLength,
|
|
189
|
+
}: {
|
|
190
|
+
value: string;
|
|
191
|
+
maxStringLength: number;
|
|
192
|
+
}): JSX.Element {
|
|
193
|
+
const [isExpanded, setExpanded] = React.useState(false);
|
|
194
|
+
const shownValue = React.useMemo(
|
|
195
|
+
() => JSON.stringify(isExpanded ? value : `${value.slice(0, maxStringLength)}…`),
|
|
196
|
+
[isExpanded, value, maxStringLength],
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
return (
|
|
200
|
+
<>
|
|
201
|
+
<span className="token string">{shownValue}</span>{' '}
|
|
202
|
+
<ShowMoreButton isExpanded={isExpanded} onToggle={() => setExpanded((v) => !v)} />
|
|
203
|
+
</>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
173
207
|
function ExpandIcon({
|
|
174
208
|
isExpanded,
|
|
175
209
|
onClick,
|
|
@@ -189,6 +223,10 @@ function ExpandIcon({
|
|
|
189
223
|
);
|
|
190
224
|
}
|
|
191
225
|
|
|
226
|
+
const ShowMoreButton = styled(ShowMoreToggleButton)`
|
|
227
|
+
user-select: none;
|
|
228
|
+
`;
|
|
229
|
+
|
|
192
230
|
const ExpandButton = styled(Button)`
|
|
193
231
|
position: absolute;
|
|
194
232
|
left: -10px;
|
|
@@ -17,6 +17,8 @@ export type JsonProps = {
|
|
|
17
17
|
className?: string;
|
|
18
18
|
expandLevel: number;
|
|
19
19
|
startLineNumber?: number;
|
|
20
|
+
/** Truncate string values longer than this at render time (full value stays available for copy). */
|
|
21
|
+
maxStringLength?: number;
|
|
20
22
|
onCopyClick?: () => void;
|
|
21
23
|
onPanelToggle?: (isExpanded: boolean, panelType?: PanelType) => void;
|
|
22
24
|
};
|
|
@@ -29,6 +31,7 @@ function JsonComponent({
|
|
|
29
31
|
onPanelToggle,
|
|
30
32
|
title,
|
|
31
33
|
controls = {},
|
|
34
|
+
maxStringLength,
|
|
32
35
|
}: JsonProps): JSX.Element {
|
|
33
36
|
const showFoldingButtons =
|
|
34
37
|
data && Object.values(data).some((value) => typeof value === 'object' && value !== null);
|
|
@@ -89,6 +92,7 @@ function JsonComponent({
|
|
|
89
92
|
standalone={true}
|
|
90
93
|
expandAllSignal={expandAllSignal}
|
|
91
94
|
defaultExpandLevel={Math.max(1, expandLevel)}
|
|
95
|
+
maxStringLength={maxStringLength}
|
|
92
96
|
/>
|
|
93
97
|
</FoldingWrap>
|
|
94
98
|
</CodeBlock>
|
|
@@ -20,8 +20,9 @@ export type LanguagePickerProps = {
|
|
|
20
20
|
|
|
21
21
|
export function LanguagePicker(props: LanguagePickerProps): JSX.Element | null {
|
|
22
22
|
const { currentLocale, locales, getLocaleUrl } = useLanguagePicker();
|
|
23
|
-
const { useTelemetry } = useThemeHooks();
|
|
23
|
+
const { useTelemetry, useTranslate } = useThemeHooks();
|
|
24
24
|
const telemetry = useTelemetry();
|
|
25
|
+
const { translate } = useTranslate();
|
|
25
26
|
|
|
26
27
|
if (locales.length < 2 || !currentLocale) {
|
|
27
28
|
return null;
|
|
@@ -32,6 +33,7 @@ export function LanguagePicker(props: LanguagePickerProps): JSX.Element | null {
|
|
|
32
33
|
icon={<GlobalOutlinedIcon color="--button-content-color" />}
|
|
33
34
|
variant="secondary"
|
|
34
35
|
size="medium"
|
|
36
|
+
aria-label={translate('languagePicker.label', 'Select language')}
|
|
35
37
|
/>
|
|
36
38
|
);
|
|
37
39
|
|
|
@@ -38,6 +38,7 @@ export const Markdown = styled.main
|
|
|
38
38
|
line-height: var(--md-content-line-height);
|
|
39
39
|
color: var(--md-content-text-color);
|
|
40
40
|
padding: 0;
|
|
41
|
+
overflow-wrap: break-word;
|
|
41
42
|
|
|
42
43
|
${({ compact }) =>
|
|
43
44
|
compact &&
|
|
@@ -85,7 +86,12 @@ export const Markdown = styled.main
|
|
|
85
86
|
scroll-margin-top: calc(var(--navbar-height) + var(--banner-height));
|
|
86
87
|
${headingAnchorCss(ANCHOR_CLASS)};
|
|
87
88
|
|
|
88
|
-
h1,
|
|
89
|
+
h1,
|
|
90
|
+
h2,
|
|
91
|
+
h3,
|
|
92
|
+
h4,
|
|
93
|
+
h5,
|
|
94
|
+
h6 {
|
|
89
95
|
display: flex;
|
|
90
96
|
align-items: flex-start;
|
|
91
97
|
gap: var(--spacing-sm);
|
|
@@ -294,8 +300,10 @@ export const Markdown = styled.main
|
|
|
294
300
|
}
|
|
295
301
|
|
|
296
302
|
blockquote {
|
|
297
|
-
margin: var(--md-blockquote-margin-vertical)
|
|
298
|
-
|
|
303
|
+
margin: var(--md-blockquote-margin-vertical)
|
|
304
|
+
var(--md-blockquote-margin-horizontal);
|
|
305
|
+
padding: var(--md-blockquote-padding-vertical)
|
|
306
|
+
var(--md-blockquote-padding-horizontal);
|
|
299
307
|
border-left: var(--md-blockquote-border-left);
|
|
300
308
|
background-color: var(--md-blockquote-bg-color);
|
|
301
309
|
color: var(--md-blockquote-text-color);
|
|
@@ -323,8 +331,7 @@ export const Markdown = styled.main
|
|
|
323
331
|
}
|
|
324
332
|
}
|
|
325
333
|
|
|
326
|
-
ul.${MARKDOWN_CLASS_NAME},
|
|
327
|
-
ol.${MARKDOWN_CLASS_NAME} {
|
|
334
|
+
ul.${MARKDOWN_CLASS_NAME}, ol.${MARKDOWN_CLASS_NAME} {
|
|
328
335
|
padding-left: var(--md-list-left-padding);
|
|
329
336
|
margin: var(--md-list-margin);
|
|
330
337
|
|
|
@@ -347,7 +354,7 @@ export const Markdown = styled.main
|
|
|
347
354
|
}
|
|
348
355
|
}
|
|
349
356
|
|
|
350
|
-
ul > li:has(input[type=
|
|
357
|
+
ul > li:has(input[type="checkbox"]) {
|
|
351
358
|
list-style: var(--md-checkbox-list-item-style);
|
|
352
359
|
}
|
|
353
360
|
|
|
@@ -1,21 +1,25 @@
|
|
|
1
1
|
import { css } from 'styled-components';
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
export const linkCss = css`
|
|
4
|
+
text-decoration: var(--link-decoration);
|
|
5
|
+
text-underline-offset: var(--link-underline-offset);
|
|
6
|
+
color: var(--link-color-primary);
|
|
7
|
+
font-weight: var(--link-font-weight);
|
|
8
|
+
|
|
9
|
+
&:visited {
|
|
10
|
+
color: var(--link-color-visited);
|
|
11
|
+
text-decoration: var(--link-decoration-visited);
|
|
12
|
+
}
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
&:hover,
|
|
15
|
+
&:focus {
|
|
16
|
+
text-decoration: var(--link-decoration-hover);
|
|
17
|
+
color: var(--link-color-primary-hover);
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
14
20
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
color: var(--link-color-primary-hover);
|
|
19
|
-
}
|
|
21
|
+
export const markdownLinksCss = css`
|
|
22
|
+
a:not([role='button']) {
|
|
23
|
+
${linkCss}
|
|
20
24
|
}
|
|
21
25
|
`;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useRef } from 'react';
|
|
1
|
+
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import styled, { css } from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import type { ItemState, MenuItemProps } from '@redocly/theme/core/types';
|
|
@@ -20,7 +20,20 @@ import { ArrowRightIcon } from '@redocly/theme/icons/ArrowRightIcon/ArrowRightIc
|
|
|
20
20
|
import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
|
|
21
21
|
import { Tag, ContentWrapper } from '@redocly/theme/components/Tag/Tag';
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
/** Nearest scrollable ancestor, stopping before <body> so only the sidebar scrolls — never the page. */
|
|
24
|
+
function getScrollParent(el: HTMLElement): HTMLElement | null {
|
|
25
|
+
let node = el.parentElement;
|
|
26
|
+
while (node && node !== document.body) {
|
|
27
|
+
const { overflowY } = getComputedStyle(node);
|
|
28
|
+
if ((overflowY === 'auto' || overflowY === 'scroll') && node.scrollHeight > node.clientHeight) {
|
|
29
|
+
return node;
|
|
30
|
+
}
|
|
31
|
+
node = node.parentElement;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function MenuItemImpl(props: React.PropsWithChildren<MenuItemProps>): JSX.Element {
|
|
24
37
|
const { item, depth, className, onClick } = props;
|
|
25
38
|
const { useTranslate, useTelemetry } = useThemeHooks();
|
|
26
39
|
const { translate } = useTranslate();
|
|
@@ -40,6 +53,25 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
|
|
|
40
53
|
const hasChevron = isNested && !isDrilldown;
|
|
41
54
|
const hasHttpTag = !!item.httpVerb || type === MenuItemType.Operation;
|
|
42
55
|
|
|
56
|
+
// When this item becomes the active one (via navigation or scroll-spy), keep
|
|
57
|
+
// it visible by scrolling only the sidebar's own scroll container.
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const el = labelRef.current;
|
|
60
|
+
if (!item.active || !el) return;
|
|
61
|
+
const scroller = getScrollParent(el);
|
|
62
|
+
if (!scroller) return;
|
|
63
|
+
|
|
64
|
+
const scrollerRect = scroller.getBoundingClientRect();
|
|
65
|
+
const elRect = el.getBoundingClientRect();
|
|
66
|
+
const margin = elRect.height;
|
|
67
|
+
|
|
68
|
+
if (elRect.top < scrollerRect.top + margin) {
|
|
69
|
+
scroller.scrollTop -= scrollerRect.top + margin - elRect.top;
|
|
70
|
+
} else if (elRect.bottom > scrollerRect.bottom - margin) {
|
|
71
|
+
scroller.scrollTop += elRect.bottom - (scrollerRect.bottom - margin);
|
|
72
|
+
}
|
|
73
|
+
}, [item.active]);
|
|
74
|
+
|
|
43
75
|
const handleOnClick = () => {
|
|
44
76
|
telemetry.sendSidebarItemClickedMessage([
|
|
45
77
|
{
|
|
@@ -448,3 +480,16 @@ const BadgeIcon = styled(GenericIcon)`
|
|
|
448
480
|
margin-right: var(--spacing-xxs);
|
|
449
481
|
flex-shrink: 0;
|
|
450
482
|
`;
|
|
483
|
+
|
|
484
|
+
/** Compares only the props that actually affect this row. `item` is expected
|
|
485
|
+
* to be reference-stable from the producer (e.g. `useMenuItems`) when the
|
|
486
|
+
* row's state hasn't changed; `onClick` and `children` are fresh JSX from
|
|
487
|
+
* the parent's `.map()` every render and would defeat the memo. */
|
|
488
|
+
export const MenuItem = React.memo(MenuItemImpl, (prev, next) => {
|
|
489
|
+
return (
|
|
490
|
+
prev.item === next.item &&
|
|
491
|
+
prev.depth === next.depth &&
|
|
492
|
+
prev.isMobile === next.isMobile &&
|
|
493
|
+
prev.className === next.className
|
|
494
|
+
);
|
|
495
|
+
});
|
|
@@ -92,15 +92,15 @@ export function NavbarItem({ navItem, className }: NavbarItemProps): JSX.Element
|
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
return (
|
|
95
|
-
<
|
|
95
|
+
<NavbarListItem data-component-name="Navbar/NavbarItem">
|
|
96
96
|
<NavbarMenuItemDropdown trigger={itemContent} triggerEvent="hover">
|
|
97
97
|
<DropdownMenu>{groupItemsComponents}</DropdownMenu>
|
|
98
98
|
</NavbarMenuItemDropdown>
|
|
99
|
-
</
|
|
99
|
+
</NavbarListItem>
|
|
100
100
|
);
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
return <
|
|
103
|
+
return <NavbarListItem data-component-name="Navbar/NavbarItem">{itemContent}</NavbarListItem>;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const NavbarMenuItemDropdown = styled(Dropdown)`
|
|
@@ -109,7 +109,13 @@ const NavbarMenuItemDropdown = styled(Dropdown)`
|
|
|
109
109
|
--dropdown-menu-item-separator-line-height: var(--navbar-separator-line-height);
|
|
110
110
|
`;
|
|
111
111
|
|
|
112
|
-
const
|
|
112
|
+
const NavbarListItem = styled.li`
|
|
113
|
+
list-style: none;
|
|
114
|
+
display: inline-flex;
|
|
115
|
+
align-items: center;
|
|
116
|
+
`;
|
|
117
|
+
|
|
118
|
+
const NavbarMenuItem = styled.div<LinkProps & { $active: boolean }>`
|
|
113
119
|
display: inline-block;
|
|
114
120
|
padding: var(--navbar-item-padding);
|
|
115
121
|
text-align: center;
|
|
@@ -83,14 +83,15 @@ export function PageActions(props: PageActionProps): JSX.Element | null {
|
|
|
83
83
|
{buttonAction.buttonText}
|
|
84
84
|
</Button>
|
|
85
85
|
{actions.length > 1 ? (
|
|
86
|
-
<
|
|
86
|
+
<Dropdown
|
|
87
87
|
withArrow
|
|
88
|
+
portalled
|
|
88
89
|
trigger={<Button aria-label="More actions" />}
|
|
89
90
|
placement="bottom"
|
|
90
91
|
alignment="end"
|
|
91
92
|
>
|
|
92
93
|
<StyledDropdownMenu>{menuItems}</StyledDropdownMenu>
|
|
93
|
-
</
|
|
94
|
+
</Dropdown>
|
|
94
95
|
) : null}
|
|
95
96
|
</ButtonGroup>
|
|
96
97
|
</PageActionsWrapper>
|
|
@@ -124,10 +125,6 @@ const LinkMenuItem = styled(Link)`
|
|
|
124
125
|
--link-decoration-hover: none;
|
|
125
126
|
`;
|
|
126
127
|
|
|
127
|
-
const StyledDropdown = styled(Dropdown)`
|
|
128
|
-
--dropdown-menu-z-index: calc(var(--z-index-raised) - 1);
|
|
129
|
-
`;
|
|
130
|
-
|
|
131
128
|
const StyledDropdownMenu = styled(DropdownMenu)`
|
|
132
129
|
--dropdown-menu-max-height: var(--page-actions-dropdown-max-height);
|
|
133
130
|
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
export type ReplayGateProps = {
|
|
4
|
+
setEnvironmentValues: (
|
|
5
|
+
envName: string,
|
|
6
|
+
values: Record<string, string | { value: string; isSecret?: boolean }>,
|
|
7
|
+
) => void;
|
|
8
|
+
setSelectedEnvironment: (envName: string) => void;
|
|
9
|
+
selectedEnvironment?: string;
|
|
10
|
+
// The current OpenAPI description's id (from x-metadata), when known. Lets a custom
|
|
11
|
+
// ReplayGate vary behavior per API in a multi-API catalog.
|
|
12
|
+
apiId?: string;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function ReplayGate({ children }: ReplayGateProps) {
|
|
17
|
+
return <>{children}</>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ReplayTopBarActionsProps = {
|
|
2
|
+
environments: ReadonlyArray<{ name: string }>;
|
|
3
|
+
activeEnvironment: string;
|
|
4
|
+
setEnvironmentValues: (
|
|
5
|
+
envName: string,
|
|
6
|
+
values: Record<string, string | { value: string; isSecret?: boolean }>,
|
|
7
|
+
) => void;
|
|
8
|
+
setSelectedEnvironment: (envName: string) => void;
|
|
9
|
+
apiId?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export function ReplayTopBarActions(_props: ReplayTopBarActionsProps) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|