@kushagradhawan/kookie-blocks 0.1.17 → 0.1.19

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 (82) hide show
  1. package/dist/cjs/components/docs/docs-page-header.d.ts +12 -0
  2. package/dist/cjs/components/docs/docs-page-header.d.ts.map +1 -0
  3. package/dist/cjs/components/docs/docs-page-header.js +10 -0
  4. package/dist/cjs/components/docs/docs-page-header.js.map +7 -0
  5. package/dist/cjs/components/docs/docs-page.d.ts +28 -0
  6. package/dist/cjs/components/docs/docs-page.d.ts.map +1 -0
  7. package/dist/cjs/components/docs/docs-page.js +2 -0
  8. package/dist/cjs/components/docs/docs-page.js.map +7 -0
  9. package/dist/cjs/components/docs/docs-shell.d.ts +32 -0
  10. package/dist/cjs/components/docs/docs-shell.d.ts.map +1 -0
  11. package/dist/cjs/components/docs/docs-shell.js +2 -0
  12. package/dist/cjs/components/docs/docs-shell.js.map +7 -0
  13. package/dist/cjs/components/docs/docs-sidebar.d.ts +33 -0
  14. package/dist/cjs/components/docs/docs-sidebar.d.ts.map +1 -0
  15. package/dist/cjs/components/docs/docs-sidebar.js +2 -0
  16. package/dist/cjs/components/docs/docs-sidebar.js.map +7 -0
  17. package/dist/cjs/components/docs/index.d.ts +8 -0
  18. package/dist/cjs/components/docs/index.d.ts.map +1 -0
  19. package/dist/cjs/components/docs/index.js +2 -0
  20. package/dist/cjs/components/docs/index.js.map +7 -0
  21. package/dist/cjs/components/docs/preview-block.d.ts +22 -0
  22. package/dist/cjs/components/docs/preview-block.d.ts.map +1 -0
  23. package/dist/cjs/components/docs/preview-block.js +2 -0
  24. package/dist/cjs/components/docs/preview-block.js.map +7 -0
  25. package/dist/cjs/components/docs/table-of-contents.d.ts +12 -0
  26. package/dist/cjs/components/docs/table-of-contents.d.ts.map +1 -0
  27. package/dist/cjs/components/docs/table-of-contents.js +2 -0
  28. package/dist/cjs/components/docs/table-of-contents.js.map +7 -0
  29. package/dist/cjs/components/docs/types.d.ts +38 -0
  30. package/dist/cjs/components/docs/types.d.ts.map +1 -0
  31. package/dist/cjs/components/docs/types.js +2 -0
  32. package/dist/cjs/components/docs/types.js.map +7 -0
  33. package/dist/cjs/components/index.d.ts +1 -0
  34. package/dist/cjs/components/index.d.ts.map +1 -1
  35. package/dist/cjs/components/index.js +1 -1
  36. package/dist/cjs/components/index.js.map +2 -2
  37. package/dist/esm/components/docs/docs-page-header.d.ts +12 -0
  38. package/dist/esm/components/docs/docs-page-header.d.ts.map +1 -0
  39. package/dist/esm/components/docs/docs-page-header.js +10 -0
  40. package/dist/esm/components/docs/docs-page-header.js.map +7 -0
  41. package/dist/esm/components/docs/docs-page.d.ts +28 -0
  42. package/dist/esm/components/docs/docs-page.d.ts.map +1 -0
  43. package/dist/esm/components/docs/docs-page.js +2 -0
  44. package/dist/esm/components/docs/docs-page.js.map +7 -0
  45. package/dist/esm/components/docs/docs-shell.d.ts +32 -0
  46. package/dist/esm/components/docs/docs-shell.d.ts.map +1 -0
  47. package/dist/esm/components/docs/docs-shell.js +2 -0
  48. package/dist/esm/components/docs/docs-shell.js.map +7 -0
  49. package/dist/esm/components/docs/docs-sidebar.d.ts +33 -0
  50. package/dist/esm/components/docs/docs-sidebar.d.ts.map +1 -0
  51. package/dist/esm/components/docs/docs-sidebar.js +2 -0
  52. package/dist/esm/components/docs/docs-sidebar.js.map +7 -0
  53. package/dist/esm/components/docs/index.d.ts +8 -0
  54. package/dist/esm/components/docs/index.d.ts.map +1 -0
  55. package/dist/esm/components/docs/index.js +2 -0
  56. package/dist/esm/components/docs/index.js.map +7 -0
  57. package/dist/esm/components/docs/preview-block.d.ts +22 -0
  58. package/dist/esm/components/docs/preview-block.d.ts.map +1 -0
  59. package/dist/esm/components/docs/preview-block.js +2 -0
  60. package/dist/esm/components/docs/preview-block.js.map +7 -0
  61. package/dist/esm/components/docs/table-of-contents.d.ts +12 -0
  62. package/dist/esm/components/docs/table-of-contents.d.ts.map +1 -0
  63. package/dist/esm/components/docs/table-of-contents.js +2 -0
  64. package/dist/esm/components/docs/table-of-contents.js.map +7 -0
  65. package/dist/esm/components/docs/types.d.ts +38 -0
  66. package/dist/esm/components/docs/types.d.ts.map +1 -0
  67. package/dist/esm/components/docs/types.js +1 -0
  68. package/dist/esm/components/docs/types.js.map +7 -0
  69. package/dist/esm/components/index.d.ts +1 -0
  70. package/dist/esm/components/index.d.ts.map +1 -1
  71. package/dist/esm/components/index.js +1 -1
  72. package/dist/esm/components/index.js.map +2 -2
  73. package/package.json +2 -1
  74. package/src/components/docs/docs-page-header.tsx +92 -0
  75. package/src/components/docs/docs-page.tsx +99 -0
  76. package/src/components/docs/docs-shell.tsx +114 -0
  77. package/src/components/docs/docs-sidebar.tsx +180 -0
  78. package/src/components/docs/index.ts +17 -0
  79. package/src/components/docs/preview-block.tsx +64 -0
  80. package/src/components/docs/table-of-contents.tsx +157 -0
  81. package/src/components/docs/types.ts +45 -0
  82. package/src/components/index.ts +1 -0
@@ -0,0 +1,114 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Shell, Flex, IconButton } from '@kushagradhawan/kookie-ui';
5
+ import { DocsSidebar } from './docs-sidebar.js';
6
+ import type { DocsNavigationConfig, DocsLogoConfig } from './types.js';
7
+
8
+ export interface DocsShellProps {
9
+ children: React.ReactNode;
10
+ /** Navigation configuration */
11
+ navigation: DocsNavigationConfig;
12
+ /** Logo configuration */
13
+ logo?: DocsLogoConfig;
14
+ /** Optional header actions (dark mode toggle, github link, etc.) */
15
+ headerActions?: React.ReactNode;
16
+ /** Optional sidebar footer content */
17
+ sidebarFooter?: React.ReactNode;
18
+ /** Shell height */
19
+ height?: 'full' | 'auto' | string | number;
20
+ /** Sidebar thin size (when collapsed) */
21
+ sidebarThinSize?: number;
22
+ /** Whether sidebar is resizable */
23
+ sidebarResizable?: boolean;
24
+ /** Mobile trigger icon */
25
+ mobileTriggerIcon?: React.ReactNode;
26
+ /** Current pathname for active state detection */
27
+ pathname?: string;
28
+ /** Link component to use (defaults to 'a') */
29
+ linkComponent?: React.ComponentType<{ href: string; children: React.ReactNode; prefetch?: boolean; 'aria-label'?: string }>;
30
+ }
31
+
32
+ export function DocsShell({
33
+ children,
34
+ navigation,
35
+ logo,
36
+ headerActions,
37
+ sidebarFooter,
38
+ height,
39
+ sidebarThinSize = 80,
40
+ sidebarResizable = true,
41
+ mobileTriggerIcon,
42
+ pathname = '',
43
+ linkComponent,
44
+ }: DocsShellProps) {
45
+ const [sidebarPresentation, setSidebarPresentation] = useState<'thin' | 'expanded'>('expanded');
46
+
47
+ // Combine headerActions with sidebarFooter
48
+ const footerContent = sidebarFooter || headerActions ? (
49
+ <Flex gap="2" align="center">
50
+ {headerActions}
51
+ {sidebarFooter}
52
+ </Flex>
53
+ ) : undefined;
54
+
55
+ return (
56
+ <Shell.Root height={height}>
57
+ <Shell.Sidebar
58
+ toggleModes="single"
59
+ thinSize={sidebarThinSize}
60
+ resizable={sidebarResizable}
61
+ defaultState={{ initial: 'collapsed', sm: 'expanded' }}
62
+ onStateChange={(state) => setSidebarPresentation(state === 'thin' ? 'thin' : 'expanded')}
63
+ presentation={{ initial: 'overlay', sm: 'fixed' }}
64
+ >
65
+ <DocsSidebar
66
+ navigation={navigation}
67
+ logo={logo}
68
+ presentation={sidebarPresentation}
69
+ footer={footerContent}
70
+ pathname={pathname}
71
+ linkComponent={linkComponent}
72
+ />
73
+ </Shell.Sidebar>
74
+
75
+ <Shell.Content>
76
+ {/* Mobile trigger */}
77
+ <Flex
78
+ display={{ initial: 'flex', sm: 'none' }}
79
+ position="fixed"
80
+ top="4"
81
+ left="4"
82
+ align="center"
83
+ justify="center"
84
+ width="auto"
85
+ height="auto"
86
+ style={{ zIndex: 999 }}
87
+ >
88
+ <IconButton variant="ghost" size="3" highContrast color="gray" asChild>
89
+ <Shell.Trigger target="sidebar">
90
+ {mobileTriggerIcon || (
91
+ <svg
92
+ width="16"
93
+ height="16"
94
+ viewBox="0 0 16 16"
95
+ fill="none"
96
+ xmlns="http://www.w3.org/2000/svg"
97
+ >
98
+ <path
99
+ d="M2 4h12M2 8h12M2 12h12"
100
+ stroke="currentColor"
101
+ strokeWidth="1.5"
102
+ strokeLinecap="round"
103
+ />
104
+ </svg>
105
+ )}
106
+ </Shell.Trigger>
107
+ </IconButton>
108
+ </Flex>
109
+
110
+ {children}
111
+ </Shell.Content>
112
+ </Shell.Root>
113
+ );
114
+ }
@@ -0,0 +1,180 @@
1
+ 'use client';
2
+
3
+ import React, { isValidElement } from 'react';
4
+ import { Sidebar, Flex, Avatar } from '@kushagradhawan/kookie-ui';
5
+ import { HugeiconsIcon } from '@hugeicons/react';
6
+ import type { DocsNavigationConfig, DocsLogoConfig } from './types.js';
7
+
8
+ // HugeIcons IconSvgObject type - readonly array of tuples with tag name and attributes
9
+ type IconSvgObject = readonly (readonly [string, { readonly [key: string]: string | number }])[];
10
+
11
+ type AccentColor =
12
+ | 'gray'
13
+ | 'gold'
14
+ | 'bronze'
15
+ | 'brown'
16
+ | 'yellow'
17
+ | 'amber'
18
+ | 'orange'
19
+ | 'tomato'
20
+ | 'red'
21
+ | 'ruby'
22
+ | 'crimson'
23
+ | 'pink'
24
+ | 'plum'
25
+ | 'purple'
26
+ | 'violet'
27
+ | 'iris'
28
+ | 'indigo'
29
+ | 'blue'
30
+ | 'cyan'
31
+ | 'teal'
32
+ | 'jade'
33
+ | 'green'
34
+ | 'grass'
35
+ | 'lime'
36
+ | 'mint'
37
+ | 'sky';
38
+
39
+ export interface DocsSidebarProps {
40
+ /** Navigation configuration */
41
+ navigation: DocsNavigationConfig;
42
+ /** Logo configuration */
43
+ logo?: DocsLogoConfig;
44
+ /** Presentation mode from parent Shell */
45
+ presentation?: 'thin' | 'expanded';
46
+ /** Footer content */
47
+ footer?: React.ReactNode;
48
+ /** Sidebar size */
49
+ size?: '1' | '2';
50
+ /** Sidebar variant */
51
+ variant?: 'soft' | 'outline' | 'surface' | 'ghost';
52
+ /** Menu item variant */
53
+ menuVariant?: 'solid' | 'soft';
54
+ /** Accent color */
55
+ color?: AccentColor;
56
+ /** Current pathname for active state detection */
57
+ pathname?: string;
58
+ /** Link component to use (defaults to 'a') */
59
+ linkComponent?: React.ComponentType<{ href: string; children: React.ReactNode; prefetch?: boolean; 'aria-label'?: string }>;
60
+ }
61
+
62
+ export function DocsSidebar({
63
+ navigation,
64
+ logo,
65
+ presentation = 'expanded',
66
+ footer,
67
+ size = '2',
68
+ variant = 'soft',
69
+ menuVariant = 'soft',
70
+ color = 'gray',
71
+ pathname = '',
72
+ linkComponent: LinkComponent = 'a' as any,
73
+ }: DocsSidebarProps) {
74
+
75
+ // Helper to check if icon is HugeIcons IconSvgObject format
76
+ const isIconSvgObject = (icon: unknown): icon is IconSvgObject => {
77
+ return Array.isArray(icon) && icon.length > 0 && Array.isArray(icon[0]);
78
+ };
79
+
80
+ // Helper to render icon
81
+ const renderIcon = (icon: React.ComponentType<{ className?: string }> | React.ReactNode | IconSvgObject) => {
82
+ if (!icon) return null;
83
+ if (isValidElement(icon)) return icon;
84
+ // Handle HugeIcons IconSvgObject format
85
+ if (isIconSvgObject(icon)) {
86
+ return <HugeiconsIcon icon={icon} size={16} />;
87
+ }
88
+ if (typeof icon === 'function') {
89
+ const IconComponent = icon as React.ComponentType<{ className?: string }>;
90
+ return <IconComponent />;
91
+ }
92
+ return null;
93
+ };
94
+
95
+ return (
96
+ <Sidebar.Root
97
+ size={size}
98
+ variant={variant}
99
+ color={color}
100
+ menuVariant={menuVariant}
101
+ presentation={presentation}
102
+ >
103
+ {logo && (
104
+ <Sidebar.Header>
105
+ <Flex justify="between" align="center" width="100%">
106
+ <LinkComponent href={logo.href || '/'} aria-label={logo.alt || 'Home'}>
107
+ <Flex align="center" gap="2">
108
+ <Avatar fallback={logo.alt?.[0] || 'K'} size="2" src={logo.src} />
109
+ </Flex>
110
+ </LinkComponent>
111
+ </Flex>
112
+ </Sidebar.Header>
113
+ )}
114
+
115
+ <Sidebar.Content>
116
+ <Sidebar.Menu>
117
+ {navigation.groups.map((group) => (
118
+ <Sidebar.Group key={group.label}>
119
+ <Sidebar.GroupLabel>{group.label}</Sidebar.GroupLabel>
120
+ <Sidebar.GroupContent>
121
+ {group.items.map((item) => {
122
+ // Check if this item or any nested item is active
123
+ const hasNestedItems = item.items && item.items.length > 0;
124
+ const isNestedActive = hasNestedItems
125
+ ? item.items!.some((subItem) => pathname === subItem.href)
126
+ : false;
127
+
128
+ if (hasNestedItems) {
129
+ return (
130
+ <Sidebar.MenuItem key={item.href}>
131
+ <Sidebar.MenuSub defaultOpen={isNestedActive}>
132
+ <Sidebar.MenuSubTrigger>
133
+ {renderIcon(item.icon)}
134
+ {item.title}
135
+ </Sidebar.MenuSubTrigger>
136
+ <Sidebar.MenuSubContent>
137
+ {item.items!.map((subItem) => (
138
+ <Sidebar.MenuButton
139
+ asChild
140
+ key={subItem.href}
141
+ isActive={pathname === subItem.href}
142
+ >
143
+ <LinkComponent href={subItem.href}>
144
+ {renderIcon(subItem.icon)}
145
+ <span className="rt-SidebarMenuLabel">{subItem.title}</span>
146
+ </LinkComponent>
147
+ </Sidebar.MenuButton>
148
+ ))}
149
+ </Sidebar.MenuSubContent>
150
+ </Sidebar.MenuSub>
151
+ </Sidebar.MenuItem>
152
+ );
153
+ }
154
+
155
+ // Regular menu item
156
+ return (
157
+ <Sidebar.MenuItem key={item.href}>
158
+ <Sidebar.MenuButton
159
+ asChild
160
+ isActive={pathname === item.href}
161
+ badge={item.badge}
162
+ >
163
+ <LinkComponent href={item.href}>
164
+ {renderIcon(item.icon)}
165
+ <span className="rt-SidebarMenuLabel">{item.title}</span>
166
+ </LinkComponent>
167
+ </Sidebar.MenuButton>
168
+ </Sidebar.MenuItem>
169
+ );
170
+ })}
171
+ </Sidebar.GroupContent>
172
+ </Sidebar.Group>
173
+ ))}
174
+ </Sidebar.Menu>
175
+ </Sidebar.Content>
176
+
177
+ {footer && <Sidebar.Footer>{footer}</Sidebar.Footer>}
178
+ </Sidebar.Root>
179
+ );
180
+ }
@@ -0,0 +1,17 @@
1
+ // Types
2
+ export type {
3
+ DocsNavigationItem,
4
+ DocsNavigationGroup,
5
+ DocsNavigationConfig,
6
+ DocsPageMeta,
7
+ DocsLogoConfig,
8
+ TocItem,
9
+ } from './types.js';
10
+
11
+ // Components
12
+ export { TableOfContents } from './table-of-contents.js';
13
+ export { DocsSidebar } from './docs-sidebar.js';
14
+ export { DocsPageHeader } from './docs-page-header.js';
15
+ export { DocsPage } from './docs-page.js';
16
+ export { DocsShell } from './docs-shell.js';
17
+ export { PreviewBlock } from './preview-block.js';
@@ -0,0 +1,64 @@
1
+ "use client";
2
+
3
+ import React from "react";
4
+ import { Box, Card, Flex, Theme } from "@kushagradhawan/kookie-ui";
5
+ import type { ReactNode } from "react";
6
+
7
+ export interface PreviewBlockProps {
8
+ children: ReactNode;
9
+ /** Background style for the preview */
10
+ background?: "none" | "dots" | "grid";
11
+ }
12
+
13
+ /**
14
+ * PreviewBlock - displays a live preview of a component
15
+ *
16
+ * Use this component to showcase live examples of components in documentation.
17
+ * Shows only the rendered component, not the code.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <PreviewBlock background="dots">
22
+ * <Button>Example Button</Button>
23
+ * </PreviewBlock>
24
+ * ```
25
+ */
26
+ export function PreviewBlock({
27
+ children,
28
+ background = "dots",
29
+ }: PreviewBlockProps) {
30
+ const backgroundStyle =
31
+ background === "dots"
32
+ ? {
33
+ backgroundImage:
34
+ "radial-gradient(circle, var(--gray-6) 1px, transparent 1px)",
35
+ backgroundSize: "24px 24px",
36
+ backgroundPosition: "center",
37
+ backgroundColor: "var(--gray-2)",
38
+ }
39
+ : background === "grid"
40
+ ? {
41
+ backgroundImage:
42
+ "linear-gradient(var(--gray-6) 1px, transparent 1px), linear-gradient(90deg, var(--gray-6) 1px, transparent 1px)",
43
+ backgroundSize: "24px 24px",
44
+ backgroundPosition: "center",
45
+ backgroundColor: "var(--gray-2)",
46
+ }
47
+ : undefined;
48
+
49
+ return (
50
+ <Box my="3">
51
+ <Card size="1" variant="soft">
52
+ <Flex
53
+ justify="center"
54
+ align="center"
55
+ py="4"
56
+ minHeight="240px"
57
+ style={backgroundStyle}
58
+ >
59
+ <Theme fontFamily="sans">{children}</Theme>
60
+ </Flex>
61
+ </Card>
62
+ </Box>
63
+ );
64
+ }
@@ -0,0 +1,157 @@
1
+ 'use client';
2
+
3
+ import React, { useEffect, useState, useCallback, useRef, memo } from 'react';
4
+ import { Box, Text, Link, Flex } from '@kushagradhawan/kookie-ui';
5
+ import type { TocItem } from './types.js';
6
+
7
+ // Generate slug from heading text
8
+ const generateSlug = (text: string): string => {
9
+ return text
10
+ .toLowerCase()
11
+ .replace(/[^\w\s-]/g, '')
12
+ .replace(/\s+/g, '-')
13
+ .trim();
14
+ };
15
+
16
+ export interface TableOfContentsProps {
17
+ className?: string;
18
+ /** Heading levels to include (default: [2]) */
19
+ levels?: number[];
20
+ /** Title shown above TOC (default: "On this page") */
21
+ title?: string;
22
+ /** Optional wrapper for the TOC content */
23
+ renderContainer?: (tocContent: React.ReactNode) => React.ReactNode | null;
24
+ }
25
+
26
+ export const TableOfContents = memo(function TableOfContents({
27
+ className,
28
+ levels = [2],
29
+ title = 'On this page',
30
+ renderContainer,
31
+ }: TableOfContentsProps) {
32
+ const [toc, setToc] = useState<TocItem[]>([]);
33
+ const [activeId, setActiveId] = useState<string>('');
34
+ const observerRef = useRef<IntersectionObserver | null>(null);
35
+ const rafIdRef = useRef<number | null>(null);
36
+ const timeoutIdRef = useRef<number | null>(null);
37
+
38
+ const extractHeadings = useCallback(() => {
39
+ rafIdRef.current = requestAnimationFrame(() => {
40
+ const contentArea = document.querySelector('[data-content-area]');
41
+ if (!contentArea) return;
42
+
43
+ // Build selector from levels
44
+ const selector = levels.map((l) => `h${l}`).join(', ');
45
+ const headingElements = Array.from(contentArea.querySelectorAll(selector));
46
+
47
+ const headings = headingElements
48
+ .map((heading) => {
49
+ const text = heading.textContent || '';
50
+ let id = heading.id;
51
+
52
+ if (!id && text) {
53
+ id = generateSlug(text);
54
+ heading.id = id;
55
+ }
56
+
57
+ return {
58
+ id,
59
+ text,
60
+ level: parseInt(heading.tagName.charAt(1)),
61
+ };
62
+ })
63
+ .filter((item) => item.id && item.text);
64
+
65
+ setToc(headings);
66
+
67
+ // Disconnect any previous observer
68
+ if (observerRef.current) {
69
+ observerRef.current.disconnect();
70
+ observerRef.current = null;
71
+ }
72
+
73
+ if (headings.length > 0) {
74
+ const observer = new IntersectionObserver(
75
+ (entries) => {
76
+ entries.forEach((entry) => {
77
+ if (entry.isIntersecting) {
78
+ setActiveId(entry.target.id);
79
+ }
80
+ });
81
+ },
82
+ { rootMargin: '-20% 0% -35% 0%' }
83
+ );
84
+
85
+ headings.forEach(({ id }) => {
86
+ const element = document.getElementById(id);
87
+ if (element) observer.observe(element);
88
+ });
89
+
90
+ observerRef.current = observer;
91
+ }
92
+ });
93
+ }, [levels]);
94
+
95
+ useEffect(() => {
96
+ // Initial extraction with delay for DOM readiness
97
+ timeoutIdRef.current = window.setTimeout(() => {
98
+ extractHeadings();
99
+ }, 100);
100
+
101
+ return () => {
102
+ if (timeoutIdRef.current) {
103
+ clearTimeout(timeoutIdRef.current);
104
+ timeoutIdRef.current = null;
105
+ }
106
+ if (rafIdRef.current) {
107
+ cancelAnimationFrame(rafIdRef.current);
108
+ rafIdRef.current = null;
109
+ }
110
+ if (observerRef.current) {
111
+ observerRef.current.disconnect();
112
+ observerRef.current = null;
113
+ }
114
+ };
115
+ }, [extractHeadings]);
116
+
117
+ const getLinkStyle = useCallback(
118
+ (level: number): React.CSSProperties => ({
119
+ display: 'block',
120
+ whiteSpace: 'nowrap',
121
+ overflow: 'hidden',
122
+ textOverflow: 'ellipsis',
123
+ paddingLeft: level > 2 ? `${(level - 2) * 12}px` : '0',
124
+ }),
125
+ []
126
+ );
127
+
128
+ if (toc.length === 0) return null;
129
+
130
+ const tocContent = (
131
+ <Flex direction="column" gap="3" className={className}>
132
+ <Text size="1" weight="medium" color="gray">
133
+ {title}
134
+ </Text>
135
+ <Flex direction="column" gap="2">
136
+ {toc.map((item) => (
137
+ <Link
138
+ key={item.id}
139
+ color="gray"
140
+ highContrast={activeId === item.id}
141
+ size="1"
142
+ href={`#${item.id}`}
143
+ style={getLinkStyle(item.level)}
144
+ >
145
+ {item.text}
146
+ </Link>
147
+ ))}
148
+ </Flex>
149
+ </Flex>
150
+ );
151
+
152
+ if (renderContainer) {
153
+ return renderContainer(tocContent);
154
+ }
155
+
156
+ return tocContent;
157
+ });
@@ -0,0 +1,45 @@
1
+ import type { ReactNode, ComponentType } from 'react';
2
+
3
+ // HugeIcons IconSvgObject type - readonly array of tuples with tag name and attributes
4
+ type IconSvgObject = readonly (readonly [string, { readonly [key: string]: string | number }])[];
5
+
6
+ // Navigation types
7
+ export interface DocsNavigationItem {
8
+ href: string;
9
+ title: string;
10
+ // Support React components, ReactNode, or HugeIcons IconSvgObject format
11
+ icon?: ComponentType<{ className?: string }> | ReactNode | IconSvgObject;
12
+ badge?: string;
13
+ items?: DocsNavigationItem[];
14
+ }
15
+
16
+ export interface DocsNavigationGroup {
17
+ label: string;
18
+ items: DocsNavigationItem[];
19
+ }
20
+
21
+ export interface DocsNavigationConfig {
22
+ groups: DocsNavigationGroup[];
23
+ }
24
+
25
+ // Page metadata types
26
+ export interface DocsPageMeta {
27
+ title: string;
28
+ description?: string;
29
+ category?: string;
30
+ source?: string;
31
+ }
32
+
33
+ // Logo configuration
34
+ export interface DocsLogoConfig {
35
+ src: string;
36
+ alt?: string;
37
+ href?: string;
38
+ }
39
+
40
+ // Table of contents types
41
+ export interface TocItem {
42
+ id: string;
43
+ text: string;
44
+ level: number;
45
+ }
@@ -1,3 +1,4 @@
1
1
  export * from './code/index.js';
2
+ export * from './docs/index.js';
2
3
  export * from './hero/index.js';
3
4
  export * from './markdown/index.js';