Skip to content

Commit

Permalink
feat: add context for data communication
Browse files Browse the repository at this point in the history
  • Loading branch information
lvisei committed Dec 13, 2024
1 parent 3a20fc2 commit 88f2aa2
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 3 deletions.
17 changes: 15 additions & 2 deletions src/GPTVis/Lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,33 @@ import type { Options } from 'react-markdown';
import Markdown from 'react-markdown';
import rehypeRaw from 'rehype-raw';
import remarkGfm from 'remark-gfm';
import { GPTVisContext, useGPTVisContext } from './useContext';

export interface GPTVisLiteProps extends Options {
/** 自定义 markdown components样式 */
/**
* 自定义 markdown components
*/
components?:
| Options['components']
| {
[key: string]: (props: any) => React.ReactNode;
};
/**
* 🧪 组件上下文数据,实验性属性
* 用于子组件与容器组件通信
*/
context?: Record<string, any>;
}

const GPTVisLite: React.FC<GPTVisLiteProps> = ({
context,
children,
components,
rehypePlugins,
remarkPlugins,
...rest
}) => {
return (
const renderer = (
<Markdown
components={components}
rehypePlugins={[rehypeRaw, ...(rehypePlugins ? rehypePlugins : [])]}
Expand All @@ -30,6 +39,10 @@ const GPTVisLite: React.FC<GPTVisLiteProps> = ({
{children}
</Markdown>
);

return context ? <GPTVisContext.Provider value={context} children={renderer} /> : renderer;
};

export { GPTVisContext, useGPTVisContext };

export default memo(GPTVisLite);
55 changes: 55 additions & 0 deletions src/GPTVis/demos/context-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { CodeBlockComponent } from '@antv/gpt-vis';
import { GPTVisContext, GPTVisLite, useGPTVisContext, withChartCode } from '@antv/gpt-vis';
import React, { useCallback, useMemo, useState } from 'react';

/**
* 自定义代码块渲染器
*/
const MyUIRenderer: CodeBlockComponent = ({ children }) => {
const context = useGPTVisContext();
console.log('context: ', context);
return (
<div style={{ backgroundColor: '#f0f0f0', padding: '10px' }}>
<p>{children}</p>
<button onClick={context?.onClick} type="button">
click
</button>
</div>
);
};
const customRenderers = { 'my-ui': MyUIRenderer };
const components = {
code: withChartCode({
languageRenderers: customRenderers, // register custom block renderer
}),
};

const content = `
\`\`\`my-ui
my ui data ...
\`\`\`
`;
export default () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('handleClick');
setCount((pre) => pre + 1);
// do something
}, []);
const context = useMemo(() => ({ count: count, onClick: handleClick }), [count]);

return (
<>
<p>count: {count}</p>
<GPTVisContext.Provider value={context}>
<div>
{/* other component ... */}
<div>
{/* other component ... */}
<GPTVisLite components={components}>{content}</GPTVisLite>
</div>
</div>
</GPTVisContext.Provider>
</>
);
};
49 changes: 49 additions & 0 deletions src/GPTVis/demos/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { CodeBlockComponent } from '@antv/gpt-vis';
import { GPTVisLite, useGPTVisContext, withChartCode } from '@antv/gpt-vis';
import React, { useCallback, useMemo, useState } from 'react';

/**
* 自定义代码块渲染器
*/
const MyUIRenderer: CodeBlockComponent = ({ children }) => {
const context = useGPTVisContext();
console.log('context: ', context);
return (
<div style={{ backgroundColor: '#f0f0f0', padding: '10px' }}>
<p>{children}</p>
<button onClick={context?.onClick} type="button">
click
</button>
</div>
);
};
const customRenderers = { 'my-ui': MyUIRenderer };
const components = {
code: withChartCode({
languageRenderers: customRenderers, // register custom block renderer
}),
};

const content = `
\`\`\`my-ui
my ui data ...
\`\`\`
`;
export default () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('handleClick');
setCount((pre) => pre + 1);
// do something
}, []);
const context = useMemo(() => ({ count: count, onClick: handleClick }), [count]);

return (
<>
<p>count: {count}</p>
<GPTVisLite context={context} components={components}>
{content}
</GPTVisLite>
</>
);
};
7 changes: 7 additions & 0 deletions src/GPTVis/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ GPTVis 协议的 Markdown 渲染器,基于 Markdown 语法扩展 `vis-chart`

<code src="./demos/code"></code>

<!-- ## 容器组件通信
传递组件上下文数据,用于子组件与容器组件通信
<code src="./demos/context"></code>
<code src="./demos/context-provider"></code> -->

## API

继承 [react-markdown](https://github.com/remarkjs/react-markdown#options) 组件全部属性。
14 changes: 14 additions & 0 deletions src/GPTVis/useContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';

type GPTVisContextValue = Record<string, any>;

export const GPTVisContext = React.createContext<GPTVisContextValue>(null as any);

export function useGPTVisContext<T = GPTVisContextValue>() {
const context = React.useContext(GPTVisContext);
if (context === null) {
throw new Error(`useGPTVisContext must be used within a GPTVisContext.Provider`);
}

return context as T;
}
7 changes: 6 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ export { withChartCode, withDefaultChartCode } from './ChartCodeRender';
export type { CodeBlockComponent, WithChartCodeOptions } from './ChartCodeRender/type';
export { default as ConfigProvider, type ConfigProviderProps } from './ConfigProvider';
export { default as GPTVis, type GPTVisProps } from './GPTVis';
export { default as GPTVisLite, type GPTVisLiteProps } from './GPTVis/Lite';
export {
GPTVisContext,
default as GPTVisLite,
useGPTVisContext,
type GPTVisLiteProps,
} from './GPTVis/Lite';

export { default as version } from './version';

0 comments on commit 88f2aa2

Please sign in to comment.