2323from panel .viewable import Viewable , Viewer
2424from panel_material_ui import (
2525 Accordion , BreakpointSwitcher , Button , Card , ChatFeed , ChatMessage ,
26- Container , Dialog , Divider , FileDownload , IconButton , Progress , Select ,
27- SpeedDial , TextAreaInput , TextInput , Typography ,
26+ Container , Dialog , Divider , FileDownload , IconButton , MenuButton , Progress ,
27+ Select , SpeedDial , TextAreaInput , TextInput , Typography ,
2828)
2929
3030from ..views .base import Panel , View
@@ -633,6 +633,18 @@ def to_notebook(self):
633633 cells = make_preamble ("" , extensions = extensions ) + cells
634634 return write_notebook (cells )
635635
636+ def to_html (self ):
637+ """
638+ Returns the HTML representation of the report.
639+ """
640+ if len (self ) and self .status != "success" :
641+ raise RuntimeError (
642+ "Report has not been executed, run report before exporting to html."
643+ )
644+ buf = io .StringIO ()
645+ self ._view .save (buf , title = self .title or "Report" )
646+ return buf .getvalue ()
647+
636648 def validate (
637649 self ,
638650 context : TContext | None = None ,
@@ -869,13 +881,34 @@ def _init_view(self):
869881 icon = "settings" , on_click = self ._open_settings , size = "large" , color = "default" ,
870882 margin = 0 , description = "Configure Report" , visible = False
871883 )
872- self ._export = IconButton (
873- icon = "get_app" , on_click = self ._trigger_download , size = "large" , color = "default" ,
874- margin = 0 , description = "Export Report to .ipynb" , visible = False
884+ self ._export = MenuButton (
885+ label = "" , icon = "get_app" , variant = "text" , color = "default" ,
886+ margin = 0 , size = "large" , visible = False ,
887+ description = "Export Report" ,
888+ items = [
889+ {"label" : "Notebook (.ipynb)" , "format" : "ipynb" , "icon" : "description" },
890+ {"label" : "HTML (.html)" , "format" : "html" , "icon" : "language" },
891+ ],
892+ on_click = lambda _ : self ._download ._transfer (),
893+ icon_size = "36px" ,
894+ sx = {
895+ "& .MuiButton-endIcon" : {"display" : "none" },
896+ "& .MuiButton-startIcon" : {"margin" : 0 },
897+ "& .MuiIcon-root" : {"color" : "var(--mui-palette-action-active)" },
898+ "minWidth" : "unset" ,
899+ "width" : "60px" ,
900+ "height" : "60px" ,
901+ "borderRadius" : "50%" ,
902+ "padding" : 0 ,
903+ },
875904 )
876905 self ._download = FileDownload (
877- callback = self ._notebook_export , filename = f"{ self .title or 'Report' } .ipynb" , visible = False
906+ auto = True ,
907+ callback = param .bind (self ._export_report , self ._export ),
908+ filename = f"{ self .title or 'Report' } .ipynb" ,
909+ visible = False ,
878910 )
911+ self ._export .attached .append (self ._download )
879912 self ._dialog = Dialog (
880913 TextInput .from_param (self .param .title , margin = (10 , 0 , 0 , 0 ), sizing_mode = "stretch_width" ),
881914 show_close_button = True ,
@@ -889,14 +922,14 @@ def _init_view(self):
889922 self ._collapse ,
890923 self ._export ,
891924 self ._settings ,
892- self ._download ,
893925 sizing_mode = "stretch_width"
894926 )
895927 self ._dial = SpeedDial (
896928 items = [
897929 {"label" : "Execute Report" , "icon" : "play_arrow" },
898930 {"label" : "Clear Report" , "icon" : "clear" },
899- {"label" : "Export Report to Notebook" , "icon" : "get_app" },
931+ {"label" : "Export as Notebook" , "icon" : "description" , "format" : "ipynb" },
932+ {"label" : "Export as HTML" , "icon" : "language" , "format" : "html" },
900933 {"label" : "Configure Report" , "icon" : "settings" }
901934 ],
902935 color = "default" ,
@@ -934,15 +967,13 @@ async def _trigger_event(self, item: dict):
934967 await self ._execute_event ()
935968 elif icon == "clear" :
936969 self .reset ()
937- elif icon == "get_app" :
938- self ._trigger_download ()
970+ elif icon in ("description" , "language" ):
971+ fmt = item .get ("format" , "ipynb" )
972+ self ._export .value = {"format" : fmt }
973+ self ._download ._transfer ()
939974 elif icon == "settings" :
940975 self ._open_settings ()
941976
942- def _trigger_download (self , event = None ):
943- self ._download .filename = f"{ self .title or 'Report' } .ipynb"
944- self ._download .transfer ()
945-
946977 @param .depends ('_current' , '_tasks' , watch = True )
947978 def _update_run_state (self ):
948979 # Play button is always enabled - users can re-run after changing settings
@@ -972,9 +1003,15 @@ async def _execute_event(self, event=None):
9721003 await asyncio .sleep (0.01 ) # yield the event loop to allow button loading state to update
9731004 await self .execute ()
9741005
975- async def _notebook_export (self ):
1006+ async def _export_report (self , item = None ):
9761007 if len (self ) and self .status != "success" :
9771008 await self .execute ()
1009+ fmt = item .get ("format" , "ipynb" ) if isinstance (item , dict ) else "ipynb"
1010+ title = self .title or "Report"
1011+ ext = "html" if fmt == "html" else "ipynb"
1012+ self ._download .filename = f"{ title } .{ ext } "
1013+ if fmt == "html" :
1014+ return io .StringIO (self .to_html ())
9781015 return io .StringIO (self .to_notebook ())
9791016
9801017 def _expand_all (self , event = None ):
0 commit comments