@redocly/theme 0.57.0-next.0 → 0.57.0-next.2

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 (108) hide show
  1. package/lib/components/Catalog/CatalogCardView/CatalogCard.js +1 -0
  2. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +36 -25
  3. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsEdge.d.ts +6 -0
  4. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsEdge.js +38 -0
  5. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.d.ts +6 -0
  6. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.js +83 -0
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.d.ts +2 -0
  8. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.js +29 -0
  9. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode.d.ts +8 -0
  10. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode.js +33 -0
  11. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNode.d.ts +16 -0
  12. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNode.js +24 -0
  13. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsRootNode.d.ts +7 -0
  14. package/lib/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsRootNode.js +30 -0
  15. package/lib/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.js +5 -2
  16. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.d.ts +1 -0
  17. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.js +7 -3
  18. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.js +3 -8
  19. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent.d.ts +22 -0
  20. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent.js +18 -0
  21. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.js +2 -2
  22. package/lib/components/Catalog/CatalogEntityIcon.d.ts +2 -1
  23. package/lib/components/Catalog/CatalogEntityIcon.js +4 -6
  24. package/lib/components/Catalog/CatalogTableView/CatalogTableViewRow.js +1 -1
  25. package/lib/components/Catalog/variables.js +42 -0
  26. package/lib/components/CatalogClassic/CatalogClassicVirtualizedGroups.js +13 -8
  27. package/lib/core/constants/catalog.d.ts +10 -0
  28. package/lib/core/constants/catalog.js +14 -1
  29. package/lib/core/hooks/__mocks__/search/use-recent-searches.d.ts +2 -2
  30. package/lib/core/hooks/__mocks__/search/use-recent-searches.js +2 -1
  31. package/lib/core/hooks/__mocks__/search/use-search-filter.d.ts +2 -2
  32. package/lib/core/hooks/__mocks__/search/use-search-filter.js +2 -1
  33. package/lib/core/hooks/__mocks__/search/use-suggested-pages.d.ts +2 -2
  34. package/lib/core/hooks/__mocks__/search/use-suggested-pages.js +2 -1
  35. package/lib/core/hooks/__mocks__/use-controlled-state.d.ts +1 -1
  36. package/lib/core/hooks/__mocks__/use-controlled-state.js +2 -1
  37. package/lib/core/hooks/__mocks__/use-input-key-commands.d.ts +3 -3
  38. package/lib/core/hooks/__mocks__/use-input-key-commands.js +3 -2
  39. package/lib/core/hooks/__mocks__/use-mobile-menu.d.ts +1 -1
  40. package/lib/core/hooks/__mocks__/use-mobile-menu.js +2 -1
  41. package/lib/core/hooks/__mocks__/use-theme-config.d.ts +2 -2
  42. package/lib/core/hooks/__mocks__/use-theme-config.js +2 -1
  43. package/lib/core/hooks/__mocks__/use-theme-hooks.d.ts +47 -47
  44. package/lib/core/hooks/__mocks__/use-theme-hooks.js +38 -37
  45. package/lib/core/hooks/__mocks__/use-time-ago.d.ts +2 -2
  46. package/lib/core/hooks/__mocks__/use-time-ago.js +2 -1
  47. package/lib/core/hooks/catalog/useGraph.d.ts +15 -0
  48. package/lib/core/hooks/catalog/useGraph.js +165 -0
  49. package/lib/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.js +4 -3
  50. package/lib/core/hooks/index.d.ts +1 -0
  51. package/lib/core/hooks/index.js +1 -0
  52. package/lib/core/hooks/menu/__mocks__/use-mobile-menu-items.d.ts +1 -1
  53. package/lib/core/hooks/menu/__mocks__/use-mobile-menu-items.js +2 -1
  54. package/lib/core/hooks/use-page-actions.js +5 -1
  55. package/lib/core/openapi/index.d.ts +1 -0
  56. package/lib/core/openapi/index.js +3 -1
  57. package/lib/core/styles/index.d.ts +1 -0
  58. package/lib/core/styles/index.js +3 -0
  59. package/lib/core/styles/xyflow.d.ts +1 -0
  60. package/lib/core/styles/xyflow.js +623 -0
  61. package/lib/core/utils/dynamic.d.ts +9 -0
  62. package/lib/core/utils/dynamic.js +59 -0
  63. package/lib/core/utils/index.d.ts +1 -0
  64. package/lib/core/utils/index.js +1 -0
  65. package/lib/icons/GenericIcon/GenericIcon.d.ts +2 -2
  66. package/lib/icons/GenericIcon/GenericIcon.js +8 -3
  67. package/lib/icons/__tests__/IconTestUtils.d.ts +0 -1
  68. package/lib/icons/__tests__/IconTestUtils.js +8 -7
  69. package/package.json +11 -14
  70. package/src/components/Catalog/CatalogCardView/CatalogCard.tsx +1 -0
  71. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +60 -42
  72. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsEdge.tsx +63 -0
  73. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy.tsx +7 -0
  74. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.tsx +91 -0
  75. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsLinkedNode.tsx +48 -0
  76. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNode.tsx +45 -0
  77. package/src/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsRootNode.tsx +49 -0
  78. package/src/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath.tsx +6 -2
  79. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelations.tsx +8 -2
  80. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.tsx +24 -43
  81. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent.tsx +76 -0
  82. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityTeamRelations.tsx +2 -2
  83. package/src/components/Catalog/CatalogEntityIcon.tsx +7 -5
  84. package/src/components/Catalog/CatalogTableView/CatalogTableViewRow.tsx +1 -1
  85. package/src/components/Catalog/variables.ts +42 -0
  86. package/src/components/CatalogClassic/CatalogClassicVirtualizedGroups.tsx +29 -18
  87. package/src/core/constants/catalog.ts +13 -0
  88. package/src/core/hooks/__mocks__/search/use-recent-searches.ts +3 -1
  89. package/src/core/hooks/__mocks__/search/use-search-filter.ts +3 -1
  90. package/src/core/hooks/__mocks__/search/use-suggested-pages.ts +3 -1
  91. package/src/core/hooks/__mocks__/use-controlled-state.ts +3 -1
  92. package/src/core/hooks/__mocks__/use-input-key-commands.ts +4 -2
  93. package/src/core/hooks/__mocks__/use-mobile-menu.ts +3 -1
  94. package/src/core/hooks/__mocks__/use-theme-config.ts +3 -1
  95. package/src/core/hooks/__mocks__/use-theme-hooks.ts +39 -37
  96. package/src/core/hooks/__mocks__/use-time-ago.ts +3 -1
  97. package/src/core/hooks/catalog/useGraph.ts +236 -0
  98. package/src/core/hooks/code-walkthrough/__mocks__/MockIntersectionObserver.ts +5 -3
  99. package/src/core/hooks/index.ts +1 -0
  100. package/src/core/hooks/menu/__mocks__/use-mobile-menu-items.ts +3 -1
  101. package/src/core/hooks/use-page-actions.ts +5 -1
  102. package/src/core/openapi/index.ts +1 -0
  103. package/src/core/styles/index.ts +1 -0
  104. package/src/core/styles/xyflow.ts +620 -0
  105. package/src/core/utils/dynamic.tsx +85 -0
  106. package/src/core/utils/index.ts +1 -0
  107. package/src/icons/GenericIcon/GenericIcon.tsx +13 -4
  108. package/src/icons/__tests__/IconTestUtils.tsx +5 -4
@@ -0,0 +1,236 @@
1
+ import { useCallback, useEffect, useMemo } from 'react';
2
+ import {
3
+ addEdge,
4
+ type Node,
5
+ type Edge,
6
+ type Connection,
7
+ useNodesState,
8
+ useEdgesState,
9
+ Position,
10
+ OnNodesChange,
11
+ OnEdgesChange,
12
+ } from '@xyflow/react';
13
+
14
+ import { type CatalogEntityNodeData } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsNode';
15
+
16
+ import { BffCatalogEntity, BffCatalogRelatedEntity } from '../../types';
17
+ import {
18
+ GraphCustomEdgeType,
19
+ GraphCustomNodeType,
20
+ GraphHandleType,
21
+ reverseRelationMap,
22
+ } from '../../constants/catalog';
23
+
24
+ export type UseGraphProps = {
25
+ entity: BffCatalogEntity;
26
+ relations: BffCatalogRelatedEntity[];
27
+ };
28
+
29
+ export type UseGraphReturn = {
30
+ nodes: Node<CatalogEntityNodeData>[];
31
+ edges: Edge[];
32
+ onNodesChange: OnNodesChange<Node<CatalogEntityNodeData>>;
33
+ onEdgesChange: OnEdgesChange<Edge>;
34
+ onConnect: (params: Connection) => void;
35
+ };
36
+
37
+ type EntityGraphData = {
38
+ id: string;
39
+ title: string;
40
+ entityType: string;
41
+ relationLabel: string;
42
+ key: string;
43
+ };
44
+
45
+ // TODO: This isn't final implementation, leaved comments for future reference.
46
+ export function useGraph({ entity, relations }: UseGraphProps): UseGraphReturn {
47
+ const rootNodeId = entity.id;
48
+
49
+ // Compute final label for a relation considering its role
50
+ const getRelationLabel = useCallback((relation: BffCatalogRelatedEntity): string => {
51
+ const relationType = relation.relationType;
52
+ if (!relationType) {
53
+ return 'related';
54
+ }
55
+
56
+ return relation.relationRole === 'source' ? reverseRelationMap[relationType] : relationType;
57
+ }, []);
58
+
59
+ const processedRelations = useMemo(() => {
60
+ // Exclude self-relations and deduplicate by id
61
+ const seenIds = new Set<string>();
62
+ const filtered = (relations ?? []).filter((r) => r.id !== rootNodeId && r.key !== entity.key);
63
+
64
+ const unique = [] as Array<{
65
+ id: string;
66
+ title: string;
67
+ entityType: string;
68
+ relationLabel: string;
69
+ key: string;
70
+ }>;
71
+
72
+ for (const r of filtered) {
73
+ if (seenIds.has(r.id)) continue;
74
+ seenIds.add(r.id);
75
+ unique.push({
76
+ id: r.id,
77
+ title: r.title,
78
+ entityType: r.type, // Group by entity type, not relation type
79
+ relationLabel: getRelationLabel(r),
80
+ key: r.key,
81
+ });
82
+ }
83
+
84
+ return unique;
85
+ }, [relations, getRelationLabel, rootNodeId, entity.key]);
86
+
87
+ // Entity data type for layout
88
+
89
+ const computedNodes = useMemo<Node<CatalogEntityNodeData>[]>(() => {
90
+ if (!processedRelations.length) {
91
+ return [
92
+ {
93
+ id: rootNodeId,
94
+ type: GraphCustomNodeType.CatalogEntity,
95
+ position: { x: 0, y: 0 },
96
+ data: {
97
+ label: entity.title,
98
+ entityType: entity.type,
99
+ isRoot: true,
100
+ entityKey: entity.key,
101
+ },
102
+ sourcePosition: Position.Bottom,
103
+ targetPosition: Position.Top,
104
+ },
105
+ ];
106
+ }
107
+
108
+ // Group entities by their entity type
109
+ const entityTypeGroups = new Map<string, EntityGraphData[]>();
110
+ for (const rel of processedRelations) {
111
+ const entityData: EntityGraphData = {
112
+ id: rel.id,
113
+ title: rel.title,
114
+ entityType: rel.entityType,
115
+ relationLabel: rel.relationLabel,
116
+ key: rel.key,
117
+ };
118
+
119
+ const current = entityTypeGroups.get(rel.entityType);
120
+ if (current) {
121
+ current.push(entityData);
122
+ } else {
123
+ entityTypeGroups.set(rel.entityType, [entityData]);
124
+ }
125
+ }
126
+
127
+ // Sort entity types for consistent ordering
128
+ const entityTypes = Array.from(entityTypeGroups.keys()).sort();
129
+
130
+ // Layout constants
131
+ const rootY = 0;
132
+ const verticalGap = 80; // Gap between entities of same type (vertical)
133
+ const horizontalGap = 250; // Gap between different entity types (horizontal)
134
+ const topMargin = 240; // Distance from root to first row of entities
135
+
136
+ // Special handling for single entity type group - root on left, entities on right
137
+ const isSingleGroup = entityTypes.length === 1;
138
+
139
+ let rootX = 0;
140
+ let startX = 0;
141
+
142
+ if (isSingleGroup) {
143
+ // Position root on the left, entities on the right
144
+ rootX = -horizontalGap / 2;
145
+ startX = horizontalGap / 2;
146
+ } else {
147
+ // Calculate starting X position to center all groups (original behavior)
148
+ const totalWidth = (entityTypes.length - 1) * horizontalGap;
149
+ startX = -totalWidth / 2;
150
+ }
151
+
152
+ const nodes: Node<CatalogEntityNodeData>[] = [
153
+ // Root entity
154
+ {
155
+ id: rootNodeId,
156
+ type: GraphCustomNodeType.CatalogEntity,
157
+ position: { x: rootX, y: rootY },
158
+ data: { label: entity.title, entityType: entity.type, isRoot: true, entityKey: entity.key },
159
+ sourcePosition: Position.Bottom,
160
+ targetPosition: Position.Top,
161
+ },
162
+ ];
163
+
164
+ // Position entities by type groups
165
+ for (let typeIndex = 0; typeIndex < entityTypes.length; typeIndex++) {
166
+ const entityType = entityTypes[typeIndex];
167
+ const entitiesOfType = entityTypeGroups.get(entityType) ?? [];
168
+
169
+ // Calculate X position for this entity type group
170
+ const groupX = startX + typeIndex * horizontalGap;
171
+
172
+ // Calculate starting Y position to center entities vertically within the group
173
+ const groupHeight = (entitiesOfType.length - 1) * verticalGap;
174
+ const groupStartY = rootY + topMargin - groupHeight / 2;
175
+
176
+ // Position each entity within the group
177
+ for (let entityIndex = 0; entityIndex < entitiesOfType.length; entityIndex++) {
178
+ const entityData = entitiesOfType[entityIndex];
179
+ const entityY = groupStartY + entityIndex * verticalGap;
180
+
181
+ nodes.push({
182
+ id: entityData.id,
183
+ type: GraphCustomNodeType.CatalogEntity,
184
+ position: { x: groupX, y: entityY },
185
+ data: {
186
+ label: entityData.title,
187
+ entityType: entityData.entityType,
188
+ isRoot: false,
189
+ entityKey: entityData.key,
190
+ },
191
+ sourcePosition: Position.Bottom,
192
+ targetPosition: Position.Top,
193
+ });
194
+ }
195
+ }
196
+
197
+ return nodes;
198
+ }, [rootNodeId, entity.title, entity.type, entity.key, processedRelations]);
199
+
200
+ const computedEdges = useMemo<Edge[]>(() => {
201
+ return processedRelations.map((relation) => ({
202
+ id: `e-${rootNodeId}-${relation.id}`,
203
+ source: rootNodeId,
204
+ target: relation.id,
205
+ sourceHandle: GraphHandleType.Source, // Use the bottom handle of the center node
206
+ targetHandle: GraphHandleType.Target, // Use the target handle (top) of related nodes
207
+ type: GraphCustomEdgeType.CatalogEdge,
208
+ label: relation.relationLabel,
209
+ }));
210
+ }, [rootNodeId, processedRelations]);
211
+
212
+ const [nodes, setNodes, onNodesChange] =
213
+ useNodesState<Node<CatalogEntityNodeData>>(computedNodes);
214
+ const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(computedEdges);
215
+
216
+ useEffect(() => {
217
+ setNodes(computedNodes);
218
+ }, [computedNodes, setNodes]);
219
+
220
+ useEffect(() => {
221
+ setEdges(computedEdges);
222
+ }, [computedEdges, setEdges]);
223
+
224
+ const onConnect = useCallback(
225
+ (params: Connection) => setEdges((edgesSnapshot) => addEdge(params, edgesSnapshot)),
226
+ [setEdges],
227
+ );
228
+
229
+ return {
230
+ nodes,
231
+ edges,
232
+ onNodesChange,
233
+ onEdgesChange,
234
+ onConnect,
235
+ };
236
+ }
@@ -1,3 +1,5 @@
1
+ import { vi } from 'vitest';
2
+
1
3
  export class MockIntersectionObserver {
2
4
  public readonly root: Element | Document | null;
3
5
  public readonly rootMargin: string;
@@ -11,9 +13,9 @@ export class MockIntersectionObserver {
11
13
  this.root = null;
12
14
  this.rootMargin = '0px';
13
15
  this.thresholds = [1];
14
- this.observe = jest.fn;
15
- this.unobserve = jest.fn;
16
- this.disconnect = jest.fn;
16
+ this.observe = vi.fn();
17
+ this.unobserve = vi.fn();
18
+ this.disconnect = vi.fn();
17
19
  }
18
20
 
19
21
  takeRecords(): any[] {
@@ -37,6 +37,7 @@ export * from './use-element-size';
37
37
  export * from './use-time-ago';
38
38
  export * from './use-input-key-commands';
39
39
  export * from './catalog/useCatalogEntities';
40
+ export * from './catalog/useGraph';
40
41
  export * from './use-active-page-version';
41
42
  export * from './use-page-versions';
42
43
  export * from './use-user-teams';
@@ -1,4 +1,6 @@
1
- export const useMobileMenuItems = jest.fn().mockReturnValue({
1
+ import { vi } from 'vitest';
2
+
3
+ export const useMobileMenuItems = vi.fn().mockReturnValue({
2
4
  items: [
3
5
  {
4
6
  type: 'group',
@@ -20,7 +20,8 @@ export function usePageActions(pageSlug: string): PageAction[] {
20
20
 
21
21
  const { navigation } = useThemeConfig();
22
22
  // Can't use any actions if no markdown files are generated for LLMs
23
- const isLlmsDisabled = usePageProps()?.seo?.llmstxt?.hide === true;
23
+ const pageProps = usePageProps();
24
+ const isLlmsDisabled = pageProps?.seo?.llmstxt?.hide || pageProps?.frontmatter?.excludeFromSearch;
24
25
  const { isPublic } = usePageData() || {};
25
26
 
26
27
  const actions: PageAction[] = useMemo(() => {
@@ -60,6 +61,9 @@ export function usePageActions(pageSlug: string): PageAction[] {
60
61
  onClick: async () => {
61
62
  try {
62
63
  const result = await fetch(mdPageUrl);
64
+ if (result.status !== 200) {
65
+ throw new Error('Failed to fetch markdown content');
66
+ }
63
67
  const text = await result.text();
64
68
 
65
69
  ClipboardService.copyCustom(text);
@@ -29,3 +29,4 @@ export { useModalScrollLock } from '../hooks/use-modal-scroll-lock';
29
29
  export { SecurityVariablesEnvSuffix } from '../constants/environments';
30
30
  export { isUndefined, isString, isNotNull, isObject } from '../utils/type-guards';
31
31
  export { ThemeDataContext, type ThemeDataTransferObject } from '../contexts/ThemeDataContext';
32
+ export { ENTITY_RELATION_TYPES } from '../constants/catalog';
@@ -1,2 +1,3 @@
1
1
  export * from './global';
2
2
  export * from './dark';
3
+ export { xyflow } from './xyflow';