@eventcatalog/core 3.29.2 → 3.30.0
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/dist/analytics/analytics.cjs +1 -1
- package/dist/analytics/analytics.js +2 -2
- package/dist/analytics/log-build.cjs +1 -1
- package/dist/analytics/log-build.js +3 -3
- package/dist/{chunk-36IA4UE4.js → chunk-6UG4JMUV.js} +1 -1
- package/dist/{chunk-MEJOYC5Z.js → chunk-ATRBVTJ6.js} +1 -1
- package/dist/{chunk-VEUNSJ6Z.js → chunk-MVZKHUX2.js} +1 -1
- package/dist/{chunk-DB4IQ3GB.js → chunk-RRBDF4MM.js} +1 -1
- package/dist/{chunk-EGQGCB2B.js → chunk-Z26P4PCB.js} +1 -1
- package/dist/constants.cjs +1 -1
- package/dist/constants.js +1 -1
- package/dist/eventcatalog.cjs +1 -1
- package/dist/eventcatalog.js +5 -5
- package/dist/generate.cjs +1 -1
- package/dist/generate.js +3 -3
- package/dist/utils/cli-logger.cjs +1 -1
- package/dist/utils/cli-logger.js +2 -2
- package/eventcatalog/astro.config.mjs +1 -1
- package/eventcatalog/public/logo.png +0 -0
- package/eventcatalog/src/components/CopyAsMarkdown.tsx +2 -2
- package/eventcatalog/src/components/EnvironmentDropdown.tsx +33 -21
- package/eventcatalog/src/components/FieldsExplorer/FieldFilters.tsx +3 -53
- package/eventcatalog/src/components/FieldsExplorer/FieldsExplorer.tsx +144 -91
- package/eventcatalog/src/components/FieldsExplorer/FieldsTable.tsx +112 -109
- package/eventcatalog/src/components/Header.astro +9 -19
- package/eventcatalog/src/components/MDX/Accordion/Accordion.tsx +12 -14
- package/eventcatalog/src/components/MDX/Accordion/AccordionGroup.astro +11 -3
- package/eventcatalog/src/components/MDX/ResourceRef/ResourceRef.astro +15 -5
- package/eventcatalog/src/components/SchemaExplorer/ApiContentViewer.tsx +164 -53
- package/eventcatalog/src/components/SchemaExplorer/DiffViewer.tsx +1 -1
- package/eventcatalog/src/components/SchemaExplorer/ExamplesViewer.tsx +4 -4
- package/eventcatalog/src/components/SchemaExplorer/Pagination.tsx +12 -10
- package/eventcatalog/src/components/SchemaExplorer/SchemaContentViewer.tsx +48 -77
- package/eventcatalog/src/components/SchemaExplorer/SchemaDetailsPanel.tsx +238 -169
- package/eventcatalog/src/components/SchemaExplorer/SchemaExplorer.tsx +189 -230
- package/eventcatalog/src/components/SchemaExplorer/SchemaListItem.tsx +39 -36
- package/eventcatalog/src/components/Search/Search.astro +1 -1
- package/eventcatalog/src/components/Seo.astro +1 -1
- package/eventcatalog/src/components/SideNav/NestedSideBar/SearchBar.tsx +3 -3
- package/eventcatalog/src/components/SideNav/NestedSideBar/index.tsx +229 -256
- package/eventcatalog/src/components/Tables/Discover/DiscoverTable.tsx +78 -59
- package/eventcatalog/src/components/Tables/Discover/columns.tsx +130 -197
- package/eventcatalog/src/components/Tables/Table.tsx +21 -18
- package/eventcatalog/src/components/Tables/columns/TeamsTableColumns.tsx +79 -131
- package/eventcatalog/src/components/Tables/columns/UserTableColumns.tsx +104 -175
- package/eventcatalog/src/enterprise/auth/error.astro +1 -1
- package/eventcatalog/src/enterprise/auth/login.astro +1 -1
- package/eventcatalog/src/enterprise/custom-documentation/components/CustomDocsNav/index.tsx +95 -93
- package/eventcatalog/src/enterprise/custom-documentation/pages/docs/custom/index.astro +174 -136
- package/eventcatalog/src/enterprise/fields/pages/fields.astro +10 -8
- package/eventcatalog/src/enterprise/integrations/eventcatalog-features.ts +0 -8
- package/eventcatalog/src/layouts/DirectoryLayout.astro +17 -88
- package/eventcatalog/src/layouts/VerticalSideBarLayout.astro +528 -146
- package/eventcatalog/src/layouts/VisualiserLayout.astro +7 -2
- package/eventcatalog/src/pages/_index.astro +5 -3
- package/eventcatalog/src/pages/architecture/[type]/[id]/[version]/index.astro +3 -3
- package/eventcatalog/src/pages/diagrams/[id]/[version]/index.astro +223 -73
- package/eventcatalog/src/pages/discover/[type]/index.astro +22 -141
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/[docVersion]/index.astro +129 -29
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/[docType]/[docId]/index.astro +129 -29
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/asyncapi/[filename].astro +6 -2
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/examples/[...filename].astro +2 -2
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/graphql/[filename].astro +21 -18
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/index.astro +33 -32
- package/eventcatalog/src/pages/docs/[type]/[id]/[version]/spec/[filename].astro +5 -1
- package/eventcatalog/src/pages/docs/[type]/[id]/language/[dictionaryId]/index.astro +2 -2
- package/eventcatalog/src/pages/docs/[type]/[id]/language/index.astro +4 -6
- package/eventcatalog/src/pages/docs/teams/[id]/index.astro +11 -4
- package/eventcatalog/src/pages/docs/users/[id]/index.astro +11 -4
- package/eventcatalog/src/pages/schemas/explorer/index.astro +10 -8
- package/eventcatalog/src/pages/studio.astro +1 -1
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/entity-map/index.astro +2 -7
- package/eventcatalog/src/pages/visualiser/[type]/[id]/[version]/index.astro +2 -2
- package/eventcatalog/src/pages/visualiser/domains/[id]/[version]/entity-map/index.astro +2 -7
- package/eventcatalog/src/styles/theme.css +68 -12
- package/eventcatalog/src/types/react-syntax-highlighter.d.ts +13 -0
- package/package.json +1 -1
- package/eventcatalog/public/logo.svg +0 -14
- package/eventcatalog/src/enterprise/plans/index.astro +0 -319
|
@@ -4,7 +4,6 @@ import { useState, useEffect, useCallback, useMemo } from 'react';
|
|
|
4
4
|
import * as LucideIcons from 'lucide-react';
|
|
5
5
|
import { ChevronRight, ChevronLeft, ChevronDown, Home, Star, FileQuestion } from 'lucide-react';
|
|
6
6
|
import type { NavNode, ChildRef } from '@stores/sidebar-store/state';
|
|
7
|
-
import SearchBar from './SearchBar';
|
|
8
7
|
import { saveState, loadState, saveCollapsedSections, loadCollapsedSections } from './storage';
|
|
9
8
|
import { useStore } from '@nanostores/react';
|
|
10
9
|
import { sidebarStore } from '@stores/sidebar-store';
|
|
@@ -61,7 +60,6 @@ export default function NestedSideBar() {
|
|
|
61
60
|
const [collapsedSections, setCollapsedSections] = useState<Set<string>>(new Set());
|
|
62
61
|
const [showPathPreview, setShowPathPreview] = useState(false);
|
|
63
62
|
const [showFullPath, setShowFullPath] = useState(false);
|
|
64
|
-
const [isSearching, setIsSearching] = useState(false);
|
|
65
63
|
|
|
66
64
|
// Build a lookup map for faster URL navigation
|
|
67
65
|
// Map format: "type:id" -> "nodeKey"
|
|
@@ -503,13 +501,13 @@ export default function NestedSideBar() {
|
|
|
503
501
|
// Show loading state if no data yet
|
|
504
502
|
if (!data || roots.length === 0) {
|
|
505
503
|
return (
|
|
506
|
-
<aside className="w-
|
|
504
|
+
<aside className="w-full min-h-full flex-1 flex flex-col font-sans bg-[rgb(var(--ec-page-bg))]">
|
|
507
505
|
{/* Search skeleton */}
|
|
508
|
-
<div className="px-
|
|
509
|
-
<div className="h-
|
|
506
|
+
<div className="px-4 py-3 border-b border-[rgb(var(--ec-content-border))] bg-[rgb(var(--ec-page-bg))]">
|
|
507
|
+
<div className="h-10 bg-[rgb(var(--ec-content-hover))] rounded-xl animate-pulse" />
|
|
510
508
|
</div>
|
|
511
509
|
{/* Content skeleton */}
|
|
512
|
-
<div className="p-
|
|
510
|
+
<div className="p-4 space-y-3">
|
|
513
511
|
{/* Group header skeleton */}
|
|
514
512
|
<div className="flex items-center gap-2 px-2 py-1.5">
|
|
515
513
|
<div className="w-3.5 h-3.5 bg-[rgb(var(--ec-content-hover))] rounded animate-pulse" />
|
|
@@ -665,32 +663,6 @@ export default function NestedSideBar() {
|
|
|
665
663
|
|
|
666
664
|
const isTopLevel = navigationStack.length === 1;
|
|
667
665
|
|
|
668
|
-
/**
|
|
669
|
-
* Navigate to a search result
|
|
670
|
-
*/
|
|
671
|
-
const navigateToSearchResult = (nodeKey: string, node: NavNode) => {
|
|
672
|
-
// If it's a leaf node with href, navigate directly
|
|
673
|
-
if (node.href && (!node.pages || node.pages.length === 0)) {
|
|
674
|
-
window.location.href = node.href;
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
// If it has children, drill down to it
|
|
679
|
-
if (node.pages && node.pages.length > 0) {
|
|
680
|
-
setSlideDirection('forward');
|
|
681
|
-
setAnimationKey((prev) => prev + 1);
|
|
682
|
-
setNavigationStack([
|
|
683
|
-
{ key: null, entries: roots, title: 'Documentation' },
|
|
684
|
-
{ key: nodeKey, entries: node.pages, title: node.title, badge: node.badge },
|
|
685
|
-
]);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
setIsSearching(false);
|
|
689
|
-
// Reset hover states
|
|
690
|
-
setShowPathPreview(false);
|
|
691
|
-
setShowFullPath(false);
|
|
692
|
-
};
|
|
693
|
-
|
|
694
666
|
/**
|
|
695
667
|
* Render a list of child refs (resolving keys as needed)
|
|
696
668
|
*/
|
|
@@ -779,8 +751,8 @@ export default function NestedSideBar() {
|
|
|
779
751
|
className={cn(
|
|
780
752
|
'tracking-tight',
|
|
781
753
|
isSubtleGroup
|
|
782
|
-
? 'text-[
|
|
783
|
-
: 'text-[
|
|
754
|
+
? 'text-[11px] text-[rgb(var(--ec-content-text-muted))] font-medium'
|
|
755
|
+
: 'text-[12px] text-[rgb(var(--ec-content-text))] font-semibold'
|
|
784
756
|
)}
|
|
785
757
|
>
|
|
786
758
|
{group.title}
|
|
@@ -891,7 +863,7 @@ export default function NestedSideBar() {
|
|
|
891
863
|
{item.leftIcon && <img src={resolveIconUrl(item.leftIcon)} alt="" loading="lazy" className="w-4 h-4 flex-shrink-0" />}
|
|
892
864
|
<span
|
|
893
865
|
className={cn(
|
|
894
|
-
'text-[
|
|
866
|
+
'text-[12px] truncate',
|
|
895
867
|
isActive
|
|
896
868
|
? 'text-[rgb(var(--ec-accent-text))] font-medium'
|
|
897
869
|
: 'text-[rgb(var(--ec-content-text-secondary))] group-hover:text-[rgb(var(--ec-content-text))]'
|
|
@@ -924,10 +896,10 @@ export default function NestedSideBar() {
|
|
|
924
896
|
);
|
|
925
897
|
|
|
926
898
|
const baseClasses =
|
|
927
|
-
'group flex items-center justify-between w-full px-3 py-
|
|
899
|
+
'group flex items-center justify-between w-full px-3 py-2 border border-transparent cursor-pointer text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] active:bg-[rgb(var(--ec-content-hover))]';
|
|
928
900
|
const parentClasses = itemHasChildren ? 'font-medium' : '';
|
|
929
901
|
const activeClasses = isActive
|
|
930
|
-
? 'bg-[rgb(var(--ec-accent-subtle))] hover:bg-[rgb(var(--ec-accent-subtle))]
|
|
902
|
+
? 'border-[rgb(var(--ec-accent)/0.16)] bg-[rgb(var(--ec-accent-subtle))] hover:bg-[rgb(var(--ec-accent-subtle))] shadow-sm'
|
|
931
903
|
: '';
|
|
932
904
|
|
|
933
905
|
// Leaf item with href → render as link
|
|
@@ -966,251 +938,252 @@ export default function NestedSideBar() {
|
|
|
966
938
|
};
|
|
967
939
|
|
|
968
940
|
return (
|
|
969
|
-
<aside className="w-
|
|
970
|
-
{
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
941
|
+
<aside className="w-full min-h-full flex-1 flex flex-col font-sans bg-[rgb(var(--ec-page-bg))]">
|
|
942
|
+
{isTopLevel && (
|
|
943
|
+
<div className="px-4 py-[13.5px] bg-[rgb(var(--ec-page-bg)/0.98)] backdrop-blur-sm border-b border-[rgb(var(--ec-content-border))] sticky top-0 z-10">
|
|
944
|
+
<div className="flex items-center w-full px-2 py-1.5">
|
|
945
|
+
<span className="text-[12px] font-semibold text-[rgb(var(--ec-content-text))] truncate">All resources</span>
|
|
946
|
+
</div>
|
|
947
|
+
</div>
|
|
948
|
+
)}
|
|
949
|
+
|
|
950
|
+
{!isTopLevel && (
|
|
951
|
+
<div
|
|
952
|
+
className="px-4 py-[13.5px] bg-[rgb(var(--ec-page-bg)/0.98)] backdrop-blur-sm border-b border-[rgb(var(--ec-content-border))] sticky top-0 z-10"
|
|
953
|
+
onMouseEnter={() => !isTopLevel && setShowPathPreview(true)}
|
|
954
|
+
onMouseLeave={() => {
|
|
955
|
+
setShowPathPreview(false);
|
|
956
|
+
setShowFullPath(false);
|
|
957
|
+
}}
|
|
958
|
+
>
|
|
959
|
+
<button
|
|
960
|
+
onClick={navigateBack}
|
|
961
|
+
disabled={isTopLevel}
|
|
962
|
+
className={cn(
|
|
963
|
+
'flex items-center gap-2 w-full px-2 py-1.5 -mx-2 rounded-md transition-colors',
|
|
964
|
+
!isTopLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
965
|
+
isTopLevel && 'cursor-default'
|
|
966
|
+
)}
|
|
967
|
+
>
|
|
968
|
+
<span
|
|
969
|
+
className={cn(
|
|
970
|
+
'flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] transition-all',
|
|
971
|
+
isTopLevel && 'opacity-0',
|
|
972
|
+
!isTopLevel && 'group-hover:-translate-x-0.5'
|
|
973
|
+
)}
|
|
984
974
|
>
|
|
985
|
-
<
|
|
986
|
-
|
|
987
|
-
|
|
975
|
+
<ChevronLeft className="w-4 h-4" />
|
|
976
|
+
</span>
|
|
977
|
+
<span className="text-[12px] font-semibold text-[rgb(var(--ec-content-text))] truncate">{currentLevel.title}</span>
|
|
978
|
+
{currentLevel.badge && (
|
|
979
|
+
<span
|
|
988
980
|
className={cn(
|
|
989
|
-
'
|
|
990
|
-
|
|
991
|
-
isTopLevel && 'cursor-default'
|
|
981
|
+
'ml-auto px-2 py-0.5 text-[8px] font-semibold uppercase tracking-wide rounded',
|
|
982
|
+
getBadgeClasses(currentLevel.badge)
|
|
992
983
|
)}
|
|
993
984
|
>
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
!isTopLevel && 'group-hover:-translate-x-0.5'
|
|
999
|
-
)}
|
|
1000
|
-
>
|
|
1001
|
-
<ChevronLeft className="w-4 h-4" />
|
|
1002
|
-
</span>
|
|
1003
|
-
<span className="text-sm font-semibold text-[rgb(var(--ec-content-text))] truncate">{currentLevel.title}</span>
|
|
1004
|
-
{currentLevel.badge && (
|
|
1005
|
-
<span
|
|
1006
|
-
className={cn(
|
|
1007
|
-
'ml-auto px-2 py-0.5 text-[9px] font-semibold uppercase tracking-wide rounded',
|
|
1008
|
-
getBadgeClasses(currentLevel.badge)
|
|
1009
|
-
)}
|
|
1010
|
-
>
|
|
1011
|
-
{currentLevel.badge}
|
|
1012
|
-
</span>
|
|
1013
|
-
)}
|
|
1014
|
-
</button>
|
|
1015
|
-
|
|
1016
|
-
{/* Path Preview Dropdown */}
|
|
1017
|
-
{showPathPreview && navigationStack.length > 1 && (
|
|
1018
|
-
<div className="absolute left-0 right-0 top-full bg-[rgb(var(--ec-content-bg))] border-b border-[rgb(var(--ec-content-border))] shadow-lg z-20">
|
|
1019
|
-
<div className="px-3 py-2">
|
|
1020
|
-
<div className="text-[10px] font-medium text-[rgb(var(--ec-content-text-muted))] uppercase tracking-wide mb-2">
|
|
1021
|
-
Navigation Path
|
|
1022
|
-
</div>
|
|
1023
|
-
<div className="flex flex-col gap-0.5">
|
|
1024
|
-
{(() => {
|
|
1025
|
-
const SHOW_FIRST = 2; // Show first N items
|
|
1026
|
-
const SHOW_LAST = 2; // Show last N items (including current)
|
|
1027
|
-
const totalItems = navigationStack.length;
|
|
1028
|
-
const hiddenCount = totalItems - SHOW_FIRST - SHOW_LAST;
|
|
1029
|
-
const shouldTruncate = hiddenCount > 0 && !showFullPath;
|
|
1030
|
-
|
|
1031
|
-
const renderPathItem = (level: NavigationLevel, index: number, displayIndex: number) => {
|
|
1032
|
-
const isCurrentLevel = index === navigationStack.length - 1;
|
|
1033
|
-
return (
|
|
1034
|
-
<button
|
|
1035
|
-
key={`path-${index}`}
|
|
1036
|
-
onClick={() => navigateToLevel(index)}
|
|
1037
|
-
disabled={isCurrentLevel}
|
|
1038
|
-
className={cn(
|
|
1039
|
-
'flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors',
|
|
1040
|
-
!isCurrentLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
1041
|
-
isCurrentLevel && 'bg-[rgb(var(--ec-content-hover))] cursor-default'
|
|
1042
|
-
)}
|
|
1043
|
-
style={{ paddingLeft: `${displayIndex * 12 + 8}px` }}
|
|
1044
|
-
>
|
|
1045
|
-
{index === 0 ? (
|
|
1046
|
-
<Home className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] flex-shrink-0" />
|
|
1047
|
-
) : (
|
|
1048
|
-
<ChevronRight className="w-3.5 h-3.5 text-[rgb(var(--ec-content-text-muted))] flex-shrink-0" />
|
|
1049
|
-
)}
|
|
1050
|
-
<span
|
|
1051
|
-
className={cn(
|
|
1052
|
-
'text-sm truncate',
|
|
1053
|
-
isCurrentLevel
|
|
1054
|
-
? 'font-medium text-[rgb(var(--ec-content-text))]'
|
|
1055
|
-
: 'text-[rgb(var(--ec-content-text-secondary))]'
|
|
1056
|
-
)}
|
|
1057
|
-
>
|
|
1058
|
-
{level.title}
|
|
1059
|
-
</span>
|
|
1060
|
-
{level.badge && (
|
|
1061
|
-
<span
|
|
1062
|
-
className={cn(
|
|
1063
|
-
'ml-auto px-1.5 py-0.5 text-[8px] font-semibold uppercase tracking-wide rounded flex-shrink-0',
|
|
1064
|
-
getBadgeClasses(level.badge)
|
|
1065
|
-
)}
|
|
1066
|
-
>
|
|
1067
|
-
{level.badge}
|
|
1068
|
-
</span>
|
|
1069
|
-
)}
|
|
1070
|
-
</button>
|
|
1071
|
-
);
|
|
1072
|
-
};
|
|
1073
|
-
|
|
1074
|
-
if (shouldTruncate) {
|
|
1075
|
-
return (
|
|
1076
|
-
<>
|
|
1077
|
-
{/* First N items */}
|
|
1078
|
-
{navigationStack.slice(0, SHOW_FIRST).map((level, index) => renderPathItem(level, index, index))}
|
|
1079
|
-
|
|
1080
|
-
{/* Collapsed middle section */}
|
|
1081
|
-
<button
|
|
1082
|
-
onClick={(e) => {
|
|
1083
|
-
e.stopPropagation();
|
|
1084
|
-
setShowFullPath(true);
|
|
1085
|
-
}}
|
|
1086
|
-
className="flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer"
|
|
1087
|
-
style={{ paddingLeft: `${SHOW_FIRST * 12 + 8}px` }}
|
|
1088
|
-
>
|
|
1089
|
-
<span className="flex items-center justify-center w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))]">
|
|
1090
|
-
<span className="text-xs">•••</span>
|
|
1091
|
-
</span>
|
|
1092
|
-
<span className="text-sm text-[rgb(var(--ec-content-text-muted))]">
|
|
1093
|
-
{hiddenCount} more level{hiddenCount > 1 ? 's' : ''}
|
|
1094
|
-
</span>
|
|
1095
|
-
<ChevronDown className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] ml-auto" />
|
|
1096
|
-
</button>
|
|
1097
|
-
|
|
1098
|
-
{/* Last N items */}
|
|
1099
|
-
{navigationStack.slice(-SHOW_LAST).map((level, sliceIndex) => {
|
|
1100
|
-
const actualIndex = totalItems - SHOW_LAST + sliceIndex;
|
|
1101
|
-
return renderPathItem(level, actualIndex, SHOW_FIRST + 1 + sliceIndex);
|
|
1102
|
-
})}
|
|
1103
|
-
</>
|
|
1104
|
-
);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
// Show full path
|
|
1108
|
-
return navigationStack.map((level, index) => renderPathItem(level, index, index));
|
|
1109
|
-
})()}
|
|
1110
|
-
</div>
|
|
1111
|
-
</div>
|
|
1112
|
-
</div>
|
|
1113
|
-
)}
|
|
1114
|
-
</div>
|
|
1115
|
-
)}
|
|
985
|
+
{currentLevel.badge}
|
|
986
|
+
</span>
|
|
987
|
+
)}
|
|
988
|
+
</button>
|
|
1116
989
|
|
|
1117
|
-
{/*
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
scrollbarColor: 'rgb(var(--ec-content-border)) transparent',
|
|
1124
|
-
}}
|
|
1125
|
-
>
|
|
1126
|
-
{/* Favorites Section */}
|
|
1127
|
-
{favorites.length > 0 && isTopLevel && (
|
|
1128
|
-
<div className="mb-6">
|
|
1129
|
-
<div className="flex items-center px-2 py-1.5">
|
|
1130
|
-
<Star className="w-3.5 h-3.5 mr-2 text-amber-400 fill-current" />
|
|
1131
|
-
<span className="text-sm text-[rgb(var(--ec-content-text))] font-semibold">Favorites</span>
|
|
990
|
+
{/* Path Preview Dropdown */}
|
|
991
|
+
{showPathPreview && navigationStack.length > 1 && (
|
|
992
|
+
<div className="absolute left-0 right-0 top-full bg-[rgb(var(--ec-page-bg))] border-b border-[rgb(var(--ec-content-border))] shadow-lg z-20">
|
|
993
|
+
<div className="px-4 py-3">
|
|
994
|
+
<div className="text-[9px] font-medium text-[rgb(var(--ec-content-text-muted))] uppercase tracking-wide mb-2">
|
|
995
|
+
Navigation Path
|
|
1132
996
|
</div>
|
|
1133
|
-
<div className="flex flex-col gap-0.5
|
|
1134
|
-
{
|
|
1135
|
-
const
|
|
1136
|
-
const
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
997
|
+
<div className="flex flex-col gap-0.5">
|
|
998
|
+
{(() => {
|
|
999
|
+
const SHOW_FIRST = 2; // Show first N items
|
|
1000
|
+
const SHOW_LAST = 2; // Show last N items (including current)
|
|
1001
|
+
const totalItems = navigationStack.length;
|
|
1002
|
+
const hiddenCount = totalItems - SHOW_FIRST - SHOW_LAST;
|
|
1003
|
+
const shouldTruncate = hiddenCount > 0 && !showFullPath;
|
|
1004
|
+
|
|
1005
|
+
const renderPathItem = (level: NavigationLevel, index: number, displayIndex: number) => {
|
|
1006
|
+
const isCurrentLevel = index === navigationStack.length - 1;
|
|
1007
|
+
return (
|
|
1008
|
+
<button
|
|
1009
|
+
key={`path-${index}`}
|
|
1010
|
+
onClick={() => navigateToLevel(index)}
|
|
1011
|
+
disabled={isCurrentLevel}
|
|
1012
|
+
className={cn(
|
|
1013
|
+
'flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors',
|
|
1014
|
+
!isCurrentLevel && 'hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer',
|
|
1015
|
+
isCurrentLevel && 'bg-[rgb(var(--ec-content-hover))] cursor-default'
|
|
1016
|
+
)}
|
|
1017
|
+
style={{ paddingLeft: `${displayIndex * 12 + 8}px` }}
|
|
1018
|
+
>
|
|
1019
|
+
{index === 0 ? (
|
|
1020
|
+
<Home className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] flex-shrink-0" />
|
|
1021
|
+
) : (
|
|
1022
|
+
<ChevronRight className="w-3.5 h-3.5 text-[rgb(var(--ec-content-text-muted))] flex-shrink-0" />
|
|
1023
|
+
)}
|
|
1148
1024
|
<span
|
|
1149
1025
|
className={cn(
|
|
1150
|
-
'text-[
|
|
1151
|
-
|
|
1152
|
-
? 'text-[rgb(var(--ec-
|
|
1153
|
-
: 'text-[rgb(var(--ec-content-text-secondary))]
|
|
1026
|
+
'text-[12px] truncate',
|
|
1027
|
+
isCurrentLevel
|
|
1028
|
+
? 'font-medium text-[rgb(var(--ec-content-text))]'
|
|
1029
|
+
: 'text-[rgb(var(--ec-content-text-secondary))]'
|
|
1154
1030
|
)}
|
|
1155
1031
|
>
|
|
1156
|
-
{
|
|
1032
|
+
{level.title}
|
|
1157
1033
|
</span>
|
|
1158
|
-
|
|
1159
|
-
<div className="flex items-center gap-1 flex-shrink-0">
|
|
1160
|
-
{fav.badge && (
|
|
1034
|
+
{level.badge && (
|
|
1161
1035
|
<span
|
|
1162
1036
|
className={cn(
|
|
1163
|
-
'px-1.5 py-0.5 text-[
|
|
1164
|
-
getBadgeClasses(
|
|
1037
|
+
'ml-auto px-1.5 py-0.5 text-[7px] font-semibold uppercase tracking-wide rounded flex-shrink-0',
|
|
1038
|
+
getBadgeClasses(level.badge)
|
|
1165
1039
|
)}
|
|
1166
1040
|
>
|
|
1167
|
-
{
|
|
1041
|
+
{level.badge}
|
|
1168
1042
|
</span>
|
|
1169
1043
|
)}
|
|
1170
|
-
|
|
1044
|
+
</button>
|
|
1045
|
+
);
|
|
1046
|
+
};
|
|
1047
|
+
|
|
1048
|
+
if (shouldTruncate) {
|
|
1049
|
+
return (
|
|
1050
|
+
<>
|
|
1051
|
+
{/* First N items */}
|
|
1052
|
+
{navigationStack.slice(0, SHOW_FIRST).map((level, index) => renderPathItem(level, index, index))}
|
|
1053
|
+
|
|
1054
|
+
{/* Collapsed middle section */}
|
|
1055
|
+
<button
|
|
1171
1056
|
onClick={(e) => {
|
|
1172
1057
|
e.stopPropagation();
|
|
1173
|
-
|
|
1174
|
-
toggleFavorite(fav.nodeKey, node);
|
|
1175
|
-
} else {
|
|
1176
|
-
// Node no longer exists, remove directly using nodeKey
|
|
1177
|
-
removeFavoriteAction(fav.nodeKey);
|
|
1178
|
-
}
|
|
1058
|
+
setShowFullPath(true);
|
|
1179
1059
|
}}
|
|
1180
|
-
className="flex items-center
|
|
1060
|
+
className="flex items-center gap-2 px-2 py-1.5 rounded text-left transition-colors hover:bg-[rgb(var(--ec-content-hover))] cursor-pointer"
|
|
1061
|
+
style={{ paddingLeft: `${SHOW_FIRST * 12 + 8}px` }}
|
|
1181
1062
|
>
|
|
1182
|
-
<
|
|
1183
|
-
|
|
1184
|
-
{node?.pages && node.pages.length > 0 && (
|
|
1185
|
-
<span className="flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] group-hover:text-[rgb(var(--ec-content-text))]">
|
|
1186
|
-
<ChevronRight className="w-4 h-4" />
|
|
1063
|
+
<span className="flex items-center justify-center w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))]">
|
|
1064
|
+
<span className="text-xs">•••</span>
|
|
1187
1065
|
</span>
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1066
|
+
<span className="text-[12px] text-[rgb(var(--ec-content-text-muted))]">
|
|
1067
|
+
{hiddenCount} more level{hiddenCount > 1 ? 's' : ''}
|
|
1068
|
+
</span>
|
|
1069
|
+
<ChevronDown className="w-3.5 h-3.5 text-[rgb(var(--ec-icon-color))] ml-auto" />
|
|
1070
|
+
</button>
|
|
1071
|
+
|
|
1072
|
+
{/* Last N items */}
|
|
1073
|
+
{navigationStack.slice(-SHOW_LAST).map((level, sliceIndex) => {
|
|
1074
|
+
const actualIndex = totalItems - SHOW_LAST + sliceIndex;
|
|
1075
|
+
return renderPathItem(level, actualIndex, SHOW_FIRST + 1 + sliceIndex);
|
|
1076
|
+
})}
|
|
1077
|
+
</>
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Show full path
|
|
1082
|
+
return navigationStack.map((level, index) => renderPathItem(level, index, index));
|
|
1083
|
+
})()}
|
|
1193
1084
|
</div>
|
|
1194
1085
|
</div>
|
|
1195
|
-
|
|
1086
|
+
</div>
|
|
1087
|
+
)}
|
|
1088
|
+
</div>
|
|
1089
|
+
)}
|
|
1196
1090
|
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1091
|
+
{/* Navigation Content */}
|
|
1092
|
+
<nav
|
|
1093
|
+
key={animationKey}
|
|
1094
|
+
className={cn('flex-1 overflow-y-auto overflow-x-hidden p-4 px-2', getAnimationClass())}
|
|
1095
|
+
style={{
|
|
1096
|
+
scrollbarWidth: 'thin',
|
|
1097
|
+
scrollbarColor: 'rgb(var(--ec-content-border)) transparent',
|
|
1098
|
+
}}
|
|
1099
|
+
>
|
|
1100
|
+
{/* Favorites Section */}
|
|
1101
|
+
{favorites.length > 0 && isTopLevel && (
|
|
1102
|
+
<div className="mb-6">
|
|
1103
|
+
<div className="flex items-center px-2 py-1.5">
|
|
1104
|
+
<Star className="w-3.5 h-3.5 mr-2 text-amber-400 fill-current" />
|
|
1105
|
+
<span className="text-[12px] text-[rgb(var(--ec-content-text))] font-semibold">Favorites</span>
|
|
1106
|
+
</div>
|
|
1107
|
+
<div className="flex flex-col gap-0.5 border-l ml-3.5 border-amber-200">
|
|
1108
|
+
{favorites.map((fav, index) => {
|
|
1109
|
+
const node = resolveRef(fav.nodeKey);
|
|
1110
|
+
const isActive = fav.href && currentPath === fav.href;
|
|
1209
1111
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1112
|
+
return (
|
|
1113
|
+
<button
|
|
1114
|
+
key={`fav-${index}`}
|
|
1115
|
+
onClick={() => navigateToFavorite(fav)}
|
|
1116
|
+
className={cn(
|
|
1117
|
+
'group flex items-center justify-between w-full px-3 py-2 border border-transparent cursor-pointer text-left transition-colors hover:bg-amber-500/10 active:bg-amber-500/20',
|
|
1118
|
+
isActive &&
|
|
1119
|
+
'border-[rgb(var(--ec-accent)/0.16)] bg-[rgb(var(--ec-accent-subtle))] hover:bg-[rgb(var(--ec-accent-subtle))] shadow-sm'
|
|
1120
|
+
)}
|
|
1121
|
+
>
|
|
1122
|
+
<div className="flex items-center gap-2.5 min-w-0 flex-1">
|
|
1123
|
+
<span
|
|
1124
|
+
className={cn(
|
|
1125
|
+
'text-[12px] truncate',
|
|
1126
|
+
isActive
|
|
1127
|
+
? 'text-[rgb(var(--ec-accent-text))] font-medium'
|
|
1128
|
+
: 'text-[rgb(var(--ec-content-text-secondary))] group-hover:text-[rgb(var(--ec-content-text))]'
|
|
1129
|
+
)}
|
|
1130
|
+
>
|
|
1131
|
+
{fav.title}
|
|
1132
|
+
</span>
|
|
1133
|
+
</div>
|
|
1134
|
+
<div className="flex items-center gap-1 flex-shrink-0">
|
|
1135
|
+
{fav.badge && (
|
|
1136
|
+
<span
|
|
1137
|
+
className={cn(
|
|
1138
|
+
'px-1.5 py-0.5 text-[7px] font-semibold uppercase tracking-wide rounded',
|
|
1139
|
+
getBadgeClasses(fav.badge)
|
|
1140
|
+
)}
|
|
1141
|
+
>
|
|
1142
|
+
{fav.badge}
|
|
1143
|
+
</span>
|
|
1144
|
+
)}
|
|
1145
|
+
<div
|
|
1146
|
+
onClick={(e) => {
|
|
1147
|
+
e.stopPropagation();
|
|
1148
|
+
if (node) {
|
|
1149
|
+
toggleFavorite(fav.nodeKey, node);
|
|
1150
|
+
} else {
|
|
1151
|
+
// Node no longer exists, remove directly using nodeKey
|
|
1152
|
+
removeFavoriteAction(fav.nodeKey);
|
|
1153
|
+
}
|
|
1154
|
+
}}
|
|
1155
|
+
className="flex items-center justify-center w-5 h-5 text-amber-400 hover:text-amber-500 rounded transition-colors cursor-pointer"
|
|
1156
|
+
>
|
|
1157
|
+
<Star className="w-3.5 h-3.5 fill-current" />
|
|
1158
|
+
</div>
|
|
1159
|
+
{node?.pages && node.pages.length > 0 && (
|
|
1160
|
+
<span className="flex items-center justify-center w-5 h-5 text-[rgb(var(--ec-icon-color))] group-hover:text-[rgb(var(--ec-content-text))]">
|
|
1161
|
+
<ChevronRight className="w-4 h-4" />
|
|
1162
|
+
</span>
|
|
1163
|
+
)}
|
|
1164
|
+
</div>
|
|
1165
|
+
</button>
|
|
1166
|
+
);
|
|
1167
|
+
})}
|
|
1168
|
+
</div>
|
|
1169
|
+
</div>
|
|
1170
|
+
)}
|
|
1171
|
+
|
|
1172
|
+
{/* Empty State */}
|
|
1173
|
+
{currentLevel.entries.length === 0 && favorites.length === 0 && (
|
|
1174
|
+
<div className="flex flex-col items-center justify-center px-6 py-12 text-center">
|
|
1175
|
+
<div className="mb-4 p-3 rounded-full bg-[rgb(var(--ec-group-icon-bg))]">
|
|
1176
|
+
<FileQuestion className="w-8 h-8 text-[rgb(var(--ec-icon-color))]" />
|
|
1177
|
+
</div>
|
|
1178
|
+
<h3 className="text-[12px] font-semibold text-[rgb(var(--ec-content-text))] mb-2">Your catalog is empty</h3>
|
|
1179
|
+
<p className="text-[11px] text-[rgb(var(--ec-content-text-muted))] leading-relaxed max-w-[240px]">
|
|
1180
|
+
Navigation will appear here when you add resources to your EventCatalog.
|
|
1181
|
+
</p>
|
|
1182
|
+
</div>
|
|
1183
|
+
)}
|
|
1184
|
+
|
|
1185
|
+
{currentLevel.entries.length > 0 && renderEntries(currentLevel.entries)}
|
|
1186
|
+
</nav>
|
|
1214
1187
|
|
|
1215
1188
|
{/* Animation keyframes */}
|
|
1216
1189
|
<style>{`
|