@redocly/theme 0.58.0-next.1 → 0.58.0-next.10
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/Catalog/Catalog.d.ts +2 -2
- package/lib/components/Catalog/Catalog.js +6 -4
- package/lib/components/Catalog/CatalogCardView/CatalogCard.js +15 -14
- package/lib/components/Catalog/CatalogEntity/CatalogEntity.d.ts +5 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +4 -4
- package/lib/components/Catalog/CatalogEntity/CatalogEntityLinks.js +0 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityMetadata.js +1 -2
- package/lib/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.js +0 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityPropertyCard.js +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.js +1 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.d.ts +5 -1
- package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +9 -7
- package/lib/components/Catalog/CatalogEntityIcon.d.ts +2 -0
- package/lib/components/Catalog/CatalogEntityIcon.js +31 -14
- package/lib/components/Catalog/CatalogEntityTypeIcon.js +19 -6
- package/lib/components/Catalog/CatalogEntityTypeTag.js +9 -3
- package/lib/components/Catalog/CatalogSelector.d.ts +1 -1
- package/lib/components/Catalog/CatalogTableView/CatalogEntityCell.js +1 -1
- package/lib/components/Catalog/CatalogViewModeToggle.d.ts +1 -1
- package/lib/components/Catalog/variables.js +9 -6
- package/lib/components/CatalogClassic/CatalogClassic.js +9 -2
- package/lib/components/CodeBlock/CodeBlock.d.ts +5 -12
- package/lib/components/CodeBlock/CodeBlockControls.d.ts +3 -3
- package/lib/components/CodeBlock/CodeBlockDropdown.d.ts +2 -2
- package/lib/components/CodeBlock/CodeBlockDropdown.js +4 -13
- package/lib/components/CodeBlock/CodeBlockTabs.d.ts +2 -2
- package/lib/components/CodeBlock/CodeBlockTabs.js +4 -3
- package/lib/components/Search/SearchDialog.js +12 -6
- package/lib/components/Search/SearchFilter.js +2 -1
- package/lib/components/Tooltip/Tooltip.js +7 -9
- package/lib/components/Tooltip/TooltipWrapper.js +1 -1
- package/lib/core/constants/catalog.d.ts +1 -1
- package/lib/core/constants/catalog.js +13 -27
- package/lib/core/contexts/CodeSnippetContext.d.ts +14 -6
- package/lib/core/contexts/CodeSnippetContext.js +57 -14
- package/lib/core/hooks/catalog/useCatalogTableViewRow.js +1 -1
- package/lib/core/hooks/use-active-section-id.js +4 -0
- package/lib/core/hooks/use-codeblock-tabs-controls.d.ts +2 -2
- package/lib/core/hooks/use-control.js +17 -2
- package/lib/core/hooks/use-local-state.js +22 -18
- package/lib/core/hooks/use-telemetry-fallback.d.ts +5 -0
- package/lib/core/hooks/use-telemetry-fallback.js +5 -0
- package/lib/core/openapi/index.d.ts +8 -4
- package/lib/core/openapi/index.js +9 -9
- package/lib/core/styles/global.js +19 -0
- package/lib/core/types/catalog.d.ts +1 -1
- package/lib/core/types/hooks.d.ts +2 -2
- package/lib/core/types/index.d.ts +1 -0
- package/lib/core/types/index.js +1 -0
- package/lib/core/types/l10n.d.ts +1 -1
- package/lib/core/types/open-api-info.d.ts +34 -0
- package/lib/core/types/open-api-info.js +3 -0
- package/lib/core/types/open-api-server.d.ts +1 -0
- package/lib/core/types/search.d.ts +2 -3
- package/lib/core/utils/urls.js +1 -1
- package/lib/ext/useConfigureReplay.d.ts +2 -1
- package/lib/icons/HierarchyIcon/HierarchyIcon.d.ts +9 -0
- package/lib/icons/HierarchyIcon/HierarchyIcon.js +23 -0
- package/lib/icons/NoteIcon/NoteIcon.d.ts +9 -0
- package/lib/icons/NoteIcon/NoteIcon.js +24 -0
- package/lib/icons/ShareIcon/ShareIcon.d.ts +9 -0
- package/lib/icons/ShareIcon/ShareIcon.js +22 -0
- package/lib/index.d.ts +2 -0
- package/lib/index.js +2 -0
- package/lib/layouts/DocumentationLayout.js +1 -3
- package/lib/markdoc/components/CodeGroup/CodeGroup.js +49 -27
- package/lib/markdoc/components/Tabs/Tabs.d.ts +2 -1
- package/lib/markdoc/components/Tabs/Tabs.js +3 -2
- package/package.json +4 -4
- package/src/components/Catalog/Catalog.tsx +18 -6
- package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +20 -19
- package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +15 -2
- package/src/components/Catalog/CatalogEntity/CatalogEntityLinks.tsx +0 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntityMetadata.tsx +1 -2
- package/src/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.tsx +0 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityPropertyCard.tsx +1 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityApiDescriptionRelations.tsx +1 -1
- package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +31 -18
- package/src/components/Catalog/CatalogEntityIcon.tsx +53 -18
- package/src/components/Catalog/CatalogEntityTypeIcon.tsx +19 -8
- package/src/components/Catalog/CatalogEntityTypeTag.tsx +11 -3
- package/src/components/Catalog/CatalogSelector.tsx +1 -1
- package/src/components/Catalog/CatalogTableView/CatalogEntityCell.tsx +1 -1
- package/src/components/Catalog/CatalogViewModeToggle.tsx +1 -1
- package/src/components/Catalog/variables.ts +9 -6
- package/src/components/CatalogClassic/CatalogClassic.tsx +26 -10
- package/src/components/CodeBlock/CodeBlock.tsx +5 -11
- package/src/components/CodeBlock/CodeBlockControls.tsx +3 -6
- package/src/components/CodeBlock/CodeBlockDropdown.tsx +11 -20
- package/src/components/CodeBlock/CodeBlockTabs.tsx +8 -8
- package/src/components/Search/SearchDialog.tsx +14 -5
- package/src/components/Search/SearchFilter.tsx +2 -1
- package/src/components/Tooltip/Tooltip.tsx +6 -8
- package/src/components/Tooltip/TooltipWrapper.tsx +1 -1
- package/src/core/constants/catalog.ts +13 -27
- package/src/core/contexts/CodeSnippetContext.tsx +54 -18
- package/src/core/hooks/catalog/useCatalogTableViewRow.ts +1 -1
- package/src/core/hooks/use-active-section-id.ts +6 -0
- package/src/core/hooks/use-codeblock-tabs-controls.ts +2 -2
- package/src/core/hooks/use-control.ts +21 -3
- package/src/core/hooks/use-local-state.ts +28 -19
- package/src/core/hooks/use-telemetry-fallback.ts +5 -0
- package/src/core/openapi/index.ts +8 -4
- package/src/core/styles/global.ts +19 -0
- package/src/core/types/catalog.ts +1 -2
- package/src/core/types/hooks.ts +6 -1
- package/src/core/types/index.ts +1 -0
- package/src/core/types/l10n.ts +3 -0
- package/src/core/types/open-api-info.ts +34 -0
- package/src/core/types/open-api-server.ts +1 -0
- package/src/core/types/search.ts +3 -3
- package/src/core/utils/urls.ts +2 -1
- package/src/ext/useConfigureReplay.ts +2 -1
- package/src/icons/HierarchyIcon/HierarchyIcon.tsx +32 -0
- package/src/icons/NoteIcon/NoteIcon.tsx +35 -0
- package/src/icons/ShareIcon/ShareIcon.tsx +23 -0
- package/src/index.ts +2 -0
- package/src/layouts/DocumentationLayout.tsx +3 -10
- package/src/markdoc/components/CodeGroup/CodeGroup.tsx +81 -52
- package/src/markdoc/components/Tabs/Tabs.tsx +10 -2
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
import React, { type JSX } from 'react';
|
|
2
|
-
import styled from 'styled-components';
|
|
3
2
|
|
|
4
|
-
import type {
|
|
3
|
+
import type { CodeBlockItems } from '@redocly/theme/components/CodeBlock/CodeBlock';
|
|
5
4
|
|
|
6
5
|
import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
|
|
7
6
|
import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
|
|
8
7
|
import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
|
|
9
8
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
10
|
-
import { ChevronSortIcon } from '@redocly/theme/icons/ChevronSortIcon/ChevronSortIcon';
|
|
11
9
|
import { NoneIcon } from '@redocly/theme/icons/NoneIcon/NoneIcon';
|
|
12
10
|
import { getFileIconByLanguage } from '@redocly/theme/core/utils';
|
|
13
11
|
|
|
14
|
-
export function CodeBlockDropdown({ items, onChange, value }:
|
|
15
|
-
const activeItem = items.find((item) => item.
|
|
12
|
+
export function CodeBlockDropdown({ items, onChange, value }: CodeBlockItems): JSX.Element {
|
|
13
|
+
const activeItem = items.find((item) => item.id === value) || items[0];
|
|
16
14
|
const icon = activeItem?.lang ? getFileIconByLanguage(activeItem?.lang) : null;
|
|
17
15
|
return (
|
|
18
|
-
<
|
|
16
|
+
<Dropdown
|
|
17
|
+
withArrow
|
|
19
18
|
alignment="end"
|
|
20
19
|
trigger={
|
|
21
|
-
<Button
|
|
20
|
+
<Button iconPosition="right" variant="ghost" size="small">
|
|
22
21
|
{icon}
|
|
23
22
|
{activeItem.name}
|
|
24
23
|
</Button>
|
|
@@ -27,11 +26,12 @@ export function CodeBlockDropdown({ items, onChange, value }: CodeBlockDropdownI
|
|
|
27
26
|
<DropdownMenu>
|
|
28
27
|
{items.map((item) => {
|
|
29
28
|
const icon = getFileIconByLanguage(item.lang || '');
|
|
29
|
+
const isActive = item.id === value;
|
|
30
30
|
return (
|
|
31
31
|
<DropdownMenuItem
|
|
32
|
-
key={item.
|
|
33
|
-
onAction={() => onChange(item.
|
|
34
|
-
active={
|
|
32
|
+
key={item.id}
|
|
33
|
+
onAction={() => onChange(item.id)}
|
|
34
|
+
active={isActive}
|
|
35
35
|
prefix={item.lang ? icon : <NoneIcon size="var(--icon-size)" />}
|
|
36
36
|
>
|
|
37
37
|
{item.name}
|
|
@@ -39,15 +39,6 @@ export function CodeBlockDropdown({ items, onChange, value }: CodeBlockDropdownI
|
|
|
39
39
|
);
|
|
40
40
|
})}
|
|
41
41
|
</DropdownMenu>
|
|
42
|
-
</
|
|
42
|
+
</Dropdown>
|
|
43
43
|
);
|
|
44
44
|
}
|
|
45
|
-
|
|
46
|
-
const StyledDropdown = styled(Dropdown)`
|
|
47
|
-
margin-left: auto;
|
|
48
|
-
--icon-size: 18px;
|
|
49
|
-
--button-color: var(--text-color-secondary);
|
|
50
|
-
button.button-size-small {
|
|
51
|
-
--button-icon-size: 18px;
|
|
52
|
-
}
|
|
53
|
-
`;
|
|
@@ -2,7 +2,7 @@ import React, { useEffect, useRef } from 'react';
|
|
|
2
2
|
import styled, { css } from 'styled-components';
|
|
3
3
|
|
|
4
4
|
import type { JSX } from 'react';
|
|
5
|
-
import type {
|
|
5
|
+
import type { CodeBlockItems } from '@redocly/theme/components/CodeBlock/CodeBlock';
|
|
6
6
|
|
|
7
7
|
import { useCodeBlockTabsControls } from '@redocly/theme/core/hooks';
|
|
8
8
|
import { Button } from '@redocly/theme/components/Button/Button';
|
|
@@ -11,7 +11,7 @@ import { ChevronRightIcon } from '@redocly/theme/icons/ChevronRightIcon/ChevronR
|
|
|
11
11
|
import { getFileIconByExt, getFileIconByLanguage } from '@redocly/theme/core/utils/get-file-icon';
|
|
12
12
|
|
|
13
13
|
export type CodeBlockTabsProps = {
|
|
14
|
-
tabs:
|
|
14
|
+
tabs: CodeBlockItems;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
export function CodeBlockTabs({ tabs }: CodeBlockTabsProps): JSX.Element {
|
|
@@ -24,7 +24,7 @@ export function CodeBlockTabs({ tabs }: CodeBlockTabsProps): JSX.Element {
|
|
|
24
24
|
});
|
|
25
25
|
|
|
26
26
|
useEffect(() => {
|
|
27
|
-
const activeTab = tabRefs.current.find((tab) => tab?.dataset.
|
|
27
|
+
const activeTab = tabRefs.current.find((tab) => tab?.dataset.id === tabs.value);
|
|
28
28
|
|
|
29
29
|
if (activeTab) {
|
|
30
30
|
activeTab.scrollIntoView({ block: 'nearest', inline: 'center' });
|
|
@@ -35,23 +35,23 @@ export function CodeBlockTabs({ tabs }: CodeBlockTabsProps): JSX.Element {
|
|
|
35
35
|
<CodeBlockTabsWrapper ref={containerRef} data-component-name="CodeBlock/CodeBlockTabs">
|
|
36
36
|
<ShadowWrapper>
|
|
37
37
|
<Tabs>
|
|
38
|
-
{tabs.items.map((
|
|
38
|
+
{tabs.items.map((item, i) => {
|
|
39
|
+
const { name, lang, id } = item;
|
|
39
40
|
const ext = name.match(/\.([^.]+)$/)?.[1];
|
|
40
41
|
const fileIcon = lang
|
|
41
42
|
? getFileIconByLanguage(lang)
|
|
42
43
|
: ext
|
|
43
44
|
? getFileIconByExt(ext)
|
|
44
45
|
: null;
|
|
45
|
-
|
|
46
46
|
return (
|
|
47
47
|
<Tab
|
|
48
48
|
ref={(el: HTMLButtonElement | null) => {
|
|
49
49
|
tabRefs.current[i] = el as HTMLButtonElement;
|
|
50
50
|
}}
|
|
51
51
|
data-name={name}
|
|
52
|
-
active={
|
|
53
|
-
key={
|
|
54
|
-
onClick={() => tabs.onChange(
|
|
52
|
+
active={id === tabs.value}
|
|
53
|
+
key={id}
|
|
54
|
+
onClick={() => tabs.onChange(id)}
|
|
55
55
|
>
|
|
56
56
|
{fileIcon}
|
|
57
57
|
{name}
|
|
@@ -306,13 +306,16 @@ export function SearchDialog({ onClose, className }: SearchDialogProps): JSX.Ele
|
|
|
306
306
|
aria-selected="true"
|
|
307
307
|
>
|
|
308
308
|
<AiStarsIcon
|
|
309
|
+
style={{ flexShrink: 0 }}
|
|
309
310
|
color="var(--search-ai-icon-color)"
|
|
310
311
|
size="36px"
|
|
311
312
|
background="var(--search-ai-icon-bg-color)"
|
|
312
313
|
margin="0 var(--spacing-md) 0 0"
|
|
313
314
|
borderRadius="var(--border-radius-lg)"
|
|
314
315
|
/>
|
|
315
|
-
<
|
|
316
|
+
<QueryWrapper>
|
|
317
|
+
<Typography fontWeight="var(--font-weight-semibold)">{query}</Typography>
|
|
318
|
+
</QueryWrapper>
|
|
316
319
|
<Typography>- {translate('search.ai.label', 'Ask AI assistant')}</Typography>
|
|
317
320
|
<ReturnKeyIcon color="var(--search-item-text-color)" />
|
|
318
321
|
</SearchWithAI>
|
|
@@ -484,10 +487,10 @@ const SearchDialogWrapper = styled.div`
|
|
|
484
487
|
border-radius: 0;
|
|
485
488
|
|
|
486
489
|
@media screen and (max-width: ${breakpoints.small}) {
|
|
487
|
-
min-height: -webkit-fill-available
|
|
488
|
-
min-height: 100dvh
|
|
489
|
-
height:
|
|
490
|
-
width: 100vw
|
|
490
|
+
min-height: -webkit-fill-available;
|
|
491
|
+
min-height: 100dvh;
|
|
492
|
+
height: 100dvh;
|
|
493
|
+
width: 100vw;
|
|
491
494
|
}
|
|
492
495
|
|
|
493
496
|
@media screen and (min-width: ${breakpoints.small}) {
|
|
@@ -539,6 +542,8 @@ const SearchDialogBodyMainView = styled.div`
|
|
|
539
542
|
|
|
540
543
|
const SearchDialogBodyFilterView = styled.div`
|
|
541
544
|
overflow: scroll;
|
|
545
|
+
max-width: var(--search-filter-width);
|
|
546
|
+
width: 100%;
|
|
542
547
|
`;
|
|
543
548
|
|
|
544
549
|
const SearchDialogFooter = styled.footer`
|
|
@@ -673,3 +678,7 @@ const SearchWithAI = styled.div`
|
|
|
673
678
|
margin-right: var(--spacing-xs);
|
|
674
679
|
}
|
|
675
680
|
`;
|
|
681
|
+
|
|
682
|
+
const QueryWrapper = styled.div`
|
|
683
|
+
word-break: break-word;
|
|
684
|
+
`;
|
|
@@ -66,7 +66,8 @@ export function SearchFilter({
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const SearchFilterWrapper = styled.div`
|
|
69
|
-
width:
|
|
69
|
+
width: 100%;
|
|
70
|
+
max-width: var(--search-filter-width);
|
|
70
71
|
display: flex;
|
|
71
72
|
flex-direction: column;
|
|
72
73
|
padding: var(--search-filter-padding);
|
|
@@ -98,14 +98,12 @@ export function TooltipComponent({
|
|
|
98
98
|
}, [isOpened, placement, updateTooltipPosition]);
|
|
99
99
|
|
|
100
100
|
useEffect(() => {
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
handleClose();
|
|
106
|
-
}
|
|
101
|
+
if (isOpen && !disabled) {
|
|
102
|
+
handleOpen();
|
|
103
|
+
} else {
|
|
104
|
+
handleClose();
|
|
107
105
|
}
|
|
108
|
-
}, [isOpen,
|
|
106
|
+
}, [isOpen, handleOpen, handleClose, disabled]);
|
|
109
107
|
|
|
110
108
|
const controllers = !isControlled &&
|
|
111
109
|
!disabled && {
|
|
@@ -268,7 +266,7 @@ const TooltipBody = styled.span<
|
|
|
268
266
|
display: inline-block;
|
|
269
267
|
|
|
270
268
|
padding: var(--tooltip-padding);
|
|
271
|
-
max-width: var(--tooltip-max-width);
|
|
269
|
+
max-width: ${({ width }) => width || 'var(--tooltip-max-width)'};
|
|
272
270
|
white-space: normal;
|
|
273
271
|
word-break: normal;
|
|
274
272
|
overflow-wrap: break-word;
|
|
@@ -26,7 +26,7 @@ export function TooltipWrapper({
|
|
|
26
26
|
showOnHover = true,
|
|
27
27
|
disabled = false,
|
|
28
28
|
}: TooltipWrapperProps): JSX.Element {
|
|
29
|
-
const tooltip = useControl();
|
|
29
|
+
const tooltip = useControl(false);
|
|
30
30
|
|
|
31
31
|
const handleMouseEnter = (): void => {
|
|
32
32
|
if (showOnHover && !disabled) {
|
|
@@ -2,33 +2,15 @@ import type { EntityRelationType } from '../types/catalog';
|
|
|
2
2
|
|
|
3
3
|
export const CATALOG_TAG_MAX_LENGTH = 15;
|
|
4
4
|
|
|
5
|
-
export const
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
12
|
-
'
|
|
13
|
-
|
|
14
|
-
'dependsOn',
|
|
15
|
-
'dependencyOf',
|
|
16
|
-
'uses',
|
|
17
|
-
'usedBy',
|
|
18
|
-
'produces',
|
|
19
|
-
'consumes',
|
|
20
|
-
'linksTo',
|
|
21
|
-
'supersedes',
|
|
22
|
-
'supersededBy',
|
|
23
|
-
'compatibleWith',
|
|
24
|
-
'extends',
|
|
25
|
-
'extendedBy',
|
|
26
|
-
'relatesTo',
|
|
27
|
-
'hasMember',
|
|
28
|
-
'memberOf',
|
|
29
|
-
'triggers',
|
|
30
|
-
'triggeredBy',
|
|
31
|
-
] as const;
|
|
5
|
+
export const PREDEFINED_ENTITY_TYPES = [
|
|
6
|
+
'service',
|
|
7
|
+
'domain',
|
|
8
|
+
'team',
|
|
9
|
+
'user',
|
|
10
|
+
'api-description',
|
|
11
|
+
'data-schema',
|
|
12
|
+
'api-operation',
|
|
13
|
+
];
|
|
32
14
|
|
|
33
15
|
export const reverseRelationMap: Record<EntityRelationType, EntityRelationType> = {
|
|
34
16
|
partOf: 'hasParts',
|
|
@@ -56,6 +38,8 @@ export const reverseRelationMap: Record<EntityRelationType, EntityRelationType>
|
|
|
56
38
|
memberOf: 'hasMember',
|
|
57
39
|
triggers: 'triggeredBy',
|
|
58
40
|
triggeredBy: 'triggers',
|
|
41
|
+
returns: 'returnedBy',
|
|
42
|
+
returnedBy: 'returns',
|
|
59
43
|
} as const;
|
|
60
44
|
|
|
61
45
|
export const relationTypeMap: Record<EntityRelationType, string> = {
|
|
@@ -84,6 +68,8 @@ export const relationTypeMap: Record<EntityRelationType, string> = {
|
|
|
84
68
|
memberOf: 'Member of',
|
|
85
69
|
triggers: 'Triggers',
|
|
86
70
|
triggeredBy: 'Triggered by',
|
|
71
|
+
returns: 'Returns',
|
|
72
|
+
returnedBy: 'Returned by',
|
|
87
73
|
};
|
|
88
74
|
|
|
89
75
|
export enum GraphHandleType {
|
|
@@ -1,31 +1,67 @@
|
|
|
1
|
-
import { createContext, useContext,
|
|
1
|
+
import React, { createContext, useContext, useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
activeSnippetName: string;
|
|
5
|
-
setActiveSnippetName: (name: string) => void;
|
|
6
|
-
};
|
|
3
|
+
import { useLocalState } from '../hooks/use-local-state';
|
|
7
4
|
|
|
8
5
|
export const CODE_GROUP_SNIPPET_NAME_KEY = 'redocly:codeGroupSnippetName';
|
|
9
6
|
|
|
7
|
+
type CodeSnippetContextType = {
|
|
8
|
+
activeSnippets: Record<string, string>;
|
|
9
|
+
setActiveSnippet: (groupId: string, snippetId: string) => void;
|
|
10
|
+
};
|
|
11
|
+
|
|
10
12
|
export const CodeSnippetContext = createContext<CodeSnippetContextType | null>(null);
|
|
11
13
|
|
|
12
|
-
export function
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
export function CodeSnippetProvider({ children }: { children: React.ReactNode }) {
|
|
15
|
+
const [activeSnippets, setActiveSnippets] = useLocalState<Record<string, string>>(
|
|
16
|
+
CODE_GROUP_SNIPPET_NAME_KEY,
|
|
17
|
+
{},
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const setActiveSnippet = useCallback(
|
|
21
|
+
(groupId: string, snippetId: string) => {
|
|
22
|
+
setActiveSnippets({ ...activeSnippets, [groupId]: snippetId });
|
|
23
|
+
},
|
|
24
|
+
[activeSnippets, setActiveSnippets],
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const contextValue = { activeSnippets, setActiveSnippet };
|
|
28
|
+
|
|
29
|
+
return <CodeSnippetContext.Provider value={contextValue}>{children}</CodeSnippetContext.Provider>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const useCodeSnippetContext = (): CodeSnippetContextType => {
|
|
15
33
|
const context = useContext(CodeSnippetContext);
|
|
34
|
+
|
|
16
35
|
if (!context) {
|
|
17
36
|
throw new Error('useCodeSnippetContext must be used within a CodeSnippetContext');
|
|
18
37
|
}
|
|
19
38
|
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
return context;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const useActiveCodeSnippetId = (
|
|
43
|
+
groupId?: string,
|
|
44
|
+
availableSnippets?: { id: string }[],
|
|
45
|
+
): [string, (id: string) => void] => {
|
|
46
|
+
const { activeSnippets, setActiveSnippet } = useCodeSnippetContext();
|
|
47
|
+
|
|
48
|
+
const storedSnippetId = groupId ? activeSnippets[groupId] || '' : '';
|
|
49
|
+
|
|
50
|
+
const activeId = useMemo(() => {
|
|
51
|
+
if (!availableSnippets?.length) return storedSnippetId;
|
|
52
|
+
|
|
53
|
+
const found = storedSnippetId && availableSnippets.find((s) => s.id === storedSnippetId);
|
|
54
|
+
return found ? storedSnippetId : availableSnippets[0]?.id || '';
|
|
55
|
+
}, [storedSnippetId, availableSnippets]);
|
|
56
|
+
|
|
57
|
+
const setActiveSnippetId = useCallback(
|
|
58
|
+
(id: string) => {
|
|
59
|
+
if (groupId) {
|
|
60
|
+
setActiveSnippet(groupId, id);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[groupId, setActiveSnippet],
|
|
22
64
|
);
|
|
23
65
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return [activeSnippetName, setActiveSnippetName];
|
|
27
|
-
} else {
|
|
28
|
-
// use global synced state for dropdown mode
|
|
29
|
-
return [context.activeSnippetName, context.setActiveSnippetName];
|
|
30
|
-
}
|
|
31
|
-
}
|
|
66
|
+
return [activeId, setActiveSnippetId];
|
|
67
|
+
};
|
|
@@ -21,7 +21,7 @@ export function useCatalogTableViewRow({
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
return Object.values(entitiesCatalogConfig.catalogs ?? {}).find((catalog) => {
|
|
24
|
-
return catalog
|
|
24
|
+
return catalog?.includes?.some((include) => include.type === entityType);
|
|
25
25
|
});
|
|
26
26
|
};
|
|
27
27
|
|
|
@@ -25,6 +25,12 @@ export function useActiveSectionId(
|
|
|
25
25
|
setItemId('');
|
|
26
26
|
return;
|
|
27
27
|
}
|
|
28
|
+
|
|
29
|
+
if (window.scrollY <= 0) {
|
|
30
|
+
setItemId(sections[0].getAttribute('data-section-id') || '');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
28
34
|
for (let i = 0; i < sections.length; i++) {
|
|
29
35
|
const section = sections[i];
|
|
30
36
|
const rect = section.getBoundingClientRect();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { useEffect, useState, useCallback, useMemo } from 'react';
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { CodeBlockItems } from '../../components/CodeBlock/CodeBlock';
|
|
4
4
|
|
|
5
5
|
type CodeBlockTabsProps = {
|
|
6
|
-
tabs:
|
|
6
|
+
tabs: CodeBlockItems;
|
|
7
7
|
containerRef: React.RefObject<HTMLDivElement | null>;
|
|
8
8
|
tabRefs: React.RefObject<HTMLButtonElement[]>;
|
|
9
9
|
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { useState, useCallback } from 'react';
|
|
1
|
+
import { useState, useCallback, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const DEFAULT_CONTROL_OPEN_DELAY = 300;
|
|
2
4
|
|
|
3
5
|
export type UseControlReturnType = {
|
|
4
6
|
isOpened: boolean;
|
|
@@ -8,9 +10,25 @@ export type UseControlReturnType = {
|
|
|
8
10
|
|
|
9
11
|
export const useControl = (initialVal = false): UseControlReturnType => {
|
|
10
12
|
const [isOpened, setIsOpened] = useState<boolean>(initialVal);
|
|
13
|
+
const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
14
|
+
|
|
15
|
+
const clearOpenTimer = useCallback(() => {
|
|
16
|
+
if (timeoutRef.current) {
|
|
17
|
+
clearTimeout(timeoutRef.current);
|
|
18
|
+
}
|
|
19
|
+
}, []);
|
|
20
|
+
|
|
21
|
+
const handleOpen = useCallback(() => {
|
|
22
|
+
clearOpenTimer();
|
|
23
|
+
timeoutRef.current = setTimeout(() => {
|
|
24
|
+
setIsOpened(true);
|
|
25
|
+
}, DEFAULT_CONTROL_OPEN_DELAY);
|
|
26
|
+
}, [clearOpenTimer]);
|
|
11
27
|
|
|
12
|
-
const
|
|
13
|
-
|
|
28
|
+
const handleClose = useCallback(() => {
|
|
29
|
+
clearOpenTimer();
|
|
30
|
+
setIsOpened(false);
|
|
31
|
+
}, [clearOpenTimer]);
|
|
14
32
|
|
|
15
33
|
return {
|
|
16
34
|
isOpened,
|
|
@@ -1,30 +1,39 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isBrowser } from '../utils/js-utils';
|
|
4
|
+
|
|
5
|
+
function getStoredValue<T>(key: string, fallback: T): T {
|
|
6
|
+
if (!isBrowser()) return fallback;
|
|
2
7
|
|
|
3
|
-
function getInitialValue<T>(key: string, initialValue: T): T {
|
|
4
|
-
if (typeof window === 'undefined') {
|
|
5
|
-
return initialValue;
|
|
6
|
-
}
|
|
7
8
|
try {
|
|
8
9
|
const savedValue = localStorage.getItem(key);
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
} catch (error) {
|
|
13
|
-
console.error(`Error reading from localStorage for key "${key}":`, error);
|
|
10
|
+
return savedValue ? (JSON.parse(savedValue) as T) : fallback;
|
|
11
|
+
} catch {
|
|
12
|
+
return fallback;
|
|
14
13
|
}
|
|
15
|
-
return initialValue;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
export function useLocalState<T>(key: string, initialValue: T): [T, (value: T) => void] {
|
|
19
|
-
const [value, setValue] = useState<T>(
|
|
17
|
+
const [value, setValue] = useState<T>(initialValue);
|
|
20
18
|
|
|
19
|
+
// Load stored value from localStorage after component mounts
|
|
20
|
+
// This ensures SSR compatibility: server and client both start with initialValue,
|
|
21
|
+
// then client loads the actual stored value without hydration mismatch
|
|
21
22
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
if (!isBrowser()) return;
|
|
24
|
+
setValue(getStoredValue(key, initialValue));
|
|
25
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
26
|
+
}, [key]);
|
|
27
|
+
|
|
28
|
+
const handleSetValue = useCallback(
|
|
29
|
+
(newValue: T) => {
|
|
30
|
+
setValue(newValue);
|
|
31
|
+
if (isBrowser()) {
|
|
32
|
+
localStorage.setItem(key, JSON.stringify(newValue));
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
[key],
|
|
36
|
+
);
|
|
28
37
|
|
|
29
|
-
return [value,
|
|
38
|
+
return [value, handleSetValue] as const;
|
|
30
39
|
}
|
|
@@ -58,4 +58,9 @@ export const useTelemetryFallback = () => ({
|
|
|
58
58
|
sendAsyncapiDocsMessageClickedMessage: () => {},
|
|
59
59
|
sendAsyncapiDocsServerModalOpenedMessage: () => {},
|
|
60
60
|
sendAsyncapiDocsDownloadDefinitionClickedMessage: () => {},
|
|
61
|
+
sendGraphqlDocsViewedMessage: () => {},
|
|
62
|
+
sendGraphqlDocsPerformanceMetricsMessage: () => {},
|
|
63
|
+
sendGraphqlDocsReferencedInLinkClickedMessage: () => {},
|
|
64
|
+
sendGraphqlDocsRequiredScopesModalOpenedMessage: () => {},
|
|
65
|
+
sendGraphqlDocsDownloadDefinitionClickedMessage: () => {},
|
|
61
66
|
});
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
// This selective import approach will help with tree shaking and reduce
|
|
5
5
|
// the overall bundle size by avoiding importing the entire theme package
|
|
6
6
|
// when only specific functionality is needed.
|
|
7
|
+
export type { UserClaims } from '../types/user-claims';
|
|
8
|
+
export type { OperationParameter, ParameterHighlight } from '../types/search';
|
|
9
|
+
export type { TFunction, TOptions } from '../types/l10n';
|
|
10
|
+
export type { SelectOption, SelectProps } from '../types/select';
|
|
7
11
|
export { IS_BROWSER } from '../utils/dom';
|
|
8
12
|
export {
|
|
9
13
|
addLeadingSlash,
|
|
@@ -18,15 +22,15 @@ export { useMount } from '../hooks/use-mount';
|
|
|
18
22
|
export { GlobalStyle } from '../styles/global';
|
|
19
23
|
export { breakpoints } from '../utils/media-css';
|
|
20
24
|
export { isPrimitive } from '../utils/args-typecheck';
|
|
21
|
-
export
|
|
25
|
+
export { ClipboardService } from '../utils/clipboard-service';
|
|
26
|
+
export { getUserAgent } from '../utils/get-user-agent';
|
|
22
27
|
export { useFocusTrap } from '../hooks/use-focus-trap';
|
|
23
|
-
export type { TFunction, TOptions } from '../types/l10n';
|
|
24
28
|
export { useThemeHooks } from '../hooks/use-theme-hooks';
|
|
25
29
|
export { useOutsideClick } from '../hooks/use-outside-click';
|
|
26
|
-
export type { SelectOption, SelectProps } from '../types/select';
|
|
27
30
|
export { useActiveSectionId } from '../hooks/use-active-section-id';
|
|
28
31
|
export { useModalScrollLock } from '../hooks/use-modal-scroll-lock';
|
|
32
|
+
export { useSearchDialog } from '../hooks/search/use-search-dialog';
|
|
33
|
+
export { useDialogHotKeys } from '../hooks/use-dialog-hotkeys';
|
|
29
34
|
export { SecurityVariablesEnvSuffix } from '../constants/environments';
|
|
30
35
|
export { isUndefined, isString, isNotNull, isObject } from '../utils/type-guards';
|
|
31
36
|
export { ThemeDataContext, type ThemeDataTransferObject } from '../contexts/ThemeDataContext';
|
|
32
|
-
export { ENTITY_RELATION_TYPES } from '../constants/catalog';
|
|
@@ -780,6 +780,25 @@ const apiReferenceDocs = css`
|
|
|
780
780
|
--fab-icon-color: var(--navbar-text-color); // @presenter Color
|
|
781
781
|
|
|
782
782
|
// @tokens End
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* @tokens OpenAPI Schema Catalog Link
|
|
786
|
+
*/
|
|
787
|
+
|
|
788
|
+
--schema-catalog-link-margin-bottom: var(--spacing-lg);
|
|
789
|
+
--schema-catalog-link-padding: 2px;
|
|
790
|
+
--schema-catalog-link-border-radius: var(--border-radius-md);
|
|
791
|
+
--schema-catalog-link-background-color: var(--layer-color);
|
|
792
|
+
--schema-catalog-link-color: var(--color-purple-7);
|
|
793
|
+
|
|
794
|
+
--schema-catalog-link-share-icon-color: var(--color-purple-7);
|
|
795
|
+
--schema-catalog-link-share-icon-background-color: var(--color-purple-1);
|
|
796
|
+
--schema-catalog-link-share-icon-border-radius: var(--border-radius-md);
|
|
797
|
+
--schema-catalog-link-share-icon-wrapper-size: var(--spacing-xl);
|
|
798
|
+
|
|
799
|
+
--schema-catalog-link-text-color: var(--text-color-primary);
|
|
800
|
+
|
|
801
|
+
// @tokens End
|
|
783
802
|
`;
|
|
784
803
|
|
|
785
804
|
const badges = css`
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { InfiniteData, UseInfiniteQueryResult } from '@tanstack/react-query';
|
|
2
2
|
import { CatalogEntityConfig, LayoutVariant } from '@redocly/config';
|
|
3
|
+
import { ENTITY_RELATION_TYPES } from '@redocly/config';
|
|
3
4
|
|
|
4
5
|
import type { CatalogFilterConfig } from '@redocly/theme/config';
|
|
5
6
|
|
|
6
|
-
import { ENTITY_RELATION_TYPES } from '../constants/catalog';
|
|
7
|
-
|
|
8
7
|
export type SortOption = 'title' | '-title' | 'type' | '-type';
|
|
9
8
|
|
|
10
9
|
export type UseCatalogResponse = {
|
package/src/core/types/hooks.ts
CHANGED
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
FilteredCatalog,
|
|
18
18
|
UseCatalogSortResponse,
|
|
19
19
|
UseCatalogSearchResponse,
|
|
20
|
+
CatalogViewMode,
|
|
20
21
|
} from './catalog';
|
|
21
22
|
import type { UserMenuData } from './user-menu';
|
|
22
23
|
import type { ItemState } from './sidebar';
|
|
@@ -139,7 +140,11 @@ export type ThemeHooks = {
|
|
|
139
140
|
nextPage?: ResolvedNavItemWithLink;
|
|
140
141
|
}
|
|
141
142
|
| undefined;
|
|
142
|
-
useCatalog: (
|
|
143
|
+
useCatalog: (
|
|
144
|
+
config?: CatalogEntityConfig,
|
|
145
|
+
entitiesCounterInitial?: number,
|
|
146
|
+
initialViewMode?: CatalogViewMode,
|
|
147
|
+
) => UseCatalogResponse;
|
|
143
148
|
useCatalogSort: () => UseCatalogSortResponse;
|
|
144
149
|
useCatalogSearch: () => UseCatalogSearchResponse;
|
|
145
150
|
useFetchCatalogEntities: (
|
package/src/core/types/index.ts
CHANGED
package/src/core/types/l10n.ts
CHANGED
|
@@ -280,6 +280,9 @@ export type TranslationKey =
|
|
|
280
280
|
| 'openapi.requiredScopes'
|
|
281
281
|
| 'openapi.unsupportedLanguage'
|
|
282
282
|
| 'openapi.failedToGenerateCodeSample'
|
|
283
|
+
| 'openapi.schemaCatalogLink.title'
|
|
284
|
+
| 'openapi.schemaCatalogLink.copyButtonTooltip'
|
|
285
|
+
| 'openapi.schemaCatalogLink.copiedTooltip'
|
|
283
286
|
| 'asyncapi.download.description.title'
|
|
284
287
|
| 'asyncapi.info.title'
|
|
285
288
|
| 'graphql.queries'
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export type OpenAPIInfo = {
|
|
2
|
+
title: string;
|
|
3
|
+
version: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
summary?: string;
|
|
6
|
+
termsOfService?: string;
|
|
7
|
+
contact?: {
|
|
8
|
+
name?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
email?: string;
|
|
11
|
+
};
|
|
12
|
+
license?: {
|
|
13
|
+
name: string;
|
|
14
|
+
url?: string;
|
|
15
|
+
identifier?: string;
|
|
16
|
+
};
|
|
17
|
+
externalDocs?: {
|
|
18
|
+
description?: string;
|
|
19
|
+
url: string;
|
|
20
|
+
};
|
|
21
|
+
'x-logo'?: {
|
|
22
|
+
url?: string;
|
|
23
|
+
backgroundColor?: string;
|
|
24
|
+
altText?: string;
|
|
25
|
+
href?: string;
|
|
26
|
+
};
|
|
27
|
+
'x-metadata'?: {
|
|
28
|
+
apiId?: string;
|
|
29
|
+
[key: string]: unknown;
|
|
30
|
+
};
|
|
31
|
+
'x-seo'?: {
|
|
32
|
+
title?: string;
|
|
33
|
+
};
|
|
34
|
+
};
|