Skip to content

Commit

Permalink
wip: event
Browse files Browse the repository at this point in the history
  • Loading branch information
lvisei committed Dec 17, 2024
1 parent 77e60a7 commit fb03a76
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 48 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"@ant-design/graphs": "^2.0.2",
"@ant-design/icons": "^5.5.2",
"@ant-design/plots": "^2.3.2",
"@antv/event-emitter": "^0.1.3",
"@antv/l7": "^2.22.3",
"@antv/larkmap": "^1.5.1",
"@babel/runtime": "^7.26.0",
Expand Down
53 changes: 36 additions & 17 deletions src/GPTVis/Lite.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { memo } from 'react';
import EventEmitter from '@antv/event-emitter';
import React, { memo, useEffect, useMemo } from 'react';
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';
import { GPTVisContext } from './hooks/useContext';
import { useEventPublish } from './hooks/useEvent';

export interface GPTVisLiteProps extends Options {
/**
Expand All @@ -15,34 +17,51 @@ export interface GPTVisLiteProps extends Options {
[key: string]: (props: any) => React.ReactNode;
};
/**
* 🧪 组件上下文数据,实验性属性
* 🧪 订阅组件事件,实验性属性
* 用于子组件与容器组件通信
*/
context?: Record<string, any>;
eventSubscribe?: Record<string, (data?: any) => void>;
}

const GPTVisLite: React.FC<GPTVisLiteProps> = ({
context,
children,
components,
rehypePlugins,
remarkPlugins,
eventSubscribe: onEvent,
...rest
}) => {
const renderer = (
<Markdown
components={components}
rehypePlugins={[rehypeRaw, ...(rehypePlugins ? rehypePlugins : [])]}
remarkPlugins={[remarkGfm, ...(remarkPlugins ? remarkPlugins : [])]}
{...rest}
>
{children}
</Markdown>
);
const eventBus = useMemo(() => new EventEmitter(), []);
const contextValue = useMemo(() => ({ eventBus }), [eventBus]);

useEffect(() => {
if (onEvent) {
const events = Object.keys(onEvent);
for (const eventName of events) {
eventBus.on(eventName, onEvent[eventName]);
}
return () => {
for (const eventName of events) {
eventBus.off(eventName, onEvent[eventName]);
}
};
}
}, [eventBus, onEvent]);

return context ? <GPTVisContext.Provider value={context} children={renderer} /> : renderer;
return (
<GPTVisContext.Provider value={contextValue}>
<Markdown
components={components}
rehypePlugins={[rehypeRaw, ...(rehypePlugins ? rehypePlugins : [])]}
remarkPlugins={[remarkGfm, ...(remarkPlugins ? remarkPlugins : [])]}
{...rest}
>
{children}
</Markdown>
</GPTVisContext.Provider>
);
};

export { GPTVisContext, useGPTVisContext };
export { useEventPublish };

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

export const MyContext = React.createContext(null as any);

export function useMyContext() {
const context = React.useContext(MyContext);
if (context === undefined || Object.keys(context).length === 0) {
throw new Error(`useMyContext must be used within a MyContext.Provider`);
}

return context;
}

/**
* 自定义代码块渲染器
*/
const MyUIRenderer: CodeBlockComponent = ({ children }) => {
const context = useGPTVisContext();
const context = useMyContext();
console.log('context: ', context);
return (
<div style={{ backgroundColor: '#f0f0f0', padding: '10px' }}>
Expand All @@ -29,6 +40,7 @@ const content = `
my ui data ...
\`\`\`
`;

export default () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
Expand All @@ -41,15 +53,15 @@ export default () => {
return (
<>
<p>count: {count}</p>
<GPTVisContext.Provider value={context}>
<MyContext.Provider value={context}>
<div>
{/* other component ... */}
<div>
{/* other component ... */}
<GPTVisLite components={components}>{content}</GPTVisLite>
</div>
</div>
</GPTVisContext.Provider>
</MyContext.Provider>
</>
);
};
32 changes: 16 additions & 16 deletions src/GPTVis/demos/context.tsx → src/GPTVis/demos/event.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import type { CodeBlockComponent } from '@antv/gpt-vis';
import { GPTVisLite, useGPTVisContext, withChartCode } from '@antv/gpt-vis';
import React, { useCallback, useMemo, useState } from 'react';
import { GPTVisLite, useEventPublish, withChartCode } from '@antv/gpt-vis';
import React from 'react';

/**
* 自定义代码块渲染器
*/
const MyUIRenderer: CodeBlockComponent = ({ children }) => {
const context = useGPTVisContext();
console.log('context: ', context);
const dispatch = useEventPublish();
console.log('dispatch: ', dispatch);
return (
<div style={{ backgroundColor: '#f0f0f0', padding: '10px' }}>
<p>{children}</p>
<button onClick={context?.onClick} type="button">
<button
onClick={() => {
dispatch('click', {});
}}
type="button"
>
click
</button>
</div>
Expand All @@ -30,20 +35,15 @@ my ui data ...
\`\`\`
`;
export default () => {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
const onClick = (data: any) => {
console.log('data: ', data);
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>
</>
<GPTVisLite eventSubscribe={{ click: onClick }} components={components}>
{content}
</GPTVisLite>
);
};
7 changes: 5 additions & 2 deletions src/GPTVis/useContext.ts → src/GPTVis/hooks/useContext.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type EventEmitter from '@antv/event-emitter';
import React from 'react';

type GPTVisContextValue = Record<string, any>;
type GPTVisContextValue = {
eventBus: EventEmitter;
};

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

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

Expand Down
7 changes: 7 additions & 0 deletions src/GPTVis/hooks/useEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { useGPTVisContext } from './useContext';

export const useEventPublish = () => {
const { eventBus } = useGPTVisContext();

return eventBus.emit.bind(eventBus);
};
6 changes: 3 additions & 3 deletions src/GPTVis/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ GPTVis 协议的 Markdown 渲染器,基于 Markdown 语法扩展 `vis-chart`

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

<!-- ## 容器组件通信
## 容器组件通信

传递组件上下文数据,用于子组件与容器组件通信

<code src="./demos/context"></code>
<code src="./demos/context-provider"></code> -->
<code src="./demos/event"></code>
<code src="./demos/context-provider"></code>

## API

Expand Down
7 changes: 1 addition & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ 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 {
GPTVisContext,
default as GPTVisLite,
useGPTVisContext,
type GPTVisLiteProps,
} from './GPTVis/Lite';
export { default as GPTVisLite, useEventPublish, type GPTVisLiteProps } from './GPTVis/Lite';

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

0 comments on commit fb03a76

Please sign in to comment.