@amritanshu3011/mdx-renderer 1.0.9 → 1.0.11
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/README.md
CHANGED
|
@@ -1,142 +1,236 @@
|
|
|
1
|
+
|
|
1
2
|
```markdown
|
|
2
3
|
# MDX Renderer
|
|
3
4
|
|
|
4
|
-
A
|
|
5
|
+
A flexible, extensible rendering engine for React. It transforms data (JSON) and MDX content into rich, interactive UI components.
|
|
6
|
+
|
|
7
|
+
Designed for **Data-Driven UI**, it allows you to render specific components dynamically based on backend responses, while giving you the power to inject your own custom components or override built-in ones.
|
|
5
8
|
|
|
6
|
-
## Installation
|
|
9
|
+
## 📦 Installation
|
|
7
10
|
|
|
8
11
|
```bash
|
|
9
|
-
npm install @amritanshu3011/mdx-renderer
|
|
12
|
+
npm install @amritanshu3011/mdx-renderer echarts recharts lucide-react
|
|
13
|
+
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### ⚠️ Import Styles
|
|
17
|
+
|
|
18
|
+
You **must** import the base CSS in your application's entry point (e.g., `main.jsx` or `App.jsx`) for components to render correctly.
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
import '@amritanshu3011/mdx-renderer/styles/base.css';
|
|
10
22
|
|
|
11
23
|
```
|
|
12
24
|
|
|
13
|
-
|
|
25
|
+
---
|
|
14
26
|
|
|
15
|
-
|
|
27
|
+
## 🚀 Quick Start (Data-Driven Mode)
|
|
16
28
|
|
|
17
|
-
|
|
29
|
+
The core principle is simple: You provide an array of data objects, and the library renders the corresponding components.
|
|
18
30
|
|
|
19
31
|
```tsx
|
|
20
|
-
import {
|
|
32
|
+
import { IntentProvider, MDXRenderer } from '@amritanshu3011/mdx-renderer';
|
|
33
|
+
import Content from './content.mdx'; // Optional MDX file
|
|
21
34
|
|
|
22
35
|
function App() {
|
|
36
|
+
// Data from your API, Backend, or LLM
|
|
37
|
+
const dashboardData = [
|
|
38
|
+
{
|
|
39
|
+
type: 'line_chart',
|
|
40
|
+
data: {
|
|
41
|
+
title: 'Monthly Revenue',
|
|
42
|
+
xKey: 'month',
|
|
43
|
+
yKey: 'value',
|
|
44
|
+
points: [{ month: 'Jan', value: 4000 }, { month: 'Feb', value: 3000 }]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
];
|
|
48
|
+
|
|
23
49
|
return (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
50
|
+
// IntentProvider handles Data, Theme, and Registry
|
|
51
|
+
<IntentProvider data={dashboardData}>
|
|
52
|
+
|
|
53
|
+
{/* 1. Render content from MDX files */}
|
|
54
|
+
<MDXRenderer>
|
|
55
|
+
<Content />
|
|
56
|
+
</MDXRenderer>
|
|
57
|
+
|
|
58
|
+
</IntentProvider>
|
|
27
59
|
);
|
|
28
60
|
}
|
|
29
61
|
|
|
30
62
|
```
|
|
31
63
|
|
|
32
|
-
|
|
64
|
+
In your `.mdx` file, simply use the dynamic tag:
|
|
33
65
|
|
|
34
|
-
```
|
|
35
|
-
|
|
66
|
+
```mdx
|
|
67
|
+
# Dashboard
|
|
36
68
|
|
|
37
|
-
|
|
38
|
-
const { visualizations, addVisualization } = useVisualizations();
|
|
69
|
+
import { DynamicVisualizations } from '@amritanshu3011/mdx-renderer';
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
const handleLoadData = () => {
|
|
42
|
-
addVisualization({
|
|
43
|
-
type: 'pros_cons', // Key must match the registry (see table below)
|
|
44
|
-
data: {
|
|
45
|
-
title: 'React vs Angular',
|
|
46
|
-
pros: ['Virtual DOM', 'Huge Ecosystem'],
|
|
47
|
-
cons: ['Steep Learning Curve']
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
};
|
|
71
|
+
Below is the dynamic analysis:
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
<div>
|
|
54
|
-
<button onClick={handleLoadData}>Load Analysis</button>
|
|
55
|
-
|
|
56
|
-
{/* Render the Dynamic Components */}
|
|
57
|
-
<div className="results-container">
|
|
58
|
-
{visualizations.map((viz, index) => {
|
|
59
|
-
const Component = visualizationRegistry[viz.type];
|
|
60
|
-
|
|
61
|
-
if (!Component) return null;
|
|
62
|
-
|
|
63
|
-
return <Component key={index} {...viz.data} />;
|
|
64
|
-
})}
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
73
|
+
<DynamicVisualizations />
|
|
69
74
|
|
|
70
75
|
```
|
|
71
76
|
|
|
72
|
-
|
|
77
|
+
---
|
|
73
78
|
|
|
74
|
-
|
|
79
|
+
## 🧩 Reusability & Extension (The Registry Pattern)
|
|
75
80
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
This library is designed to be **extended**. You are not limited to the built-in components. You can use the `registry` prop to:
|
|
82
|
+
|
|
83
|
+
1. **Add** entirely new components (e.g., a Video Player, Map, or KPI Card).
|
|
84
|
+
2. **Override** built-in components to match your design system.
|
|
85
|
+
3. **Adapt** backend data structures to match component props.
|
|
86
|
+
|
|
87
|
+
### Example: Adding a Custom Component
|
|
88
|
+
|
|
89
|
+
**1. Create your component**
|
|
90
|
+
|
|
91
|
+
```jsx
|
|
92
|
+
// src/components/ImageViewer.jsx
|
|
93
|
+
export const ImageViewer = ({ src, caption }) => (
|
|
94
|
+
<div className="card">
|
|
95
|
+
<img src={src} alt="Custom" />
|
|
96
|
+
<p>{caption}</p>
|
|
97
|
+
</div>
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**2. Register it in your App**
|
|
103
|
+
|
|
104
|
+
```jsx
|
|
105
|
+
import { ImageViewer } from './components/ImageViewer';
|
|
106
|
+
|
|
107
|
+
const myRegistry = {
|
|
108
|
+
// Map the backend 'type' string to your React Component
|
|
109
|
+
'image_viewer': ImageViewer
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
function App() {
|
|
113
|
+
const data = [
|
|
114
|
+
{
|
|
115
|
+
type: 'image_viewer', // Matches your registry key
|
|
116
|
+
data: { src: 'demo.jpg', caption: 'Injected Component' }
|
|
117
|
+
}
|
|
118
|
+
];
|
|
79
119
|
|
|
80
|
-
function DocumentPage() {
|
|
81
120
|
return (
|
|
82
|
-
<
|
|
83
|
-
<MDXRenderer>
|
|
84
|
-
|
|
85
|
-
</MDXRenderer>
|
|
86
|
-
</ThemeProvider>
|
|
121
|
+
<IntentProvider data={data} registry={myRegistry}>
|
|
122
|
+
<MDXRenderer><Content /></MDXRenderer>
|
|
123
|
+
</IntentProvider>
|
|
87
124
|
);
|
|
88
125
|
}
|
|
89
126
|
|
|
90
127
|
```
|
|
91
128
|
|
|
92
|
-
|
|
129
|
+
### Example: Adapting Data (Adapter Pattern)
|
|
93
130
|
|
|
94
|
-
|
|
131
|
+
If your backend API returns data that doesn't match the library's expected props, don't change the API. Just wrap the component!
|
|
95
132
|
|
|
96
|
-
|
|
133
|
+
```jsx
|
|
134
|
+
import { LineChart } from '@amritanshu3011/mdx-renderer';
|
|
97
135
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
136
|
+
// Backend sends 'history', Library expects 'points'
|
|
137
|
+
const CustomChartAdapter = (props) => {
|
|
138
|
+
const adaptedProps = {
|
|
139
|
+
...props,
|
|
140
|
+
points: props.history // Remap the data
|
|
141
|
+
};
|
|
142
|
+
return <LineChart {...adaptedProps} />;
|
|
143
|
+
};
|
|
106
144
|
|
|
107
|
-
|
|
145
|
+
const myRegistry = {
|
|
146
|
+
'line_chart': CustomChartAdapter // Override the default handler
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
108
152
|
|
|
109
|
-
|
|
153
|
+
## 📚 Built-in Component Reference
|
|
154
|
+
|
|
155
|
+
The library comes with a suite of "Smart Components" ready to use.
|
|
156
|
+
|
|
157
|
+
| Component Key | Description | Required Data Structure |
|
|
110
158
|
| --- | --- | --- |
|
|
111
|
-
|
|
|
159
|
+
| **`line_chart`** | Simple Line Chart | `{ title, xKey, yKey, points: [{x, y}] }` |
|
|
160
|
+
| **`pros_cons`** | Two-column list | `{ title, pros: [], cons: [] }` |
|
|
161
|
+
| **`comparison`** | Feature comparison table | `{ title, items: [{ name, price, features[] }] }` |
|
|
162
|
+
| **`flow`** | Process flow diagram | `{ title, steps: [{ id, label, status }] }` |
|
|
163
|
+
| **`concept_tree`** | Hierarchical tree view | `{ title, data: { name, children: [] } }` |
|
|
164
|
+
| **`sunburst`** | Radial hierarchy chart | `{ title, data: { name, children: [{ name, value }] } }` |
|
|
165
|
+
| **`interactive_composite`** | Tabbed view container | `{ title, selector: {}, views: {} }` |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
## 🎨 Theming
|
|
169
|
+
|
|
170
|
+
The `IntentProvider` accepts a theme object to customize your application's look. You can override colors, spacing, typography, and border radius.
|
|
112
171
|
|
|
113
|
-
|
|
172
|
+
### 1. Basic Customization (Safe Merging)
|
|
114
173
|
|
|
115
|
-
|
|
174
|
+
**Important:** To prevent errors, always merge your overrides with the `defaultTheme`. This ensures that required properties (like spacing or typography) are not missing.
|
|
116
175
|
|
|
117
176
|
```tsx
|
|
118
|
-
import {
|
|
177
|
+
import { IntentProvider, defaultTheme } from '@amritanshu3011/mdx-renderer';
|
|
119
178
|
|
|
120
|
-
|
|
179
|
+
// Create your custom theme by overriding specific values
|
|
180
|
+
const myBrandTheme = {
|
|
181
|
+
...defaultTheme, // 1. Keep default structure
|
|
121
182
|
colors: {
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
background: '#
|
|
125
|
-
text: '#
|
|
183
|
+
...defaultTheme.colors, // 2. Keep default colors (like charts)
|
|
184
|
+
primary: '#ec4899', // 3. Override Primary (Pink)
|
|
185
|
+
background: '#111827', // Override Background
|
|
186
|
+
text: '#f9fafb'
|
|
126
187
|
},
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
medium: '
|
|
130
|
-
large: '24px'
|
|
188
|
+
borderRadius: {
|
|
189
|
+
...defaultTheme.borderRadius,
|
|
190
|
+
medium: '12px' // Rounder corners
|
|
131
191
|
}
|
|
132
192
|
};
|
|
133
193
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
194
|
+
function App() {
|
|
195
|
+
return (
|
|
196
|
+
<IntentProvider theme={myBrandTheme} data={...}>
|
|
197
|
+
<AppContent />
|
|
198
|
+
</IntentProvider>
|
|
199
|
+
);
|
|
200
|
+
}
|
|
138
201
|
```
|
|
202
|
+
**Important:** Here is the full type definition for reference:
|
|
139
203
|
|
|
140
|
-
```
|
|
141
204
|
|
|
205
|
+
```tsx
|
|
206
|
+
type Theme = {
|
|
207
|
+
colors: {
|
|
208
|
+
primary: string;
|
|
209
|
+
secondary: string;
|
|
210
|
+
text: string;
|
|
211
|
+
textSecondary: string;
|
|
212
|
+
background: string;
|
|
213
|
+
cardBackground: string;
|
|
214
|
+
border: string;
|
|
215
|
+
chartLine: string;
|
|
216
|
+
chartBar: string;
|
|
217
|
+
};
|
|
218
|
+
spacing: {
|
|
219
|
+
small: string; // e.g. '8px'
|
|
220
|
+
medium: string; // e.g. '16px'
|
|
221
|
+
large: string; // e.g. '32px'
|
|
222
|
+
xlarge: string; // e.g. '48px'
|
|
223
|
+
};
|
|
224
|
+
typography: {
|
|
225
|
+
fontFamily: string;
|
|
226
|
+
h1Size: string;
|
|
227
|
+
h2Size: string;
|
|
228
|
+
bodySize: string;
|
|
229
|
+
smallSize: string;
|
|
230
|
+
};
|
|
231
|
+
borderRadius: {
|
|
232
|
+
small: string;
|
|
233
|
+
medium: string;
|
|
234
|
+
};
|
|
235
|
+
};
|
|
142
236
|
```
|
package/dist/IntentProvider.d.ts
CHANGED
package/dist/IntentProvider.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { ThemeProvider } from './theme/ThemeContext';
|
|
3
3
|
import { VisualizationProvider } from './context/VisualizationContext';
|
|
4
|
-
export const IntentProvider = ({ children, data = [], theme }
|
|
5
|
-
|
|
4
|
+
export const IntentProvider = ({ children, data = [], theme, registry = {} // Default to empty
|
|
5
|
+
}) => {
|
|
6
|
+
return (_jsx(ThemeProvider, { initialTheme: theme, children: _jsx(VisualizationProvider, { initialVisualizations: data, customRegistry: registry, children: children }) }));
|
|
6
7
|
};
|
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useVisualizations } from '../context/VisualizationContext';
|
|
3
|
-
import { visualizationRegistry } from '../registry/visualizationRegistry';
|
|
4
3
|
export const DynamicVisualizations = () => {
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
// 1. Get the MERGED registry from Context (Defaults + User Custom)
|
|
5
|
+
const { visualizations, registry } = useVisualizations();
|
|
6
|
+
if (!visualizations || visualizations.length === 0)
|
|
7
7
|
return null;
|
|
8
|
-
return (_jsx("div", { children: visualizations.map((viz, index) => {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
return (_jsx("div", { className: "w-full flex flex-col gap-8 my-8", children: visualizations.map((viz, index) => {
|
|
9
|
+
// 2. Look up component in the Context Registry
|
|
10
|
+
// We use string indexing because users can add ANY key
|
|
11
|
+
const Component = registry[viz.type];
|
|
11
12
|
if (!Component) {
|
|
12
|
-
console.warn(`Unknown visualization type
|
|
13
|
-
|
|
13
|
+
console.warn(`MDX Renderer: Unknown visualization type "${viz.type}"`);
|
|
14
|
+
// Render a helpful error for the developer/user
|
|
15
|
+
return (_jsxs("div", { className: "p-4 border border-red-200 bg-red-50 text-red-600 rounded-lg text-sm font-mono", children: ["\u26A0\uFE0F Unknown component type: ", _jsx("strong", { children: viz.type })] }, index));
|
|
14
16
|
}
|
|
15
|
-
return (_jsx("div", { children: _jsx(Component, { ...viz.data }) }, index));
|
|
17
|
+
return (_jsx("div", { className: "mdx-viz-block w-full", children: _jsx(Component, { ...viz.data }) }, index));
|
|
16
18
|
}) }));
|
|
17
19
|
};
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { ReactNode, ComponentType } from 'react';
|
|
2
|
+
export interface VisualizationItem {
|
|
3
|
+
type: string;
|
|
4
|
+
data: any;
|
|
5
|
+
}
|
|
3
6
|
interface VisualizationContextType {
|
|
4
|
-
visualizations:
|
|
5
|
-
addVisualization: (viz:
|
|
7
|
+
visualizations: VisualizationItem[];
|
|
8
|
+
addVisualization: (viz: VisualizationItem) => void;
|
|
6
9
|
clearVisualizations: () => void;
|
|
10
|
+
registry: Record<string, ComponentType<any>>;
|
|
11
|
+
}
|
|
12
|
+
export interface VisualizationProviderProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
initialVisualizations?: VisualizationItem[];
|
|
15
|
+
customRegistry?: Record<string, ComponentType<any>>;
|
|
7
16
|
}
|
|
8
|
-
export declare const VisualizationProvider: ({ children, initialVisualizations }:
|
|
9
|
-
children: React.ReactNode;
|
|
10
|
-
initialVisualizations?: any[];
|
|
11
|
-
}) => import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
export declare const VisualizationProvider: ({ children, initialVisualizations, customRegistry }: VisualizationProviderProps) => import("react/jsx-runtime").JSX.Element;
|
|
12
18
|
export declare const useVisualizations: () => VisualizationContextType;
|
|
13
19
|
export {};
|
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React, { createContext, useContext } from 'react';
|
|
2
|
+
import React, { createContext, useContext, useState } from 'react';
|
|
3
|
+
// Import your default registry to use as a base
|
|
4
|
+
import { visualizationRegistry as defaultRegistry } from '../registry/visualizationRegistry';
|
|
3
5
|
const VisualizationContext = createContext(undefined);
|
|
4
|
-
export const VisualizationProvider = ({ children, initialVisualizations = [] }) => {
|
|
5
|
-
const [visualizations, setVisualizations] =
|
|
6
|
+
export const VisualizationProvider = ({ children, initialVisualizations = [], customRegistry = {} }) => {
|
|
7
|
+
const [visualizations, setVisualizations] = useState(initialVisualizations);
|
|
8
|
+
// 1. MERGE LOGIC: Combine defaults with user overrides
|
|
9
|
+
// Using useMemo is good practice here to prevent recreation on every render,
|
|
10
|
+
// but for simplicity, direct object spread works too.
|
|
11
|
+
const mergedRegistry = { ...defaultRegistry, ...customRegistry };
|
|
12
|
+
// 2. Reactive State Update
|
|
6
13
|
React.useEffect(() => {
|
|
7
14
|
if (initialVisualizations.length > 0) {
|
|
8
15
|
setVisualizations(initialVisualizations);
|
|
9
16
|
}
|
|
10
17
|
}, [initialVisualizations]);
|
|
11
18
|
const addVisualization = (viz) => {
|
|
12
|
-
console.log('Storing visualization:', viz);
|
|
13
19
|
setVisualizations(prev => [...prev, viz]);
|
|
14
20
|
};
|
|
15
21
|
const clearVisualizations = () => {
|
|
16
22
|
setVisualizations([]);
|
|
17
23
|
};
|
|
18
|
-
return (_jsx(VisualizationContext.Provider, { value: {
|
|
24
|
+
return (_jsx(VisualizationContext.Provider, { value: {
|
|
25
|
+
visualizations,
|
|
26
|
+
addVisualization,
|
|
27
|
+
clearVisualizations,
|
|
28
|
+
registry: mergedRegistry // <--- Pass the merged registry to the app
|
|
29
|
+
}, children: children }));
|
|
19
30
|
};
|
|
20
31
|
export const useVisualizations = () => {
|
|
21
32
|
const context = useContext(VisualizationContext);
|