@@ -49,6 +49,7 @@ import { keepPreviousData, QueryClientProvider, useQuery } from "@tanstack/react
4949import { FlowClass , FlowEdge , FlowNode , layoutGraph , PreviousLayout , SelectedNode } from "./layout" ;
5050import { queryClient } from "./queryClient" ;
5151import { Loading } from "./Loading" ;
52+ import { Slider , SliderOutput , SliderTack } from "./react-aria-components-tailwind-starter/src/slider" ;
5253
5354export function EClassNode ( { data, selected } : NodeProps < FlowClass > ) {
5455 return (
@@ -392,114 +393,122 @@ function LayoutFlow({
392393 < >
393394 { layoutQuery . isFetching ? < Loading /> : < > </ > }
394395 < 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 >
407410 </ SetSelectedNodeContext . Provider >
408411 </ >
409412 ) ;
410413}
411414
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+ }
415445
446+ export function Visualizer ( { egraphs, height = null , resize = false } : { egraphs : string [ ] ; height ?: string | null ; resize ?: boolean } ) {
416447 const [ rootElem , setRootElem ] = useState < HTMLDivElement | null > ( null ) ;
417448
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+
423464 return (
424465 < div className = { `w-full relative ${ resize ? "resize-y" : "" } ` } style = { { height : height || "100%" } } ref = { setRootElem } >
425466 { /* Hidden node to measure text size */ }
426467 < div className = "invisible absolute" >
427468 < ENode outerRef = { setOuterElem } innerRef = { setInnerElem } />
428469 </ 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+ ) }
434474 </ div >
435475 ) ;
436476}
437477
438478// Put these both in one file, so its emitted as a single chunk and anywidget doesn't have to import another file
439479
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-
462480/// Render anywidget model to the given element
463481// Must be named `render` to work as an anywidget module
464482// https://anywidget.dev/en/afm/#lifecycle-methods
465483// eslint-disable-next-line react-refresh/only-export-components
466484export function render ( { model, el } : { el : HTMLElement ; model : AnyModel } ) {
485+ // only render once with data, dont support updating widget yet
467486 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+ // };
473492 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 >
475496 ) ;
476497
477498 return ( ) => {
478- model . off ( "change:egraph" , callback ) ;
499+ // model.off("change:egraph", callback);
479500 root . unmount ( ) ;
480501 } ;
481502}
482503
483504/// 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
485506/// Call `unmount` to unmount the visualizer
486507// 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 } {
488509 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 } /> ) ;
503512 }
504513
505514 function unmount ( ) {
0 commit comments