@@ -49,6 +49,7 @@ import { keepPreviousData, QueryClientProvider, useQuery } from "@tanstack/react
49
49
import { FlowClass , FlowEdge , FlowNode , layoutGraph , PreviousLayout , SelectedNode } from "./layout" ;
50
50
import { queryClient } from "./queryClient" ;
51
51
import { Loading } from "./Loading" ;
52
+ import { Slider , SliderOutput , SliderTack } from "./react-aria-components-tailwind-starter/src/slider" ;
52
53
53
54
export function EClassNode ( { data, selected } : NodeProps < FlowClass > ) {
54
55
return (
@@ -392,114 +393,122 @@ function LayoutFlow({
392
393
< >
393
394
{ layoutQuery . isFetching ? < Loading /> : < > </ > }
394
395
< SetSelectedNodeContext . Provider value = { setSelectedNode } >
395
- < Rendering
396
- nodes = { nodes }
397
- edges = { edges }
398
- nodeToEdges = { nodeToEdges }
399
- edgeToNodes = { edgeToNodes }
400
- selectedNode = { selectedNode }
401
- elkJSON = { elkJSON }
402
- useInteractiveLayout = { useInteractiveLayout }
403
- setUseInteractiveLayout = { setUseInteractiveLayout }
404
- mergeEdges = { mergeEdges }
405
- setMergeEdges = { setMergeEdges }
406
- />
396
+ < ReactFlowProvider >
397
+ < Rendering
398
+ nodes = { nodes }
399
+ edges = { edges }
400
+ nodeToEdges = { nodeToEdges }
401
+ edgeToNodes = { edgeToNodes }
402
+ selectedNode = { selectedNode }
403
+ elkJSON = { elkJSON }
404
+ useInteractiveLayout = { useInteractiveLayout }
405
+ setUseInteractiveLayout = { setUseInteractiveLayout }
406
+ mergeEdges = { mergeEdges }
407
+ setMergeEdges = { setMergeEdges }
408
+ />
409
+ </ ReactFlowProvider >
407
410
</ SetSelectedNodeContext . Provider >
408
411
</ >
409
412
) ;
410
413
}
411
414
412
- export function Visualizer ( { egraph, height = null , resize = false } : { egraph : string ; height ?: string | null ; resize ?: boolean } ) {
413
- const [ outerElem , setOuterElem ] = useState < HTMLDivElement | null > ( null ) ;
414
- const [ innerElem , setInnerElem ] = useState < HTMLDivElement | null > ( null ) ;
415
+ function SelectSider ( { length, onSelect, selected } : { length : number ; onSelect : ( index : number ) => void ; selected : number } ) {
416
+ return (
417
+ < div className = { `absolute top-0 left-0 p-4 z-50 backdrop-blur-sm ${ length > 1 ? "" : "opacity-0" } ` } >
418
+ < Slider
419
+ minValue = { 0 }
420
+ maxValue = { length - 1 }
421
+ onChange = { onSelect }
422
+ value = { selected }
423
+ aria-label = "Select which egraph to display from the history"
424
+ >
425
+ < div className = "flex flex-1 items-end" >
426
+ < div className = "flex flex-1 flex-col" >
427
+ < SliderOutput className = "self-center" >
428
+ { ( { state } ) => {
429
+ return (
430
+ < span className = "text-sm" >
431
+ { state . getThumbValueLabel ( 0 ) } / { length - 1 }
432
+ </ span >
433
+ ) ;
434
+ } }
435
+ </ SliderOutput >
436
+ < div className = "flex flex-1 items-center gap-3" >
437
+ < SliderTack thumbLabels = { [ "volume" ] } />
438
+ </ div >
439
+ </ div >
440
+ </ div >
441
+ </ Slider >
442
+ </ div >
443
+ ) ;
444
+ }
415
445
446
+ export function Visualizer ( { egraphs, height = null , resize = false } : { egraphs : string [ ] ; height ?: string | null ; resize ?: boolean } ) {
416
447
const [ rootElem , setRootElem ] = useState < HTMLDivElement | null > ( null ) ;
417
448
418
- const aspectRatio = useMemo ( ( ) => {
419
- if ( rootElem ) {
420
- return rootElem . clientWidth / rootElem . clientHeight ;
421
- }
422
- } , [ rootElem ] ) ;
449
+ const [ outerElem , setOuterElem ] = useState < HTMLDivElement | null > ( null ) ;
450
+ const [ innerElem , setInnerElem ] = useState < HTMLDivElement | null > ( null ) ;
451
+ const aspectRatio = rootElem ? rootElem . clientWidth / rootElem . clientHeight : null ;
452
+
453
+ // If we are at null, then use the last item in the list
454
+ // if the last selection was for a list of egraphs that no longer exists, then use the last item in the list
455
+ const [ selected , setSelected ] = useState < null | { egraphs : string [ ] ; index : number } > ( null ) ;
456
+ const actualSelected = selected && selected . egraphs === egraphs ? selected . index : egraphs . length - 1 ;
457
+ const onSelect = useCallback (
458
+ ( index : number ) => {
459
+ setSelected ( { egraphs, index } ) ;
460
+ } ,
461
+ [ setSelected , egraphs ]
462
+ ) ;
463
+
423
464
return (
424
465
< div className = { `w-full relative ${ resize ? "resize-y" : "" } ` } style = { { height : height || "100%" } } ref = { setRootElem } >
425
466
{ /* Hidden node to measure text size */ }
426
467
< div className = "invisible absolute" >
427
468
< ENode outerRef = { setOuterElem } innerRef = { setInnerElem } />
428
469
</ div >
429
- < ReactFlowProvider >
430
- { outerElem && innerElem && aspectRatio && (
431
- < LayoutFlow aspectRatio = { aspectRatio } egraph = { egraph } outerElem = { outerElem } innerElem = { innerElem } />
432
- ) }
433
- </ ReactFlowProvider >
470
+ < SelectSider length = { egraphs . length } onSelect = { onSelect } selected = { actualSelected } />
471
+ { outerElem && innerElem && aspectRatio && (
472
+ < LayoutFlow aspectRatio = { aspectRatio } egraph = { egraphs [ actualSelected ] } outerElem = { outerElem } innerElem = { innerElem } />
473
+ ) }
434
474
</ div >
435
475
) ;
436
476
}
437
477
438
478
// Put these both in one file, so its emitted as a single chunk and anywidget doesn't have to import another file
439
479
440
- function VisualizerWithTransition ( {
441
- initialEgraph,
442
- registerChangeEGraph,
443
- resize,
444
- height,
445
- } : {
446
- initialEgraph : string ;
447
- registerChangeEGraph : ( setEgraph : ( egraph : string ) => void ) => void ;
448
- resize ?: boolean ;
449
- height ?: string ;
450
- } ) {
451
- const [ egraph , setEgraph ] = useState ( initialEgraph ) ;
452
- useEffect ( ( ) => {
453
- registerChangeEGraph ( setEgraph ) ;
454
- } , [ registerChangeEGraph , setEgraph ] ) ;
455
- return (
456
- < QueryClientProvider client = { queryClient } >
457
- < Visualizer egraph = { egraph } height = { height } resize = { resize } />
458
- </ QueryClientProvider >
459
- ) ;
460
- }
461
-
462
480
/// Render anywidget model to the given element
463
481
// Must be named `render` to work as an anywidget module
464
482
// https://anywidget.dev/en/afm/#lifecycle-methods
465
483
// eslint-disable-next-line react-refresh/only-export-components
466
484
export function render ( { model, el } : { el : HTMLElement ; model : AnyModel } ) {
485
+ // only render once with data, dont support updating widget yet
467
486
const root = createRoot ( el ) ;
468
- let callback : ( ) => void ;
469
- const registerChangeEGraph = ( setEgraph : ( egraph : string ) => void ) => {
470
- callback = ( ) => setEgraph ( model . get ( "egraph" ) ) ;
471
- model . on ( "change:egraph" , callback ) ;
472
- } ;
487
+ // let callback: () => void;
488
+ // const registerChangeEGraph = (setEgraph: (egraph: string) => void) => {
489
+ // callback = () => setEgraph(model.get("egraph"));
490
+ // model.on("change:egraph", callback);
491
+ // };
473
492
root . render (
474
- < VisualizerWithTransition initialEgraph = { model . get ( "egraph" ) } registerChangeEGraph = { registerChangeEGraph } height = "600px" resize />
493
+ < QueryClientProvider client = { queryClient } >
494
+ < Visualizer egraphs = { model . get ( "egraphs" ) } height = "600px" resize />
495
+ </ QueryClientProvider >
475
496
) ;
476
497
477
498
return ( ) => {
478
- model . off ( "change:egraph" , callback ) ;
499
+ // model.off("change:egraph", callback);
479
500
root . unmount ( ) ;
480
501
} ;
481
502
}
482
503
483
504
/// Mount the visualizer to the given element
484
- /// Call `render` to render a new egraph
505
+ /// Call `render` to render a new list of egraphs
485
506
/// Call `unmount` to unmount the visualizer
486
507
// eslint-disable-next-line react-refresh/only-export-components
487
- export function mount ( element : HTMLElement ) : { render : ( egraph : string ) => void ; unmount : ( ) => void } {
508
+ export function mount ( element : HTMLElement ) : { render : ( egraphs : string [ ] ) => void ; unmount : ( ) => void } {
488
509
const root = createRoot ( element ) ;
489
- let setEgraph : null | ( ( egraph : string ) => void ) = null ;
490
- function render ( egraph : string ) {
491
- if ( setEgraph ) {
492
- setEgraph ( egraph ) ;
493
- } else {
494
- root . render (
495
- < VisualizerWithTransition
496
- initialEgraph = { egraph }
497
- registerChangeEGraph = { ( setEgraph_ ) => {
498
- setEgraph = setEgraph_ ;
499
- } }
500
- />
501
- ) ;
502
- }
510
+ function render ( egraphs : string [ ] ) {
511
+ root . render ( < Visualizer egraphs = { egraphs } /> ) ;
503
512
}
504
513
505
514
function unmount ( ) {
0 commit comments