@eventcatalog/core 5.0.0-beta.2 → 5.0.0-beta.20
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/LICENSE +234 -134
- package/README.md +5 -5
- package/dist/analytics/analytics.cjs +18 -3
- package/dist/analytics/analytics.d.cts +1 -1
- package/dist/analytics/analytics.d.ts +1 -1
- package/dist/analytics/analytics.js +3 -2
- package/dist/analytics/log-build.cjs +71 -35
- package/dist/analytics/log-build.js +5 -4
- package/dist/analytics/telemetry.cjs +42 -0
- package/dist/analytics/telemetry.d.cts +20 -0
- package/dist/analytics/telemetry.d.ts +20 -0
- package/dist/analytics/telemetry.js +8 -0
- package/dist/catalog-runtime.cjs +1 -1
- package/dist/{chunk-HGRFXTFC.js → chunk-3VQSVQTU.js} +1 -1
- package/dist/{chunk-KIDISU6X.js → chunk-5ZUN2TXB.js} +56 -33
- package/dist/{chunk-TQLRG35T.js → chunk-AWOO63ME.js} +71 -45
- package/dist/chunk-CPW6G62V.js +17 -0
- package/dist/{chunk-4J2ZM2HX.js → chunk-F236OJZD.js} +1 -1
- package/dist/{chunk-XARPIXWF.js → chunk-FWH5T6U4.js} +9 -3
- package/dist/{chunk-62EV4IJA.js → chunk-R2X4ZEF6.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +214 -129
- package/dist/eventcatalog.config.d.cts +59 -1
- package/dist/eventcatalog.config.d.ts +59 -1
- package/dist/eventcatalog.js +39 -15
- package/dist/federation/federate.js +2 -2
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio-fonts.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.css.mjs +3 -0
- package/dist/runtime-dependencies/@eventcatalog__visualiser__studio.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__provider.mjs +3 -0
- package/dist/runtime-dependencies/@hocuspocus__server.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-local-relay.mjs +3 -0
- package/dist/runtime-dependencies/@mcp-b__webmcp-polyfill.mjs +3 -0
- package/dist/runtime-dependencies/htmlparser2.mjs +3 -0
- package/dist/runtime-dependencies/manifest.json +13 -1
- package/dist/runtime-dependencies/react-hotkeys-hook.mjs +3 -0
- package/dist/runtime-dependencies/react-syntax-highlighter__dist__cjs__create-element.js.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols.mjs +3 -0
- package/dist/runtime-dependencies/y-protocols__awareness.mjs +3 -0
- package/dist/runtime-dependencies/yjs.mjs +3 -0
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/dist/utils/license-status.cjs +71 -45
- package/dist/utils/license-status.d.cts +14 -5
- package/dist/utils/license-status.d.ts +14 -5
- package/dist/utils/license-status.js +1 -1
- package/eventcatalog/astro.config.mjs +23 -1
- package/eventcatalog/integrations/skip-startup-reload.mjs +31 -0
- package/eventcatalog/integrations/stable-dependency-order.mjs +29 -0
- package/eventcatalog/integrations/storage.mjs +20 -0
- package/eventcatalog/integrations/studio-server.mjs +32 -0
- package/eventcatalog/src/components/CatalogGraph/CatalogForceGraph.tsx +39 -0
- package/eventcatalog/src/components/ChatPanel/ChatPanel.tsx +2 -0
- package/eventcatalog/src/components/Grids/utils.tsx +0 -23
- package/eventcatalog/src/components/Header.astro +3 -3
- package/eventcatalog/src/components/MDX/CodeGroup/CodeGroup.tsx +6 -1
- package/eventcatalog/src/components/MDX/NodeGraph/AstroNodeGraph.tsx +42 -3
- package/eventcatalog/src/components/MDX/NodeGraph/NodeGraph.astro +5 -1
- package/eventcatalog/src/components/NavigationProgress.astro +7 -0
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaCodeModal.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +2 -0
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +12 -4
- package/eventcatalog/src/components/Search/SearchDataLoader.astro +16 -28
- package/eventcatalog/src/components/Search/SearchModal.tsx +2 -10
- package/eventcatalog/src/components/Search/search-utils.ts +26 -0
- package/eventcatalog/src/components/Seo.astro +5 -1
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +53 -83
- package/eventcatalog/src/components/SideNav/NestedSideBar/storage.ts +2 -0
- package/eventcatalog/src/components/SideNav/NestedSideBar/utils.ts +131 -0
- package/eventcatalog/src/components/SideNav/SideNav.astro +9 -1
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +4 -62
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +17 -150
- package/eventcatalog/src/components/Tables/Resources/ResourcesTable.tsx +425 -0
- package/eventcatalog/src/components/Tables/RowActionsMenu.tsx +157 -0
- package/eventcatalog/src/components/Tables/TablePagination.tsx +82 -0
- package/eventcatalog/src/features/ai/chat-api.ts +3 -3
- package/eventcatalog/src/features/auth/error.astro +4 -2
- package/eventcatalog/src/features/auth/login.astro +2 -1
- package/eventcatalog/src/features/auth/middleware/middleware-auth.ts +10 -0
- package/eventcatalog/src/features/auth/unauthorized.astro +1 -1
- package/eventcatalog/src/features/fields/pages/fields.astro +1 -1
- package/eventcatalog/src/features/integrations/eventcatalog-features.ts +34 -1
- package/eventcatalog/src/features/mcp/apps/architecture-diagram/node-chat.tsx +6 -45
- package/eventcatalog/src/features/mcp/apps/canvas/shared.ts +47 -0
- package/eventcatalog/src/features/mcp/apps/canvas/styles.css +24 -0
- package/eventcatalog/src/features/mcp/apps/canvas/view.tsx +278 -0
- package/eventcatalog/src/features/mcp/apps/generated/canvas.html +272 -0
- package/eventcatalog/src/features/mcp/apps/generated/viewer.html +77 -76
- package/eventcatalog/src/features/mcp/apps/shared/app-view.tsx +2 -1
- package/eventcatalog/src/features/mcp/mcp-apps.ts +6 -3
- package/eventcatalog/src/features/mcp/mcp-server.ts +36 -5
- package/eventcatalog/src/features/server/start.ts +25 -0
- package/eventcatalog/src/features/storage/database.ts +88 -0
- package/eventcatalog/src/features/storage/migrations.ts +85 -0
- package/eventcatalog/src/features/storage/sql/database.sql +5 -0
- package/eventcatalog/src/features/storage/sql/migrations/0001-studio-canvases.sql +12 -0
- package/eventcatalog/src/features/storage/sql/migrations.sql +15 -0
- package/eventcatalog/src/features/storage/sql.ts +28 -0
- package/eventcatalog/src/features/studio/agent-choreography.ts +384 -0
- package/eventcatalog/src/features/studio/agents.ts +28 -0
- package/eventcatalog/src/features/studio/api/canvases-api.ts +219 -0
- package/eventcatalog/src/features/studio/api/canvases.ts +25 -0
- package/eventcatalog/src/features/studio/api/client.ts +40 -0
- package/eventcatalog/src/features/studio/canvas-actions.ts +540 -0
- package/eventcatalog/src/features/studio/canvas-preview.ts +52 -0
- package/eventcatalog/src/features/studio/catalog-resources.ts +155 -0
- package/eventcatalog/src/features/studio/catalog.ts +173 -0
- package/eventcatalog/src/features/studio/components/CanvasHeader.tsx +518 -0
- package/eventcatalog/src/features/studio/components/CanvasStatusMenu.tsx +118 -0
- package/eventcatalog/src/features/studio/components/ConnectAgentDialog.tsx +434 -0
- package/eventcatalog/src/features/studio/components/ConnectionsChooser.tsx +143 -0
- package/eventcatalog/src/features/studio/components/DeleteCanvasDialog.tsx +65 -0
- package/eventcatalog/src/features/studio/components/Dialog.tsx +60 -0
- package/eventcatalog/src/features/studio/components/JoinForm.tsx +109 -0
- package/eventcatalog/src/features/studio/components/LeftPanel.tsx +203 -0
- package/eventcatalog/src/features/studio/components/NodeDetails.tsx +183 -0
- package/eventcatalog/src/features/studio/components/OpenInStudioDialog.tsx +79 -0
- package/eventcatalog/src/features/studio/components/Presence.tsx +293 -0
- package/eventcatalog/src/features/studio/components/ShareDialog.tsx +396 -0
- package/eventcatalog/src/features/studio/components/ShortcutsDialog.tsx +41 -0
- package/eventcatalog/src/features/studio/components/StudioDesigner.tsx +2091 -0
- package/eventcatalog/src/features/studio/components/StudioNotice.astro +24 -0
- package/eventcatalog/src/features/studio/document-name.ts +7 -0
- package/eventcatalog/src/features/studio/from-visualiser.ts +138 -0
- package/eventcatalog/src/features/studio/hooks/presence-sender.ts +63 -0
- package/eventcatalog/src/features/studio/hooks/presence-store.ts +119 -0
- package/eventcatalog/src/features/studio/hooks/use-canvas-webmcp.ts +353 -0
- package/eventcatalog/src/features/studio/hooks/use-comments.ts +54 -0
- package/eventcatalog/src/features/studio/hooks/use-follow-camera.ts +109 -0
- package/eventcatalog/src/features/studio/hooks/use-studio-flow.ts +782 -0
- package/eventcatalog/src/features/studio/identity.ts +59 -0
- package/eventcatalog/src/features/studio/limits.ts +2 -0
- package/eventcatalog/src/features/studio/pages/[id].astro +69 -0
- package/eventcatalog/src/features/studio/pages/index.astro +784 -0
- package/eventcatalog/src/features/studio/pages/webmcp-relay.ts +27 -0
- package/eventcatalog/src/features/studio/server/canvas-mcp.ts +495 -0
- package/eventcatalog/src/features/studio/server/runtime.ts +498 -0
- package/eventcatalog/src/features/studio/server/sign-in.ts +18 -0
- package/eventcatalog/src/features/studio/server/sql/storage.sql +18 -0
- package/eventcatalog/src/features/studio/server/storage.ts +90 -0
- package/eventcatalog/src/features/studio/server/tool-icons.ts +30 -0
- package/eventcatalog/src/features/studio/tool-sync.ts +75 -0
- package/eventcatalog/src/layouts/BaseLayout.astro +4 -4
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +13 -7
- package/eventcatalog/src/pages/_index.astro +1 -1
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.spec.ts +52 -63
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/_index.data.ts +34 -46
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +11 -29
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_ResourcesPage.astro +79 -0
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.spec.ts +32 -14
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_index.data.ts +63 -58
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/index.astro +2 -62
- package/eventcatalog/src/pages/docs/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/pages/docs/services/[id]/[version]/resources/index.astro +13 -0
- package/eventcatalog/src/pages/index.astro +1 -1
- package/eventcatalog/src/pages/schemas/explorer/index.astro +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/index.astro +1 -1
- package/eventcatalog/src/stores/sidebar-store/builders/domain.ts +24 -42
- package/eventcatalog/src/stores/sidebar-store/builders/flow.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/message.ts +2 -0
- package/eventcatalog/src/stores/sidebar-store/builders/service.ts +23 -23
- package/eventcatalog/src/stores/sidebar-store/builders/shared.ts +13 -0
- package/eventcatalog/src/stores/sidebar-store/builders/system.ts +18 -27
- package/eventcatalog/src/stores/sidebar-store/state.ts +5 -0
- package/eventcatalog/src/styles/tailwind.css +2 -2
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +10 -0
- package/eventcatalog/src/utils/collections/channels.ts +56 -1
- package/eventcatalog/src/utils/collections/flows.ts +50 -1
- package/eventcatalog/src/utils/collections/message-connections.ts +60 -0
- package/eventcatalog/src/utils/collections/message-triggers.ts +2 -1
- package/eventcatalog/src/utils/collections/resource-owners.ts +193 -0
- package/eventcatalog/src/utils/collections/resources.ts +310 -0
- package/eventcatalog/src/utils/collections/util.ts +31 -0
- package/eventcatalog/src/utils/feature.ts +8 -0
- package/eventcatalog/src/utils/git.ts +11 -5
- package/eventcatalog/src/utils/link-validation.ts +25 -19
- package/eventcatalog/src/utils/node-graphs/domain-entity-map.ts +3 -1
- package/eventcatalog/src/utils/node-graphs/domain-levels-node-graph.ts +3 -3
- package/eventcatalog/src/utils/node-graphs/export-mermaid.ts +0 -2
- package/eventcatalog/src/utils/node-graphs/layout-node-graph.ts +20 -1
- package/eventcatalog/src/utils/node-graphs/node-resource.ts +67 -0
- package/eventcatalog/src/utils/node-graphs/systems-node-graph.ts +1 -1
- package/eventcatalog/src/utils/page-title.ts +15 -0
- package/eventcatalog/src/utils/syntax-highlighter-renderer.ts +64 -0
- package/eventcatalog/src/utils/trial.ts +1 -1
- package/package.json +22 -16
- package/dist/runtime-dependencies/shelljs.mjs +0 -3
- package/eventcatalog/public/studio-bg.png +0 -0
- package/eventcatalog/src/components/Grids/DomainGrid.tsx +0 -838
- package/eventcatalog/src/components/Grids/MessageGrid.tsx +0 -344
- package/eventcatalog/src/components/Grids/SystemGrid.tsx +0 -185
- package/eventcatalog/src/components/Grids/components.tsx +0 -170
- package/eventcatalog/src/components/Grids/message-link.ts +0 -38
- package/eventcatalog/src/components/Studio/StudioPageModal.tsx +0 -150
- package/eventcatalog/src/components/Tables/SystemResources/SystemResourcesTable.tsx +0 -294
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.spec.ts +0 -65
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/resources/_resources.ts +0 -46
- package/eventcatalog/src/pages/studio.astro +0 -316
- package/dist/{chunk-AYNZXUB5.js → chunk-KHACCTW2.js} +3 -3
|
@@ -1,7 +1,130 @@
|
|
|
1
1
|
// Shared utilities for NestedSideBar components
|
|
2
2
|
|
|
3
|
+
import type { ChildRef, NavigationData, NavNode } from '@stores/sidebar-store/state';
|
|
4
|
+
|
|
3
5
|
export const SIDEBAR_GROUP_COLLAPSE_THRESHOLD = 5;
|
|
4
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Strip the configured base path from a pathname so URL pattern matching works
|
|
9
|
+
* regardless of the base path setting (e.g. `/eventcatalog/docs/...` → `/docs/...`).
|
|
10
|
+
*/
|
|
11
|
+
export const stripBasePath = (pathname: string): string => {
|
|
12
|
+
const base = __EC_BASE__.replace(/\/$/, ''); // e.g. '/eventcatalog'
|
|
13
|
+
if (base && pathname.startsWith(base)) {
|
|
14
|
+
return pathname.slice(base.length) || '/';
|
|
15
|
+
}
|
|
16
|
+
return pathname;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type NavigationLevel = {
|
|
20
|
+
key: string | null; // The key of the node that was drilled into (null for root)
|
|
21
|
+
entries: ChildRef[];
|
|
22
|
+
title: string;
|
|
23
|
+
badge?: string; // Category badge (e.g., "Domain", "Service")
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const rootLevel = (roots: ChildRef[]): NavigationLevel => ({ key: null, entries: roots, title: 'Documentation' });
|
|
27
|
+
|
|
28
|
+
/** Resolve a child reference to a node, following unversioned aliases (string → key). */
|
|
29
|
+
export const resolveNavRef = (nodes: NavigationData['nodes'], ref: ChildRef): NavNode | null => {
|
|
30
|
+
if (typeof ref !== 'string') return ref;
|
|
31
|
+
const node = nodes[ref];
|
|
32
|
+
if (!node) return null;
|
|
33
|
+
if (typeof node === 'string') return (nodes[node] as NavNode) ?? null;
|
|
34
|
+
return node;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** Map of "type:id" → versioned node key, used to match URLs to nodes. */
|
|
38
|
+
export const buildNodeLookup = (nodes: NavigationData['nodes']): Map<string, string> => {
|
|
39
|
+
const lookup = new Map<string, string>();
|
|
40
|
+
|
|
41
|
+
Object.entries(nodes).forEach(([key, value]) => {
|
|
42
|
+
// Skip keys that are references (string values pointing to other keys)
|
|
43
|
+
// These are unversioned aliases like "domain:E-Commerce" -> "domain:E-Commerce:1.0.0"
|
|
44
|
+
if (typeof value === 'string') return;
|
|
45
|
+
|
|
46
|
+
// Key formats:
|
|
47
|
+
// - "type:id:version" (e.g., "service:OrdersService:0.0.3")
|
|
48
|
+
// - "type:id" (e.g., "user:john", "team:backend") - non-versioned resources
|
|
49
|
+
// - "list:name" (e.g., "list:domains") - skip these
|
|
50
|
+
const parts = key.split(':');
|
|
51
|
+
|
|
52
|
+
// Skip list items
|
|
53
|
+
if (parts[0] === 'list') return;
|
|
54
|
+
|
|
55
|
+
if (parts.length >= 2) {
|
|
56
|
+
// Store as "type:id"
|
|
57
|
+
lookup.set(`${parts[0]}:${parts[1]}`, key);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return lookup;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What the sidebar shows for a URL before it knows the reader's saved drill-down path:
|
|
66
|
+
* the root level on the homepage or for pages without children, otherwise the matching
|
|
67
|
+
* resource's own level (the same "flattened" stack the client falls back to).
|
|
68
|
+
*/
|
|
69
|
+
export const getStackForUrl = (
|
|
70
|
+
url: string,
|
|
71
|
+
data: NavigationData,
|
|
72
|
+
nodeLookup: ReadonlyMap<string, string> = buildNodeLookup(data.nodes)
|
|
73
|
+
): NavigationLevel[] => {
|
|
74
|
+
const root = rootLevel(data.roots);
|
|
75
|
+
if (url === '/' || url === '') return [root];
|
|
76
|
+
|
|
77
|
+
const targetKey = findNodeKeyByUrl(url, data.nodes, nodeLookup);
|
|
78
|
+
const target = targetKey ? resolveNavRef(data.nodes, targetKey) : null;
|
|
79
|
+
if (!targetKey || !target?.pages?.length) return [root];
|
|
80
|
+
|
|
81
|
+
return [root, { key: targetKey, entries: target.pages, title: target.title, badge: target.badge }];
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Everything the sidebar needs to render one page's level, without the whole catalog: the
|
|
86
|
+
* stack for the URL and only the nodes that level shows (its items, and the contents of its
|
|
87
|
+
* groups). Rendered on the server so the sidebar appears with the HTML, and handed to the
|
|
88
|
+
* island so it keeps showing it until the full navigation data loads.
|
|
89
|
+
*/
|
|
90
|
+
export type SidebarSnapshot = {
|
|
91
|
+
url: string;
|
|
92
|
+
stack: NavigationLevel[];
|
|
93
|
+
data: NavigationData;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const nodeLookupCache = new WeakMap<NavigationData, Map<string, string>>();
|
|
97
|
+
|
|
98
|
+
export const buildSidebarSnapshot = (data: NavigationData, url: string): SidebarSnapshot => {
|
|
99
|
+
let nodeLookup = nodeLookupCache.get(data);
|
|
100
|
+
if (!nodeLookup) {
|
|
101
|
+
nodeLookup = buildNodeLookup(data.nodes);
|
|
102
|
+
nodeLookupCache.set(data, nodeLookup);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const stack = getStackForUrl(url, data, nodeLookup);
|
|
106
|
+
const level = stack[stack.length - 1];
|
|
107
|
+
const nodes: NavigationData['nodes'] = {};
|
|
108
|
+
|
|
109
|
+
const include = (ref: ChildRef) => {
|
|
110
|
+
if (typeof ref === 'string') {
|
|
111
|
+
if (ref in nodes) return;
|
|
112
|
+
const value = data.nodes[ref];
|
|
113
|
+
if (value === undefined) return;
|
|
114
|
+
nodes[ref] = value;
|
|
115
|
+
if (typeof value === 'string' && data.nodes[value] !== undefined) nodes[value] = data.nodes[value];
|
|
116
|
+
}
|
|
117
|
+
const node = resolveNavRef(data.nodes, ref);
|
|
118
|
+
// Groups render their children inline; items with children only need their own node
|
|
119
|
+
if (node?.type === 'group') node.pages?.forEach(include);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
if (level.key) include(level.key);
|
|
123
|
+
level.entries.forEach(include);
|
|
124
|
+
|
|
125
|
+
return { url, stack, data: { roots: data.roots, nodes } };
|
|
126
|
+
};
|
|
127
|
+
|
|
5
128
|
/**
|
|
6
129
|
* The user's overrides of each group's default collapse state, keyed by group id.
|
|
7
130
|
* A group is in at most one set; absence means "use the group's default".
|
|
@@ -67,6 +190,14 @@ export const toggleGroupCollapsed = (
|
|
|
67
190
|
* Finds the sidebar node represented by a resource URL.
|
|
68
191
|
* The URL must have its configured base path removed before it is passed in.
|
|
69
192
|
*/
|
|
193
|
+
/**
|
|
194
|
+
* Whether an item in a group drops its default per-collection icon because the group heading
|
|
195
|
+
* already conveys its type: every item under a nested group, and items whose icon is the
|
|
196
|
+
* group's own (e.g. events listed under Events). Custom icons (`leftIcon`) always show.
|
|
197
|
+
*/
|
|
198
|
+
export const hidesDefaultItemIcon = (group: NavNode, item: NavNode, isNested: boolean): boolean =>
|
|
199
|
+
isNested || (!!group.icon && group.icon === item.icon);
|
|
200
|
+
|
|
70
201
|
export const findNodeKeyByUrl = (
|
|
71
202
|
url: string,
|
|
72
203
|
nodes: Record<string, unknown>,
|
|
@@ -3,10 +3,18 @@ import type { HTMLAttributes } from 'astro/types';
|
|
|
3
3
|
|
|
4
4
|
interface Props extends Omit<HTMLAttributes<'div'>, 'children'> {}
|
|
5
5
|
import NestedSideBar from './NestedSideBar';
|
|
6
|
+
import { buildSidebarSnapshot, stripBasePath } from './NestedSideBar/utils';
|
|
7
|
+
import { getNestedSideBarData } from '@stores/sidebar-store/state';
|
|
6
8
|
import { ClientRouter } from 'astro:transitions';
|
|
9
|
+
|
|
10
|
+
// Render this page's sidebar level into the HTML so it shows before any JavaScript runs;
|
|
11
|
+
// the full navigation still loads from /api/sidebar-data.json (see SearchDataLoader)
|
|
12
|
+
const snapshot = buildSidebarSnapshot(await getNestedSideBarData(), stripBasePath(Astro.url.pathname));
|
|
7
13
|
---
|
|
8
14
|
|
|
9
15
|
<div {...Astro.props}>
|
|
10
16
|
<ClientRouter />
|
|
11
|
-
<NestedSideBar client:only="react"
|
|
17
|
+
<NestedSideBar client:only="react" snapshot={snapshot}>
|
|
18
|
+
<NestedSideBar slot="fallback" snapshot={snapshot} />
|
|
19
|
+
</NestedSideBar>
|
|
12
20
|
</div>
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type ColumnFiltersState,
|
|
12
12
|
type SortingState,
|
|
13
13
|
} from '@tanstack/react-table';
|
|
14
|
-
import { ArrowDown, ArrowUp, ArrowUpDown,
|
|
14
|
+
import { ArrowDown, ArrowUp, ArrowUpDown, SearchX, X, Search, Users } from 'lucide-react';
|
|
15
15
|
import { UserIcon } from '@heroicons/react/24/outline';
|
|
16
16
|
import { useEffect, useMemo, useState } from 'react';
|
|
17
17
|
import type { TableConfiguration } from '@types';
|
|
@@ -19,6 +19,7 @@ import { isSameVersion } from '@utils/collections/version-compare';
|
|
|
19
19
|
import { resolveIconUrl } from '@utils/icon';
|
|
20
20
|
import { FilterDropdown, CheckboxItem } from './FilterComponents';
|
|
21
21
|
import { getDiscoverColumns } from './columns';
|
|
22
|
+
import { TablePagination, useStoredPageSize } from '../TablePagination';
|
|
22
23
|
import { formatAdrStatus, type AdrStatus } from '@utils/collections/adr-constants';
|
|
23
24
|
import { buildDiscoverFilterSearch, filterKnownValues, parseDiscoverFilterSearch } from './url-filters';
|
|
24
25
|
|
|
@@ -243,18 +244,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
|
|
|
243
244
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
244
245
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
245
246
|
const [tableFilter, setTableFilter] = useState(initialUrlFilters.q);
|
|
246
|
-
const
|
|
247
|
-
const PAGE_SIZE_STORAGE_KEY = 'eventcatalog-discover-page-size';
|
|
248
|
-
const [pageSize, setPageSize] = useState<number>(() => {
|
|
249
|
-
if (typeof window === 'undefined') return 10;
|
|
250
|
-
const stored = Number(window.localStorage.getItem(PAGE_SIZE_STORAGE_KEY));
|
|
251
|
-
return PAGE_SIZE_OPTIONS.includes(stored) ? stored : 10;
|
|
252
|
-
});
|
|
253
|
-
useEffect(() => {
|
|
254
|
-
if (typeof window !== 'undefined') {
|
|
255
|
-
window.localStorage.setItem(PAGE_SIZE_STORAGE_KEY, String(pageSize));
|
|
256
|
-
}
|
|
257
|
-
}, [pageSize]);
|
|
247
|
+
const [pageSize, setPageSize] = useStoredPageSize('eventcatalog-discover-page-size');
|
|
258
248
|
const [showOnlyLatest, setShowOnlyLatest] = useState(initialUrlFilters.showOnlyLatest);
|
|
259
249
|
const [onlyShowDrafts, setOnlyShowDrafts] = useState(initialUrlFilters.onlyShowDrafts);
|
|
260
250
|
const [selectedDomains, setSelectedDomains] = useState<string[]>(() =>
|
|
@@ -1287,55 +1277,7 @@ export function DiscoverTable<T extends DiscoverTableData>({
|
|
|
1287
1277
|
</div>
|
|
1288
1278
|
</div>
|
|
1289
1279
|
|
|
1290
|
-
{
|
|
1291
|
-
<div className="flex-shrink-0 flex items-center justify-between px-6 py-3 border-t border-[rgb(var(--ec-page-border))]">
|
|
1292
|
-
<div className="flex items-center gap-3 text-xs text-[rgb(var(--ec-page-text-muted))]">
|
|
1293
|
-
{totalResults > 0 && (
|
|
1294
|
-
<span>
|
|
1295
|
-
<span className="font-medium text-[rgb(var(--ec-page-text))]">{table.getRowModel().rows.length}</span> of{' '}
|
|
1296
|
-
<span className="font-medium text-[rgb(var(--ec-page-text))]">{totalResults}</span> results
|
|
1297
|
-
</span>
|
|
1298
|
-
)}
|
|
1299
|
-
{totalResults > 0 && <span aria-hidden className="h-3 w-px bg-[rgb(var(--ec-page-border))]" />}
|
|
1300
|
-
<label className="flex items-center gap-1.5">
|
|
1301
|
-
<span>Per page</span>
|
|
1302
|
-
<select
|
|
1303
|
-
value={pageSize}
|
|
1304
|
-
onChange={(e) => setPageSize(Number(e.target.value))}
|
|
1305
|
-
className="cursor-pointer rounded bg-transparent px-1 py-0.5 font-medium text-[rgb(var(--ec-page-text))] hover:bg-[rgb(var(--ec-content-hover))] focus:bg-[rgb(var(--ec-content-hover))] focus:outline-none"
|
|
1306
|
-
>
|
|
1307
|
-
{PAGE_SIZE_OPTIONS.map((size) => (
|
|
1308
|
-
<option key={size} value={size}>
|
|
1309
|
-
{size}
|
|
1310
|
-
</option>
|
|
1311
|
-
))}
|
|
1312
|
-
</select>
|
|
1313
|
-
</label>
|
|
1314
|
-
</div>
|
|
1315
|
-
<div className="flex items-center gap-1.5">
|
|
1316
|
-
<button
|
|
1317
|
-
className="p-1.5 text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-page-text))] disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
|
1318
|
-
onClick={() => table.previousPage()}
|
|
1319
|
-
disabled={!table.getCanPreviousPage()}
|
|
1320
|
-
title="Previous page"
|
|
1321
|
-
>
|
|
1322
|
-
<ChevronLeft className="w-4 h-4" />
|
|
1323
|
-
</button>
|
|
1324
|
-
<span className="text-xs tabular-nums text-[rgb(var(--ec-page-text-muted))] min-w-[60px] text-center">
|
|
1325
|
-
<span className="font-medium text-[rgb(var(--ec-page-text))]">{table.getState().pagination.pageIndex + 1}</span>
|
|
1326
|
-
{' / '}
|
|
1327
|
-
<span>{table.getPageCount() || 1}</span>
|
|
1328
|
-
</span>
|
|
1329
|
-
<button
|
|
1330
|
-
className="p-1.5 text-[rgb(var(--ec-icon-color))] hover:text-[rgb(var(--ec-page-text))] disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
|
|
1331
|
-
onClick={() => table.nextPage()}
|
|
1332
|
-
disabled={!table.getCanNextPage()}
|
|
1333
|
-
title="Next page"
|
|
1334
|
-
>
|
|
1335
|
-
<ChevronRight className="w-4 h-4" />
|
|
1336
|
-
</button>
|
|
1337
|
-
</div>
|
|
1338
|
-
</div>
|
|
1280
|
+
<TablePagination table={table} pageSize={pageSize} onPageSizeChange={setPageSize} />
|
|
1339
1281
|
</div>
|
|
1340
1282
|
</div>
|
|
1341
1283
|
);
|
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
import { createColumnHelper } from '@tanstack/react-table';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DocumentTextIcon, MapIcon } from '@heroicons/react/24/solid';
|
|
5
|
-
import {
|
|
6
|
-
ArrowDownIcon,
|
|
7
|
-
ArrowTopRightOnSquareIcon,
|
|
8
|
-
ArrowUpIcon,
|
|
9
|
-
EllipsisVerticalIcon,
|
|
10
|
-
StarIcon,
|
|
11
|
-
} from '@heroicons/react/24/outline';
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { ArrowDownIcon, ArrowTopRightOnSquareIcon, ArrowUpIcon } from '@heroicons/react/24/outline';
|
|
12
4
|
import { buildUrl } from '@utils/url-builder';
|
|
13
5
|
import { getColorAndIconForCollection } from '@utils/collections/icons';
|
|
14
6
|
import { getCollectionTextColorClass } from '@utils/collection-colors';
|
|
15
7
|
import { getBadgeHref, getBadgeReactStyle } from '@utils/badge-styles';
|
|
16
8
|
import { isIconPath, resolveIconUrl } from '@utils/icon';
|
|
17
|
-
import { useStore } from '@nanostores/react';
|
|
18
|
-
import { favoritesStore, toggleFavorite, type FavoriteItem } from '../../../stores/favorites-store';
|
|
19
9
|
import type { DiscoverTableData, CollectionType } from './DiscoverTable';
|
|
20
10
|
import type { TableConfiguration } from '@types';
|
|
21
11
|
import { formatAdrDate, isAdrCollection } from '@utils/collections/adr-constants';
|
|
22
12
|
import { CornerDownRight } from 'lucide-react';
|
|
13
|
+
import { RowActionsMenu } from '../RowActionsMenu';
|
|
23
14
|
|
|
24
15
|
const columnHelper = createColumnHelper<DiscoverTableData>();
|
|
25
16
|
|
|
@@ -167,147 +158,23 @@ const ResourceNameCell = ({ item }: { item: DiscoverTableData }) => {
|
|
|
167
158
|
);
|
|
168
159
|
};
|
|
169
160
|
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
const RowActionsMenu = ({ item, collectionType }: { item: DiscoverTableData; collectionType: CollectionType }) => {
|
|
174
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
175
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
176
|
-
const menuRef = useRef<HTMLDivElement>(null);
|
|
177
|
-
const [menuPosition, setMenuPosition] = useState<{ top: number; left: number } | null>(null);
|
|
178
|
-
const favorites = useStore(favoritesStore);
|
|
179
|
-
const href = buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`);
|
|
180
|
-
const visualiserHref = buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`);
|
|
181
|
-
const hasVisualiser = !isAdrCollection(item.collection);
|
|
182
|
-
const nodeKey = `${item.collection}-${item.data.id}-${item.data.version}`;
|
|
183
|
-
const badgeLabel =
|
|
161
|
+
// Discover keys favorites as `${collection}-${id}-${version}`; keep that so existing favorites still match.
|
|
162
|
+
const DiscoverRowActions = ({ item, collectionType }: { item: DiscoverTableData; collectionType: CollectionType }) => {
|
|
163
|
+
const badge =
|
|
184
164
|
collectionType === 'external-systems'
|
|
185
165
|
? 'External System'
|
|
186
166
|
: collectionType.charAt(0).toUpperCase() + collectionType.slice(1, -1);
|
|
187
|
-
const isFavorite = favorites.some((fav) => fav.nodeKey === nodeKey);
|
|
188
|
-
|
|
189
|
-
// The table lives inside scrollable, overflow-hidden containers, so the menu is
|
|
190
|
-
// rendered in a portal with fixed positioning to avoid being clipped.
|
|
191
|
-
const updatePosition = () => {
|
|
192
|
-
const button = buttonRef.current;
|
|
193
|
-
if (!button) return;
|
|
194
|
-
const rect = button.getBoundingClientRect();
|
|
195
|
-
const menuHeight = menuRef.current?.offsetHeight ?? 0;
|
|
196
|
-
const spaceBelow = window.innerHeight - rect.bottom;
|
|
197
|
-
|
|
198
|
-
// Flip above the trigger when there isn't enough room below it.
|
|
199
|
-
const openUpwards = menuHeight > 0 && spaceBelow < menuHeight + MENU_GAP && rect.top > spaceBelow;
|
|
200
|
-
const top = openUpwards ? rect.top - menuHeight - MENU_GAP : rect.bottom + MENU_GAP;
|
|
201
|
-
const left = Math.max(MENU_GAP, rect.right - MENU_WIDTH);
|
|
202
|
-
|
|
203
|
-
setMenuPosition({ top, left });
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
useLayoutEffect(() => {
|
|
207
|
-
if (!isOpen) return;
|
|
208
|
-
updatePosition();
|
|
209
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
210
|
-
}, [isOpen]);
|
|
211
|
-
|
|
212
|
-
useEffect(() => {
|
|
213
|
-
if (!isOpen) return;
|
|
214
|
-
|
|
215
|
-
const handlePointerDown = (event: MouseEvent) => {
|
|
216
|
-
const target = event.target as Node;
|
|
217
|
-
if (!menuRef.current?.contains(target) && !buttonRef.current?.contains(target)) {
|
|
218
|
-
setIsOpen(false);
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const handleEscape = (event: KeyboardEvent) => {
|
|
223
|
-
if (event.key === 'Escape') {
|
|
224
|
-
setIsOpen(false);
|
|
225
|
-
}
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
const handleReposition = () => updatePosition();
|
|
229
|
-
|
|
230
|
-
document.addEventListener('mousedown', handlePointerDown);
|
|
231
|
-
document.addEventListener('keydown', handleEscape);
|
|
232
|
-
window.addEventListener('resize', handleReposition);
|
|
233
|
-
window.addEventListener('scroll', handleReposition, true);
|
|
234
|
-
|
|
235
|
-
return () => {
|
|
236
|
-
document.removeEventListener('mousedown', handlePointerDown);
|
|
237
|
-
document.removeEventListener('keydown', handleEscape);
|
|
238
|
-
window.removeEventListener('resize', handleReposition);
|
|
239
|
-
window.removeEventListener('scroll', handleReposition, true);
|
|
240
|
-
};
|
|
241
|
-
}, [isOpen]);
|
|
242
|
-
|
|
243
|
-
const handleToggleFavorite = () => {
|
|
244
|
-
const favoriteItem: FavoriteItem = {
|
|
245
|
-
nodeKey,
|
|
246
|
-
path: [],
|
|
247
|
-
title: item.data.name,
|
|
248
|
-
badge: badgeLabel,
|
|
249
|
-
href,
|
|
250
|
-
};
|
|
251
|
-
toggleFavorite(favoriteItem);
|
|
252
|
-
setIsOpen(false);
|
|
253
|
-
};
|
|
254
|
-
|
|
255
167
|
return (
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
</button>
|
|
267
|
-
|
|
268
|
-
{isOpen &&
|
|
269
|
-
typeof document !== 'undefined' &&
|
|
270
|
-
createPortal(
|
|
271
|
-
<div
|
|
272
|
-
ref={menuRef}
|
|
273
|
-
role="menu"
|
|
274
|
-
style={{
|
|
275
|
-
position: 'fixed',
|
|
276
|
-
top: menuPosition?.top ?? -9999,
|
|
277
|
-
left: menuPosition?.left ?? -9999,
|
|
278
|
-
width: MENU_WIDTH,
|
|
279
|
-
visibility: menuPosition ? 'visible' : 'hidden',
|
|
280
|
-
}}
|
|
281
|
-
className="z-50 overflow-hidden rounded-xl border border-[rgb(var(--ec-page-border))] bg-[rgb(var(--ec-dropdown-bg))] shadow-xl"
|
|
282
|
-
>
|
|
283
|
-
<a
|
|
284
|
-
href={href}
|
|
285
|
-
className="flex items-center gap-2.5 px-3 py-2.5 text-xs font-medium text-[rgb(var(--ec-page-text))] transition-colors hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-accent))]"
|
|
286
|
-
>
|
|
287
|
-
<DocumentTextIcon className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))]" />
|
|
288
|
-
View documentation
|
|
289
|
-
</a>
|
|
290
|
-
{hasVisualiser && (
|
|
291
|
-
<a
|
|
292
|
-
href={visualiserHref}
|
|
293
|
-
className="flex items-center gap-2.5 px-3 py-2.5 text-xs font-medium text-[rgb(var(--ec-page-text))] transition-colors hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-accent))]"
|
|
294
|
-
>
|
|
295
|
-
<MapIcon className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))]" />
|
|
296
|
-
View in visualiser
|
|
297
|
-
</a>
|
|
298
|
-
)}
|
|
299
|
-
<button
|
|
300
|
-
type="button"
|
|
301
|
-
onClick={handleToggleFavorite}
|
|
302
|
-
className="flex w-full items-center gap-2.5 px-3 py-2.5 text-left text-xs font-medium text-[rgb(var(--ec-page-text))] transition-colors hover:bg-[rgb(var(--ec-content-hover))] hover:text-[rgb(var(--ec-accent))]"
|
|
303
|
-
>
|
|
304
|
-
<StarIcon className="h-3.5 w-3.5 text-[rgb(var(--ec-page-text-muted))]" />
|
|
305
|
-
{isFavorite ? 'Remove from favorites' : 'Add to favorites'}
|
|
306
|
-
</button>
|
|
307
|
-
</div>,
|
|
308
|
-
document.body
|
|
309
|
-
)}
|
|
310
|
-
</div>
|
|
168
|
+
<RowActionsMenu
|
|
169
|
+
title={item.data.name}
|
|
170
|
+
href={buildUrl(`/docs/${item.collection}/${item.data.id}/${item.data.version}`)}
|
|
171
|
+
visualiserHref={
|
|
172
|
+
isAdrCollection(item.collection)
|
|
173
|
+
? undefined
|
|
174
|
+
: buildUrl(`/visualiser/${item.collection}/${item.data.id}/${item.data.version}`)
|
|
175
|
+
}
|
|
176
|
+
favorite={{ nodeKey: `${item.collection}-${item.data.id}-${item.data.version}`, badge }}
|
|
177
|
+
/>
|
|
311
178
|
);
|
|
312
179
|
};
|
|
313
180
|
|
|
@@ -319,7 +186,7 @@ const createActionsColumn = (collectionType: CollectionType, tableConfiguration:
|
|
|
319
186
|
enableSorting: false,
|
|
320
187
|
cell: (info) => {
|
|
321
188
|
const item = info.row.original;
|
|
322
|
-
return <
|
|
189
|
+
return <DiscoverRowActions item={item} collectionType={collectionType} />;
|
|
323
190
|
},
|
|
324
191
|
meta: {
|
|
325
192
|
showFilter: false,
|