@amritanshu3011/mdx-renderer 1.0.4 → 1.0.6

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.
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { MDXProvider } from '@mdx-js/react';
3
- import { useThemeWithFallback } from './utils/useThemeWithFallback';
3
+ // 👇 CHANGE 1: Use the Context Hook (so updates happen live)
4
+ // import { useThemeWithFallback } from './utils/useThemeWithFallback';
5
+ import { useTheme } from './theme/ThemeContext';
4
6
  // Text components
5
7
  import { Heading } from './components/text/Heading';
6
8
  import { Paragraph } from './components/text/Paragraph';
@@ -51,6 +53,27 @@ const components = {
51
53
  ComparisonGrid,
52
54
  };
53
55
  export const MDXRenderer = ({ children, className = 'mdx-content' }) => {
54
- const theme = useThemeWithFallback();
55
- return (_jsx("div", { className: className, "data-theme": theme.colors.background === '#212529' ? 'dark' : 'light', children: _jsx(MDXProvider, { components: components, children: children }) }));
56
+ // 👇 CHANGE 3: Get theme from Context
57
+ const { theme } = useTheme();
58
+ // 👇 CHANGE 4: Create the Bridge (JS -> CSS Variables)
59
+ const themeStyles = {
60
+ // Colors
61
+ '--color-primary': theme.colors.primary,
62
+ '--color-secondary': theme.colors.secondary,
63
+ '--color-text': theme.colors.text,
64
+ '--color-text-secondary': theme.colors.textSecondary,
65
+ '--color-background': theme.colors.background,
66
+ '--color-card-bg': theme.colors.cardBackground,
67
+ '--color-border': theme.colors.border,
68
+ // Spacing
69
+ '--spacing-small': theme.spacing.small,
70
+ '--spacing-medium': theme.spacing.medium,
71
+ '--spacing-large': theme.spacing.large,
72
+ // Typography
73
+ '--font-family': theme.typography.fontFamily,
74
+ // Border Radius
75
+ '--radius-small': theme.borderRadius.small,
76
+ '--radius-medium': theme.borderRadius.medium,
77
+ };
78
+ return (_jsx("div", { className: className, style: themeStyles, children: _jsx(MDXProvider, { components: components, children: children }) }));
56
79
  };
@@ -1,11 +1,18 @@
1
1
  import React from 'react';
2
- interface FlowStep {
3
- title: string;
4
- description: string;
2
+ export interface FlowStep {
3
+ id: string;
4
+ label: string;
5
+ value: number;
6
+ color?: string;
7
+ dropOff?: string;
8
+ breakdown?: {
9
+ label: string;
10
+ value: number;
11
+ }[];
5
12
  }
6
13
  export interface FlowBlockProps {
7
14
  title: string;
15
+ total_candidates?: number;
8
16
  steps: FlowStep[];
9
17
  }
10
18
  export declare const FlowBlock: React.FC<FlowBlockProps>;
11
- export {};
@@ -1,46 +1,16 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React, { useState, useMemo } from 'react';
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React, { useState } from 'react';
3
3
  import { useThemeWithFallback } from '../../utils/useThemeWithFallback';
4
- export const FlowBlock = ({ title, steps }) => {
4
+ export const FlowBlock = ({ title, total_candidates, steps }) => {
5
5
  const theme = useThemeWithFallback();
6
- // --- State for Data Manipulation ---
7
- const [viewMode, setViewMode] = useState('flow');
8
- const [filterText, setFilterText] = useState('');
9
- const [sortMode, setSortMode] = useState('original');
10
- const [isGrouped, setIsGrouped] = useState(false); // Group by First Letter
11
- const [isPivoted, setIsPivoted] = useState(false); // Only useful in Table view
12
- // --- Logic: Filter & Sort ---
13
- const processedData = useMemo(() => {
14
- let data = [...steps];
15
- // 1. Filter
16
- if (filterText) {
17
- const lower = filterText.toLowerCase();
18
- data = data.filter(s => s.title.toLowerCase().includes(lower) ||
19
- s.description.toLowerCase().includes(lower));
20
- }
21
- // 2. Sort
22
- if (sortMode !== 'original') {
23
- data.sort((a, b) => {
24
- return sortMode === 'asc'
25
- ? a.title.localeCompare(b.title)
26
- : b.title.localeCompare(a.title);
27
- });
28
- }
29
- return data;
30
- }, [steps, filterText, sortMode]);
31
- // --- Logic: Grouping ---
32
- const groupedData = useMemo(() => {
33
- if (!isGrouped)
34
- return { 'All Steps': processedData };
35
- return processedData.reduce((acc, step) => {
36
- const key = step.title.charAt(0).toUpperCase();
37
- if (!acc[key])
38
- acc[key] = [];
39
- acc[key].push(step);
40
- return acc;
41
- }, {});
42
- }, [processedData, isGrouped]);
43
- // --- Styles Helper ---
6
+ const [viewMode, setViewMode] = useState('chart');
7
+ // Track expanded rows for both Chart and Table views
8
+ const [expandedSteps, setExpandedSteps] = useState({});
9
+ const toggleStep = (id) => {
10
+ setExpandedSteps(prev => ({ ...prev, [id]: !prev[id] }));
11
+ };
12
+ const maxValue = Math.max(...steps.map(s => s.value));
13
+ // --- Styles ---
44
14
  const btnStyle = (active) => ({
45
15
  padding: `${theme.spacing.small} ${theme.spacing.medium}`,
46
16
  backgroundColor: active ? theme.colors.primary : 'transparent',
@@ -49,74 +19,79 @@ export const FlowBlock = ({ title, steps }) => {
49
19
  borderRadius: theme.borderRadius.small,
50
20
  cursor: 'pointer',
51
21
  fontSize: theme.typography.smallSize,
52
- fontWeight: 'bold'
22
+ fontWeight: 'bold',
23
+ transition: 'all 0.2s',
53
24
  });
54
25
  return (_jsxs("div", { style: {
55
26
  margin: `${theme.spacing.large} 0`,
56
- border: `1px solid ${theme.colors.border || '#ddd'}`,
27
+ border: `1px solid ${theme.colors.border || '#e5e7eb'}`,
57
28
  borderRadius: theme.borderRadius.medium,
29
+ backgroundColor: theme.colors.background,
58
30
  boxShadow: '0 4px 12px rgba(0,0,0,0.05)',
59
31
  overflow: 'hidden'
60
32
  }, children: [_jsxs("div", { style: {
61
- padding: theme.spacing.medium,
62
- borderBottom: `1px solid ${theme.colors.border || '#eee'}`,
63
- backgroundColor: theme.colors.background || '#f9f9f9'
64
- }, children: [_jsx("h2", { style: {
65
- color: theme.colors.text,
66
- margin: `0 0 ${theme.spacing.medium} 0`,
67
- textAlign: 'left'
68
- }, children: title }), _jsxs("div", { style: { display: 'flex', flexWrap: 'wrap', gap: theme.spacing.medium, alignItems: 'center', justifyContent: 'space-between' }, children: [_jsxs("div", { style: { display: 'flex', gap: theme.spacing.small, flexWrap: 'wrap' }, children: [_jsx("input", { type: "text", placeholder: "Filter steps...", value: filterText, onChange: (e) => setFilterText(e.target.value), style: {
69
- padding: theme.spacing.small,
70
- borderRadius: theme.borderRadius.small,
71
- border: `1px solid ${theme.colors.border || '#ccc'}`
72
- } }), _jsxs("button", { onClick: () => setSortMode(prev => prev === 'original' ? 'asc' : prev === 'asc' ? 'desc' : 'original'), style: btnStyle(sortMode !== 'original'), children: ["Sort ", sortMode === 'original' ? 'Original' : sortMode === 'asc' ? '(A-Z)' : '(Z-A)'] }), _jsx("button", { onClick: () => setIsGrouped(!isGrouped), style: btnStyle(isGrouped), children: isGrouped ? 'Ungroup' : 'Group (A-Z)' })] }), _jsxs("div", { style: { display: 'flex', gap: theme.spacing.small }, children: [viewMode === 'table' && (_jsx("button", { onClick: () => setIsPivoted(!isPivoted), style: btnStyle(isPivoted), children: "Pivot Data" })), _jsx("div", { style: { display: 'flex', border: `1px solid ${theme.colors.primary}`, borderRadius: theme.borderRadius.small, overflow: 'hidden' }, children: ['flow', 'grid', 'list', 'table'].map((v) => (_jsx("button", { onClick: () => setViewMode(v), style: {
73
- ...btnStyle(viewMode === v),
74
- border: 'none',
75
- borderRadius: 0,
76
- textTransform: 'capitalize'
77
- }, children: v }, v))) })] })] })] }), _jsxs("div", { style: { padding: theme.spacing.medium, backgroundColor: theme.colors.background }, children: [Object.entries(groupedData).map(([groupTitle, groupSteps]) => (_jsxs("div", { style: { marginBottom: theme.spacing.large }, children: [isGrouped && (_jsxs("h4", { style: {
78
- color: theme.colors.textSecondary,
79
- borderBottom: `1px solid ${theme.colors.border || '#eee'}`,
80
- marginBottom: theme.spacing.medium
81
- }, children: ["Group: ", groupTitle] })), viewMode === 'flow' && (_jsxs("div", { style: {
82
- display: 'flex',
83
- alignItems: 'center',
84
- gap: theme.spacing.medium,
85
- overflowX: 'auto',
86
- padding: theme.spacing.small
87
- }, children: [groupSteps.map((step, i) => (_jsxs(React.Fragment, { children: [_jsxs("div", { style: {
88
- flex: '0 0 220px',
89
- padding: theme.spacing.medium,
90
- backgroundColor: theme.colors.cardBackground,
91
- border: `2px solid ${theme.colors.primary}`,
92
- borderRadius: theme.borderRadius.medium,
93
- boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
94
- }, children: [_jsxs("div", { style: {
95
- fontWeight: 'bold',
96
- color: theme.colors.primary,
97
- marginBottom: theme.spacing.small,
98
- fontSize: theme.typography.bodySize
99
- }, children: [i + 1, ". ", step.title] }), _jsx("div", { style: {
100
- color: theme.colors.textSecondary,
101
- fontSize: theme.typography.smallSize,
102
- lineHeight: '1.5'
103
- }, children: step.description })] }), i < groupSteps.length - 1 && (_jsx("div", { style: {
104
- color: theme.colors.primary,
105
- fontSize: '28px',
106
- fontWeight: 'bold',
107
- minWidth: '20px'
108
- }, children: "\u2192" }))] }, i))), groupSteps.length === 0 && _jsx("div", { style: { color: theme.colors.textSecondary }, children: "No steps match filter." })] })), viewMode === 'grid' && (_jsx("div", { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(220px, 1fr))', gap: theme.spacing.medium }, children: groupSteps.map((step, i) => (_jsxs("div", { style: {
109
- padding: theme.spacing.medium,
110
- backgroundColor: theme.colors.cardBackground,
111
- border: `1px solid ${theme.colors.border || '#ddd'}`,
112
- borderRadius: theme.borderRadius.medium
113
- }, children: [_jsx("div", { style: { fontWeight: 'bold', color: theme.colors.text }, children: step.title }), _jsx("hr", { style: { margin: '8px 0', border: '0', borderTop: `1px solid ${theme.colors.border || '#eee'}` } }), _jsx("div", { style: { color: theme.colors.textSecondary, fontSize: theme.typography.smallSize }, children: step.description })] }, i))) })), viewMode === 'list' && (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: theme.spacing.small }, children: groupSteps.map((step, i) => (_jsxs("div", { style: {
114
- padding: theme.spacing.medium,
115
- backgroundColor: theme.colors.cardBackground,
116
- borderLeft: `4px solid ${theme.colors.primary}`,
117
- borderRadius: theme.borderRadius.small,
118
- display: 'flex',
119
- alignItems: 'center',
120
- justifyContent: 'space-between'
121
- }, children: [_jsxs("span", { style: { fontWeight: 'bold', color: theme.colors.text }, children: [i + 1, ". ", step.title] }), _jsx("span", { style: { color: theme.colors.textSecondary }, children: step.description })] }, i))) })), viewMode === 'table' && (_jsx("div", { style: { overflowX: 'auto' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: theme.typography.smallSize }, children: [!isPivoted && (_jsxs(_Fragment, { children: [_jsx("thead", { children: _jsxs("tr", { style: { backgroundColor: theme.colors.background, borderBottom: `2px solid ${theme.colors.primary}` }, children: [_jsx("th", { style: { padding: theme.spacing.small, textAlign: 'left' }, children: "Step #" }), _jsx("th", { style: { padding: theme.spacing.small, textAlign: 'left' }, children: "Title" }), _jsx("th", { style: { padding: theme.spacing.small, textAlign: 'left' }, children: "Description" })] }) }), _jsx("tbody", { children: groupSteps.map((step, i) => (_jsxs("tr", { style: { borderBottom: `1px solid ${theme.colors.border || '#eee'}` }, children: [_jsx("td", { style: { padding: theme.spacing.small }, children: i + 1 }), _jsx("td", { style: { padding: theme.spacing.small, fontWeight: 'bold' }, children: step.title }), _jsx("td", { style: { padding: theme.spacing.small, color: theme.colors.textSecondary }, children: step.description })] }, i))) })] })), isPivoted && (_jsxs("tbody", { children: [_jsxs("tr", { style: { borderBottom: `1px solid ${theme.colors.border || '#eee'}` }, children: [_jsx("th", { style: { padding: theme.spacing.small, textAlign: 'right', backgroundColor: theme.colors.background }, children: "Title" }), groupSteps.map((step, i) => (_jsx("td", { style: { padding: theme.spacing.small, fontWeight: 'bold', borderLeft: `1px solid ${theme.colors.border}` }, children: step.title }, i)))] }), _jsxs("tr", { children: [_jsx("th", { style: { padding: theme.spacing.small, textAlign: 'right', backgroundColor: theme.colors.background }, children: "Description" }), groupSteps.map((step, i) => (_jsx("td", { style: { padding: theme.spacing.small, color: theme.colors.textSecondary, borderLeft: `1px solid ${theme.colors.border}` }, children: step.description }, i)))] })] }))] }) }))] }, groupTitle))), processedData.length === 0 && (_jsxs("div", { style: { textAlign: 'center', padding: theme.spacing.large, color: theme.colors.textSecondary }, children: ["No results found for \"", filterText, "\""] }))] })] }));
33
+ padding: '16px 20px',
34
+ borderBottom: `1px solid ${theme.colors.border || '#e5e7eb'}`,
35
+ display: 'flex',
36
+ justifyContent: 'space-between',
37
+ alignItems: 'center',
38
+ backgroundColor: theme.colors.cardBackground
39
+ }, children: [_jsxs("div", { children: [_jsx("h3", { style: { margin: 0, fontSize: '18px', color: theme.colors.text }, children: title }), total_candidates && (_jsxs("div", { style: { fontSize: '13px', color: theme.colors.textSecondary, marginTop: '4px' }, children: ["Total Volume: ", _jsx("strong", { children: total_candidates.toLocaleString() })] }))] }), _jsxs("div", { style: { display: 'flex', borderRadius: '6px', overflow: 'hidden', border: `1px solid ${theme.colors.primary}` }, children: [_jsx("button", { onClick: () => setViewMode('chart'), style: { ...btnStyle(viewMode === 'chart'), border: 'none', borderRadius: 0 }, children: "Funnel" }), _jsx("button", { onClick: () => setViewMode('table'), style: { ...btnStyle(viewMode === 'table'), border: 'none', borderRadius: 0 }, children: "Grid" })] })] }), _jsxs("div", { style: { padding: '24px' }, children: [viewMode === 'chart' && (_jsx("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '8px' }, children: steps.map((step, index) => {
40
+ const widthPercentage = Math.max(25, (step.value / maxValue) * 100);
41
+ const isExpanded = expandedSteps[step.id];
42
+ const hasBreakdown = step.breakdown && step.breakdown.length > 0;
43
+ return (_jsxs("div", { style: { width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center' }, children: [_jsxs("div", { onClick: () => hasBreakdown && toggleStep(step.id), style: {
44
+ width: `${widthPercentage}%`,
45
+ backgroundColor: step.color || theme.colors.primary,
46
+ borderRadius: '8px',
47
+ padding: '12px 16px',
48
+ color: '#fff',
49
+ display: 'flex',
50
+ justifyContent: 'space-between',
51
+ alignItems: 'center',
52
+ cursor: hasBreakdown ? 'pointer' : 'default',
53
+ boxShadow: '0 2px 4px rgba(0,0,0,0.15)',
54
+ transition: 'width 0.3s ease, transform 0.1s',
55
+ position: 'relative'
56
+ }, onMouseEnter: (e) => e.currentTarget.style.transform = hasBreakdown ? 'scale(1.01)' : 'none', onMouseLeave: (e) => e.currentTarget.style.transform = 'none', children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [hasBreakdown && _jsx("span", { style: { fontSize: '10px' }, children: isExpanded ? '▼' : '▶' }), _jsxs("span", { style: { fontWeight: 600, fontSize: '15px' }, children: [index + 1, ". ", step.label] })] }), _jsx("span", { style: { fontWeight: 'bold', backgroundColor: 'rgba(255,255,255,0.2)', padding: '2px 8px', borderRadius: '4px' }, children: step.value.toLocaleString() })] }), isExpanded && hasBreakdown && (_jsx("div", { style: {
57
+ width: `${widthPercentage}%`,
58
+ backgroundColor: theme.colors.cardBackground,
59
+ border: `1px solid ${theme.colors.border}`,
60
+ borderTop: 'none',
61
+ borderRadius: '0 0 8px 8px',
62
+ padding: '12px',
63
+ marginBottom: '10px',
64
+ boxShadow: 'inset 0 4px 6px -4px rgba(0,0,0,0.1)'
65
+ }, children: _jsx("table", { style: { width: '100%', fontSize: '13px', color: theme.colors.text }, children: _jsx("tbody", { children: step.breakdown.map((item, idx) => (_jsxs("tr", { style: { borderBottom: idx !== step.breakdown.length - 1 ? `1px solid ${theme.colors.border}` : 'none' }, children: [_jsx("td", { style: { padding: '6px 0', color: theme.colors.textSecondary }, children: item.label }), _jsx("td", { style: { padding: '6px 0', textAlign: 'right', fontWeight: 'bold' }, children: item.value.toLocaleString() })] }, idx))) }) }) })), !isExpanded && index < steps.length - 1 && (_jsxs("div", { style: { height: '24px', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative', width: '100%' }, children: [_jsx("div", { style: { width: '2px', height: '100%', backgroundColor: theme.colors.border } }), step.dropOff && (_jsxs("span", { style: {
66
+ position: 'absolute',
67
+ fontSize: '11px',
68
+ color: '#dc2626',
69
+ backgroundColor: '#fef2f2',
70
+ padding: '2px 8px',
71
+ borderRadius: '10px',
72
+ border: '1px solid #fecaca',
73
+ fontWeight: 600
74
+ }, children: [step.dropOff, " Drop-off"] }))] }))] }, step.id));
75
+ }) })), viewMode === 'table' && (_jsx("div", { style: { overflowX: 'auto', border: `1px solid ${theme.colors.border}`, borderRadius: '8px' }, children: _jsxs("table", { style: { width: '100%', borderCollapse: 'collapse', fontSize: '14px', backgroundColor: theme.colors.cardBackground }, children: [_jsx("thead", { children: _jsxs("tr", { style: { backgroundColor: theme.colors.background, borderBottom: `2px solid ${theme.colors.border}` }, children: [_jsx("th", { style: { padding: '12px 16px', textAlign: 'left', color: theme.colors.textSecondary, fontWeight: 600 }, children: "Stage" }), _jsx("th", { style: { padding: '12px 16px', textAlign: 'right', color: theme.colors.textSecondary, fontWeight: 600 }, children: "Candidates" }), _jsx("th", { style: { padding: '12px 16px', textAlign: 'right', color: theme.colors.textSecondary, fontWeight: 600 }, children: "Status" })] }) }), _jsx("tbody", { children: steps.map((step, i) => {
76
+ const hasBreakdown = step.breakdown && step.breakdown.length > 0;
77
+ const isExpanded = expandedSteps[step.id];
78
+ return (_jsxs(React.Fragment, { children: [_jsxs("tr", { onClick: () => hasBreakdown && toggleStep(step.id), style: {
79
+ borderBottom: isExpanded ? 'none' : `1px solid ${theme.colors.border}`,
80
+ cursor: hasBreakdown ? 'pointer' : 'default',
81
+ transition: 'background-color 0.2s'
82
+ }, onMouseEnter: (e) => e.currentTarget.style.backgroundColor = theme.colors.background, onMouseLeave: (e) => e.currentTarget.style.backgroundColor = 'transparent', children: [_jsx("td", { style: { padding: '14px 16px', fontWeight: 600, color: theme.colors.text }, children: _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [hasBreakdown && (_jsx("span", { style: { color: theme.colors.primary, fontSize: '12px' }, children: isExpanded ? '▼' : '▶' })), i + 1, ". ", step.label] }) }), _jsx("td", { style: { padding: '14px 16px', textAlign: 'right', fontWeight: 'bold', fontSize: '15px' }, children: step.value.toLocaleString() }), _jsx("td", { style: { padding: '14px 16px', textAlign: 'right' }, children: step.dropOff ? (_jsxs("span", { style: {
83
+ color: '#dc2626',
84
+ backgroundColor: '#fef2f2',
85
+ padding: '4px 8px',
86
+ borderRadius: '4px',
87
+ fontSize: '12px',
88
+ fontWeight: 600
89
+ }, children: [step.dropOff, " Loss"] })) : (_jsx("span", { style: { color: '#16a34a', fontSize: '20px' }, children: "\u2022" })) })] }), isExpanded && hasBreakdown && (_jsx("tr", { style: { borderBottom: `1px solid ${theme.colors.border}`, backgroundColor: theme.colors.background }, children: _jsx("td", { colSpan: 3, style: { padding: '0 0 12px 0' }, children: _jsxs("div", { style: {
90
+ margin: '0 20px',
91
+ padding: '12px',
92
+ borderLeft: `3px solid ${theme.colors.primary}`,
93
+ backgroundColor: theme.colors.cardBackground,
94
+ borderRadius: '0 4px 4px 0'
95
+ }, children: [_jsx("h4", { style: { margin: '0 0 8px 0', fontSize: '12px', textTransform: 'uppercase', color: theme.colors.textSecondary }, children: "Breakdown" }), _jsx("table", { style: { width: '100%', fontSize: '13px' }, children: _jsx("tbody", { children: step.breakdown.map((b, idx) => (_jsxs("tr", { children: [_jsx("td", { style: { padding: '4px 0', color: theme.colors.text }, children: b.label }), _jsx("td", { style: { padding: '4px 0', textAlign: 'right', fontWeight: 600 }, children: b.value.toLocaleString() })] }, idx))) }) })] }) }) }))] }, step.id));
96
+ }) })] }) }))] })] }));
122
97
  };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useMemo } from 'react';
3
- import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, RadialBarChart, RadialBar, Treemap } from 'recharts';
2
+ import { useState, useMemo, useEffect, useRef } from 'react';
3
+ import * as echarts from 'echarts';
4
4
  import { useThemeWithFallback } from '../../utils/useThemeWithFallback';
5
5
  export const InteractiveCompositeBlock = ({ title, selector, views }) => {
6
6
  const theme = useThemeWithFallback();
@@ -9,18 +9,19 @@ export const InteractiveCompositeBlock = ({ title, selector, views }) => {
9
9
  const [viewMode, setViewMode] = useState('split');
10
10
  const [filterText, setFilterText] = useState('');
11
11
  const [sortConfig, setSortConfig] = useState({ col: '', dir: null });
12
+ // --- Refs ---
13
+ const chartRef = useRef(null);
14
+ const chartInstance = useRef(null);
12
15
  // --- Data Access ---
13
- const chartData = views.chart.series[selected] || [];
16
+ const rawChartData = views.chart.series[selected] || [];
14
17
  const rawTableRows = views.table.rows[selected] || [];
15
18
  // --- Logic: Process Table Data ---
16
19
  const tableRows = useMemo(() => {
17
20
  let data = [...rawTableRows];
18
- // 1. Filter
19
21
  if (filterText) {
20
22
  const lower = filterText.toLowerCase();
21
23
  data = data.filter(row => Object.values(row).some(val => String(val).toLowerCase().includes(lower)));
22
24
  }
23
- // 2. Sort
24
25
  if (sortConfig.col && sortConfig.dir) {
25
26
  data.sort((a, b) => {
26
27
  const valA = a[sortConfig.col];
@@ -35,24 +36,141 @@ export const InteractiveCompositeBlock = ({ title, selector, views }) => {
35
36
  }
36
37
  return data;
37
38
  }, [rawTableRows, filterText, sortConfig]);
38
- // --- Handlers ---
39
39
  const handleSort = (col) => {
40
40
  setSortConfig(prev => ({
41
41
  col,
42
42
  dir: prev.col === col && prev.dir === 'asc' ? 'desc' : 'asc'
43
43
  }));
44
44
  };
45
- const renderChart = () => {
46
- switch (views.chart.chartType) {
47
- case 'radial_bar':
48
- return (_jsxs(RadialBarChart, { innerRadius: "30%", outerRadius: "90%", data: chartData, children: [_jsx(RadialBar, { dataKey: views.chart.yKey, fill: theme.colors.primary }), _jsx(Tooltip, {})] }));
49
- case 'treemap':
50
- return (_jsx(Treemap, { data: chartData, dataKey: views.chart.yKey, stroke: theme.colors.border, fill: theme.colors.primary, children: _jsx(Tooltip, {}) }));
51
- case 'line':
52
- default:
53
- return (_jsxs(LineChart, { data: chartData, children: [views.chart.xKey && _jsx(XAxis, { dataKey: views.chart.xKey, tick: { fill: theme.colors.textSecondary } }), _jsx(YAxis, { tick: { fill: theme.colors.textSecondary } }), _jsx(Tooltip, {}), _jsx(Line, { type: "monotone", dataKey: views.chart.yKey, stroke: theme.colors.primary, strokeWidth: 2, dot: false })] }));
45
+ // --- Logic: ECharts Option Generator ---
46
+ const chartOptions = useMemo(() => {
47
+ const { chartType, xKey, yKey } = views.chart;
48
+ const commonOption = {
49
+ tooltip: {
50
+ trigger: 'item',
51
+ backgroundColor: theme.colors.cardBackground,
52
+ borderColor: theme.colors.border,
53
+ textStyle: { color: theme.colors.text }
54
+ },
55
+ grid: { top: 30, right: 30, bottom: 20, left: 40, containLabel: true },
56
+ textStyle: { fontFamily: 'sans-serif' },
57
+ };
58
+ if (chartType === 'line') {
59
+ return {
60
+ ...commonOption,
61
+ tooltip: { trigger: 'axis' },
62
+ xAxis: {
63
+ type: 'category',
64
+ // Fallback to empty string if xKey is missing
65
+ data: rawChartData.map(d => xKey ? d[xKey] : ''),
66
+ axisLine: { lineStyle: { color: theme.colors.border } },
67
+ axisLabel: { color: theme.colors.textSecondary }
68
+ },
69
+ yAxis: {
70
+ type: 'value',
71
+ splitLine: { lineStyle: { type: 'dashed', color: theme.colors.border } },
72
+ axisLabel: { color: theme.colors.textSecondary }
73
+ },
74
+ series: [{
75
+ data: rawChartData.map(d => d[yKey]),
76
+ type: 'line',
77
+ smooth: true,
78
+ symbol: 'circle',
79
+ symbolSize: 6,
80
+ itemStyle: { color: theme.colors.primary },
81
+ areaStyle: {
82
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
83
+ { offset: 0, color: theme.colors.primary },
84
+ { offset: 1, color: 'rgba(255, 255, 255, 0)' }
85
+ ]),
86
+ opacity: 0.2
87
+ }
88
+ }]
89
+ };
54
90
  }
55
- };
91
+ if (chartType === 'radial_bar') {
92
+ return {
93
+ ...commonOption,
94
+ polar: { radius: [30, '80%'] },
95
+ angleAxis: {
96
+ type: 'category',
97
+ data: rawChartData.map(d => xKey ? d[xKey] : ''),
98
+ startAngle: 75,
99
+ axisLine: { lineStyle: { color: theme.colors.border } },
100
+ axisLabel: { color: theme.colors.textSecondary }
101
+ },
102
+ radiusAxis: { type: 'value', show: false },
103
+ series: [{
104
+ type: 'bar',
105
+ data: rawChartData.map(d => d[yKey]),
106
+ coordinateSystem: 'polar',
107
+ itemStyle: { color: theme.colors.primary },
108
+ label: { show: true, position: 'middle', formatter: '{b}' }
109
+ }]
110
+ };
111
+ }
112
+ if (chartType === 'treemap') {
113
+ // FIX: Better logic to determine the label (name)
114
+ const treeData = rawChartData.map((d, index) => {
115
+ // 1. Try explicit xKey
116
+ if (xKey && d[xKey])
117
+ return { name: d[xKey], value: d[yKey] };
118
+ // 2. Try guessing common name keys
119
+ const nameCandidate = d.name || d.label || d.category || d.id;
120
+ if (nameCandidate)
121
+ return { name: nameCandidate, value: d[yKey] };
122
+ // 3. Fallback to index so it's unique (prevents "Node Node")
123
+ return { name: `Item ${index + 1}`, value: d[yKey] };
124
+ });
125
+ return {
126
+ ...commonOption,
127
+ series: [{
128
+ type: 'treemap',
129
+ data: treeData,
130
+ label: {
131
+ show: true,
132
+ formatter: '{b}' // Show name
133
+ },
134
+ itemStyle: {
135
+ borderColor: theme.colors.cardBackground,
136
+ borderWidth: 2,
137
+ gapWidth: 1
138
+ },
139
+ levels: [
140
+ { itemStyle: { borderColor: '#555', borderWidth: 4, gapWidth: 4 } },
141
+ { colorSaturation: [0.3, 0.6], itemStyle: { borderColorSaturation: 0.7, gapWidth: 2, borderWidth: 2 } }
142
+ ]
143
+ }]
144
+ };
145
+ }
146
+ return {};
147
+ }, [views.chart, rawChartData, theme]);
148
+ // --- Effect: Initialize & Update Chart ---
149
+ useEffect(() => {
150
+ if (chartRef.current && !chartInstance.current) {
151
+ chartInstance.current = echarts.init(chartRef.current);
152
+ }
153
+ if (chartInstance.current) {
154
+ chartInstance.current.setOption(chartOptions, true);
155
+ }
156
+ const handleResize = () => chartInstance.current?.resize();
157
+ window.addEventListener('resize', handleResize);
158
+ return () => {
159
+ window.removeEventListener('resize', handleResize);
160
+ if (!chartRef.current && chartInstance.current) {
161
+ chartInstance.current.dispose();
162
+ chartInstance.current = null;
163
+ }
164
+ };
165
+ }, [chartOptions, viewMode]);
166
+ useEffect(() => {
167
+ if (viewMode === 'table') {
168
+ if (chartInstance.current) {
169
+ chartInstance.current.dispose();
170
+ chartInstance.current = null;
171
+ }
172
+ }
173
+ }, [viewMode]);
56
174
  const btnStyle = (active) => ({
57
175
  padding: '4px 12px',
58
176
  backgroundColor: active ? theme.colors.primary : 'transparent',
@@ -83,7 +201,7 @@ export const InteractiveCompositeBlock = ({ title, selector, views }) => {
83
201
  borderRadius: theme.borderRadius.medium,
84
202
  border: `1px solid ${theme.colors.border}`,
85
203
  marginBottom: viewMode === 'split' ? theme.spacing.medium : 0
86
- }, children: _jsx(ResponsiveContainer, { width: "100%", height: "100%", children: renderChart() }) })), (viewMode === 'split' || viewMode === 'table') && (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: theme.spacing.small, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("input", { type: "text", placeholder: "Filter table data...", value: filterText, onChange: (e) => setFilterText(e.target.value), style: { padding: '4px 8px', borderRadius: theme.borderRadius.small, border: `1px solid ${theme.colors.border}`, width: '200px' } }), _jsxs("span", { style: { fontSize: '11px', color: theme.colors.textSecondary }, children: [tableRows.length, " rows found"] })] }), _jsx("div", { style: {
204
+ }, children: _jsx("div", { ref: chartRef, style: { width: '100%', height: '100%' } }) })), (viewMode === 'split' || viewMode === 'table') && (_jsxs("div", { children: [_jsxs("div", { style: { marginBottom: theme.spacing.small, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }, children: [_jsx("input", { type: "text", placeholder: "Filter table data...", value: filterText, onChange: (e) => setFilterText(e.target.value), style: { padding: '4px 8px', borderRadius: theme.borderRadius.small, border: `1px solid ${theme.colors.border}`, width: '200px' } }), _jsxs("span", { style: { fontSize: '11px', color: theme.colors.textSecondary }, children: [tableRows.length, " rows found"] })] }), _jsx("div", { style: {
87
205
  overflowX: 'auto',
88
206
  backgroundColor: theme.colors.cardBackground,
89
207
  borderRadius: theme.borderRadius.medium,
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export { defaultTheme } from './theme/defaultTheme';
4
4
  export { useThemeWithFallback } from './utils/useThemeWithFallback';
5
5
  export type { Theme } from './theme/types';
6
6
  export { MDXRenderer } from './MDXRenderer';
7
+ import './styles/base.css';
7
8
  export { ProsConsBlock } from './components/smart/ProsConsBlock';
8
9
  export { ComparisonBlock } from './components/smart/ComparisonBlock';
9
10
  export { FlowBlock } from './components/smart/FlowBlock';
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { defaultTheme } from './theme/defaultTheme';
5
5
  export { useThemeWithFallback } from './utils/useThemeWithFallback';
6
6
  // Renderer
7
7
  export { MDXRenderer } from './MDXRenderer';
8
+ import './styles/base.css';
8
9
  // Smart Components
9
10
  export { ProsConsBlock } from './components/smart/ProsConsBlock';
10
11
  export { ComparisonBlock } from './components/smart/ComparisonBlock';
@@ -21,7 +21,7 @@ export const lightTheme = {
21
21
  h2Size: '24px',
22
22
  bodySize: '16px',
23
23
  smallSize: '14px',
24
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
24
+ fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
25
25
  },
26
26
  borderRadius: {
27
27
  small: '4px',
@@ -51,7 +51,7 @@ export const darkTheme = {
51
51
  h2Size: '24px',
52
52
  bodySize: '16px',
53
53
  smallSize: '14px',
54
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
54
+ fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
55
55
  },
56
56
  borderRadius: {
57
57
  small: '4px',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@amritanshu3011/mdx-renderer",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Reusable MDX visualization library",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,17 +14,20 @@
14
14
  "author": "Amritanshu",
15
15
  "license": "MIT",
16
16
  "peerDependencies": {
17
+ "@mdx-js/react": ">=2.0.0",
17
18
  "react": ">=18.0.0",
18
- "react-dom": ">=18.0.0",
19
- "@mdx-js/react": ">=2.0.0"
19
+ "react-dom": ">=18.0.0"
20
20
  },
21
21
  "dependencies": {
22
- "recharts": "^2.0.0",
23
22
  "echarts": "^5.0.0",
24
- "lucide-react": "^0.562.0"
23
+ "lucide-react": "^0.562.0",
24
+ "recharts": "^2.0.0"
25
25
  },
26
26
  "devDependencies": {
27
+ "@types/node": "^25.0.9",
28
+ "@types/react": "^18.0.0",
27
29
  "typescript": "^5.0.0",
28
- "@types/react": "^18.0.0"
30
+ "vite": "^7.3.1",
31
+ "vite-plugin-dts": "^4.5.4"
29
32
  }
30
33
  }