|
1 | 1 | import React, { useContext, useEffect, useState } from 'react';
|
2 | 2 | import ReactDOM from 'react-dom';
|
3 | 3 | import { observer } from "mobx-react-lite";
|
4 |
| -import { autorun } from "mobx" |
| 4 | +import { reaction } from "mobx" |
5 | 5 | import { GraphicWalker, PureRenderer, GraphicRenderer, TableWalker } from '@kanaries/graphic-walker'
|
6 | 6 | import type { VizSpecStore } from '@kanaries/graphic-walker/store/visualSpecStore'
|
7 | 7 | import type { IGWHandler, IViewField, ISegmentKey, IDarkMode, IChatMessage, IRow } from '@kanaries/graphic-walker/interfaces';
|
8 | 8 | import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
| 9 | +import { Streamlit, withStreamlitConnection } from "streamlit-component-lib" |
9 | 10 |
|
10 | 11 | import Options from './components/options';
|
11 | 12 | import { IAppProps } from './interfaces';
|
12 | 13 |
|
13 | 14 | import { loadDataSource, postDataService, finishDataService, getDatasFromKernelBySql, getDatasFromKernelByPayload } from './dataSource';
|
14 | 15 |
|
15 | 16 | import commonStore from "./store/common";
|
16 |
| -import { initJupyterCommunication, initHttpCommunication } from "./utils/communication"; |
| 17 | +import { initJupyterCommunication, initHttpCommunication, streamlitComponentCallback } from "./utils/communication"; |
17 | 18 | import communicationStore from "./store/communication"
|
18 | 19 | import { setConfig } from './utils/userConfig';
|
19 | 20 | import CodeExportModal from './components/codeExportModal';
|
@@ -41,7 +42,7 @@ import {
|
41 | 42 | ToggleGroup,
|
42 | 43 | ToggleGroupItem,
|
43 | 44 | } from "@/components/ui/toggle-group"
|
44 |
| -import { SunIcon, MoonIcon, DesktopIcon } from "@radix-ui/react-icons" |
| 45 | +import { SunIcon, MoonIcon, DesktopIcon, ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons" |
45 | 46 |
|
46 | 47 | // @ts-ignore
|
47 | 48 | import style from './index.css?inline'
|
@@ -151,9 +152,17 @@ const ExploreApp: React.FC<IAppProps & {initChartFlag: boolean}> = (props) => {
|
151 | 152 | if (prop === "current") {
|
152 | 153 | if (value) {
|
153 | 154 | disposerRef.current?.();
|
154 |
| - disposerRef.current = autorun(() => { |
155 |
| - setIsChanged((value as VizSpecStore).canUndo); |
156 |
| - }); |
| 155 | + const store = value as VizSpecStore; |
| 156 | + disposerRef.current = reaction( |
| 157 | + () => store.currentVis, |
| 158 | + () => { |
| 159 | + setIsChanged((value as VizSpecStore).canUndo); |
| 160 | + streamlitComponentCallback({ |
| 161 | + event: "spec_change", |
| 162 | + data: store.exportCode() |
| 163 | + }); |
| 164 | + }, |
| 165 | + ); |
157 | 166 | }
|
158 | 167 | }
|
159 | 168 | return Reflect.set(target, prop, value);
|
@@ -279,18 +288,45 @@ const ExploreApp: React.FC<IAppProps & {initChartFlag: boolean}> = (props) => {
|
279 | 288 | const PureRednererApp: React.FC<IAppProps> = observer((props) => {
|
280 | 289 | const computationCallback = getComputationCallback(props);
|
281 | 290 | const spec = props.visSpec[0];
|
| 291 | + const [expand, setExpand] = useState(false); |
282 | 292 |
|
283 | 293 | return (
|
284 | 294 | <React.StrictMode>
|
285 |
| - <PureRenderer |
286 |
| - {...props.extraConfig} |
287 |
| - name={spec.name} |
288 |
| - visualConfig={spec.config} |
289 |
| - visualLayout={spec.layout} |
290 |
| - visualState={spec.encodings} |
291 |
| - type='remote' |
292 |
| - computation={computationCallback!} |
293 |
| - /> |
| 295 | + <div className='flex'> |
| 296 | + { |
| 297 | + !expand && (<PureRenderer |
| 298 | + {...props.extraConfig} |
| 299 | + appearance={useContext(darkModeContext)} |
| 300 | + vizThemeConfig={props.themeKey} |
| 301 | + name={spec.name} |
| 302 | + visualConfig={spec.config} |
| 303 | + visualLayout={spec.layout} |
| 304 | + visualState={spec.encodings} |
| 305 | + type='remote' |
| 306 | + computation={computationCallback!} |
| 307 | + />) |
| 308 | + } |
| 309 | + { |
| 310 | + expand && commonStore.isStreamlitComponent && ( |
| 311 | + <div style={{minWidth: "96%"}}> |
| 312 | + <GraphicWalker |
| 313 | + {...props.extraConfig} |
| 314 | + appearance={useContext(darkModeContext)} |
| 315 | + vizThemeConfig={props.themeKey} |
| 316 | + fieldkeyGuard={props.fieldkeyGuard} |
| 317 | + fields={props.rawFields} |
| 318 | + data={props.useKernelCalc ? undefined : props.dataSource} |
| 319 | + computation={computationCallback} |
| 320 | + chart={props.visSpec} |
| 321 | + experimentalFeatures={{ computedField: props.useKernelCalc }} |
| 322 | + defaultConfig={{ config: { timezoneDisplayOffset: 0 } }} |
| 323 | + /> |
| 324 | + </div> |
| 325 | + ) |
| 326 | + } |
| 327 | + { commonStore.isStreamlitComponent && expand && ( <ChevronLeftIcon className='h-6 w-6 cursor-pointer border border-black-600 rounded-full'onClick={() => setExpand(false)}></ChevronLeftIcon> )} |
| 328 | + { commonStore.isStreamlitComponent && !expand && ( <ChevronRightIcon className='h-6 w-6 cursor-pointer border border-black-600 rounded-full'onClick={() => setExpand(true)}></ChevronRightIcon> )} |
| 329 | + </div> |
294 | 330 | </React.StrictMode>
|
295 | 331 | )
|
296 | 332 | });
|
@@ -338,18 +374,14 @@ function GWalkerComponent(props: IAppProps) {
|
338 | 374 | }
|
339 | 375 | }, []);
|
340 | 376 |
|
341 |
| - switch(props.gwMode) { |
342 |
| - case "explore": |
343 |
| - return <ExploreApp {...props} dataSource={dataSource} initChartFlag={initChartFlag} />; |
344 |
| - case "renderer": |
345 |
| - return <PureRednererApp {...props} dataSource={dataSource} />; |
346 |
| - case "filter_renderer": |
347 |
| - return <GraphicRendererApp {...props} dataSource={dataSource} />; |
348 |
| - case "table": |
349 |
| - return <TableWalkerApp {...props} dataSource={dataSource} />; |
350 |
| - default: |
351 |
| - return<ExploreApp {...props} dataSource={dataSource} initChartFlag={initChartFlag} /> |
352 |
| - } |
| 377 | + return ( |
| 378 | + <React.StrictMode> |
| 379 | + { props.gwMode === "explore" && <ExploreApp {...props} dataSource={dataSource} initChartFlag={initChartFlag} /> } |
| 380 | + { props.gwMode === "renderer" && <PureRednererApp {...props} dataSource={dataSource} /> } |
| 381 | + { props.gwMode === "filter_renderer" && <GraphicRendererApp {...props} dataSource={dataSource} /> } |
| 382 | + { props.gwMode === "table" && <TableWalkerApp {...props} dataSource={dataSource} /> } |
| 383 | + </React.StrictMode> |
| 384 | + ) |
353 | 385 | }
|
354 | 386 |
|
355 | 387 | function GWalker(props: IAppProps, id: string) {
|
@@ -478,4 +510,50 @@ function TableWalkerApp(props: IAppProps) {
|
478 | 510 | )
|
479 | 511 | }
|
480 | 512 |
|
481 |
| -export default { GWalker, PreviewApp, ChartPreviewApp } |
| 513 | + |
| 514 | +function SteamlitGWalkerApp(streamlitProps: any) { |
| 515 | + const props = streamlitProps.args as IAppProps; |
| 516 | + const [inited, setInited] = useState(false); |
| 517 | + const container = React.useRef(null); |
| 518 | + props.visSpec = FormatSpec(props.visSpec, props.rawFields); |
| 519 | + |
| 520 | + useEffect(() => { |
| 521 | + commonStore.setIsStreamlitComponent(true); |
| 522 | + initOnHttpCommunication(props).then(() => { |
| 523 | + setInited(true); |
| 524 | + }) |
| 525 | + }, []); |
| 526 | + |
| 527 | + useEffect(() => { |
| 528 | + if (!container.current) return; |
| 529 | + const resizeObserver = new ResizeObserver(() => { |
| 530 | + Streamlit.setFrameHeight((container.current?.clientHeight ?? 0) + 20); |
| 531 | + }) |
| 532 | + resizeObserver.observe(container.current); |
| 533 | + return () => resizeObserver.disconnect(); |
| 534 | + }, [inited]); |
| 535 | + |
| 536 | + return ( |
| 537 | + <React.StrictMode> |
| 538 | + {inited && ( |
| 539 | + <div ref={container}> |
| 540 | + <MainApp darkMode={props.dark}> |
| 541 | + <GWalkerComponent {...props} /> |
| 542 | + </MainApp> |
| 543 | + </div> |
| 544 | + )} |
| 545 | + </React.StrictMode> |
| 546 | + ); |
| 547 | +}; |
| 548 | + |
| 549 | +const StreamlitGWalker = () => { |
| 550 | + const StreamlitGWalker = withStreamlitConnection(SteamlitGWalkerApp); |
| 551 | + ReactDOM.render( |
| 552 | + <React.StrictMode> |
| 553 | + <StreamlitGWalker /> |
| 554 | + </React.StrictMode>, |
| 555 | + document.getElementById("root") |
| 556 | + ) |
| 557 | +} |
| 558 | + |
| 559 | +export default { GWalker, PreviewApp, ChartPreviewApp, StreamlitGWalker } |
0 commit comments