@kushagradhawan/kookie-blocks 0.1.17 → 0.1.18
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/cjs/components/docs/docs-page-header.d.ts +12 -0
- package/dist/cjs/components/docs/docs-page-header.d.ts.map +1 -0
- package/dist/cjs/components/docs/docs-page-header.js +10 -0
- package/dist/cjs/components/docs/docs-page-header.js.map +7 -0
- package/dist/cjs/components/docs/docs-page.d.ts +28 -0
- package/dist/cjs/components/docs/docs-page.d.ts.map +1 -0
- package/dist/cjs/components/docs/docs-page.js +2 -0
- package/dist/cjs/components/docs/docs-page.js.map +7 -0
- package/dist/cjs/components/docs/docs-shell.d.ts +32 -0
- package/dist/cjs/components/docs/docs-shell.d.ts.map +1 -0
- package/dist/cjs/components/docs/docs-shell.js +2 -0
- package/dist/cjs/components/docs/docs-shell.js.map +7 -0
- package/dist/cjs/components/docs/docs-sidebar.d.ts +33 -0
- package/dist/cjs/components/docs/docs-sidebar.d.ts.map +1 -0
- package/dist/cjs/components/docs/docs-sidebar.js +2 -0
- package/dist/cjs/components/docs/docs-sidebar.js.map +7 -0
- package/dist/cjs/components/docs/index.d.ts +7 -0
- package/dist/cjs/components/docs/index.d.ts.map +1 -0
- package/dist/cjs/components/docs/index.js +2 -0
- package/dist/cjs/components/docs/index.js.map +7 -0
- package/dist/cjs/components/docs/table-of-contents.d.ts +12 -0
- package/dist/cjs/components/docs/table-of-contents.d.ts.map +1 -0
- package/dist/cjs/components/docs/table-of-contents.js +2 -0
- package/dist/cjs/components/docs/table-of-contents.js.map +7 -0
- package/dist/cjs/components/docs/types.d.ts +38 -0
- package/dist/cjs/components/docs/types.d.ts.map +1 -0
- package/dist/cjs/components/docs/types.js +2 -0
- package/dist/cjs/components/docs/types.js.map +7 -0
- package/dist/cjs/components/index.d.ts +1 -0
- package/dist/cjs/components/index.d.ts.map +1 -1
- package/dist/cjs/components/index.js +1 -1
- package/dist/cjs/components/index.js.map +2 -2
- package/dist/esm/components/docs/docs-page-header.d.ts +12 -0
- package/dist/esm/components/docs/docs-page-header.d.ts.map +1 -0
- package/dist/esm/components/docs/docs-page-header.js +10 -0
- package/dist/esm/components/docs/docs-page-header.js.map +7 -0
- package/dist/esm/components/docs/docs-page.d.ts +28 -0
- package/dist/esm/components/docs/docs-page.d.ts.map +1 -0
- package/dist/esm/components/docs/docs-page.js +2 -0
- package/dist/esm/components/docs/docs-page.js.map +7 -0
- package/dist/esm/components/docs/docs-shell.d.ts +32 -0
- package/dist/esm/components/docs/docs-shell.d.ts.map +1 -0
- package/dist/esm/components/docs/docs-shell.js +2 -0
- package/dist/esm/components/docs/docs-shell.js.map +7 -0
- package/dist/esm/components/docs/docs-sidebar.d.ts +33 -0
- package/dist/esm/components/docs/docs-sidebar.d.ts.map +1 -0
- package/dist/esm/components/docs/docs-sidebar.js +2 -0
- package/dist/esm/components/docs/docs-sidebar.js.map +7 -0
- package/dist/esm/components/docs/index.d.ts +7 -0
- package/dist/esm/components/docs/index.d.ts.map +1 -0
- package/dist/esm/components/docs/index.js +2 -0
- package/dist/esm/components/docs/index.js.map +7 -0
- package/dist/esm/components/docs/table-of-contents.d.ts +12 -0
- package/dist/esm/components/docs/table-of-contents.d.ts.map +1 -0
- package/dist/esm/components/docs/table-of-contents.js +2 -0
- package/dist/esm/components/docs/table-of-contents.js.map +7 -0
- package/dist/esm/components/docs/types.d.ts +38 -0
- package/dist/esm/components/docs/types.d.ts.map +1 -0
- package/dist/esm/components/docs/types.js +1 -0
- package/dist/esm/components/docs/types.js.map +7 -0
- package/dist/esm/components/index.d.ts +1 -0
- package/dist/esm/components/index.d.ts.map +1 -1
- package/dist/esm/components/index.js +1 -1
- package/dist/esm/components/index.js.map +2 -2
- package/package.json +2 -1
- package/src/components/docs/docs-page-header.tsx +92 -0
- package/src/components/docs/docs-page.tsx +99 -0
- package/src/components/docs/docs-shell.tsx +114 -0
- package/src/components/docs/docs-sidebar.tsx +180 -0
- package/src/components/docs/index.ts +16 -0
- package/src/components/docs/table-of-contents.tsx +157 -0
- package/src/components/docs/types.ts +45 -0
- package/src/components/index.ts +1 -0
|
@@ -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,16 @@
|
|
|
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';
|
|
@@ -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
|
+
}
|
package/src/components/index.ts
CHANGED