11import os
2- import streamlit . components . v1 as components
2+
33import streamlit as st
4+ import streamlit .components .v1 as components
5+ from streamlit .components .v1 .custom_component import MarshallComponentException
46
57_RELEASE = False
68
1416 build_dir = os .path .join (parent_dir , "frontend/build" )
1517 _component_func = components .declare_component ("streamlit_condition_tree" , path = build_dir )
1618
17-
1819type_mapper = {
1920 'b' : 'boolean' ,
2021 'i' : 'number' ,
3031}
3132
3233
34+ # stole from https://github.com/andfanilo/streamlit-echarts/blob/master/streamlit_echarts/frontend/src/utils.js
35+ # Thanks andfanilo
36+ class JsCode :
37+ def __init__ (self , js_code : str ):
38+ """Wrapper around a js function to be injected on gridOptions.
39+ code is not checked at all.
40+ set allow_unsafe_jscode=True on AgGrid call to use it.
41+ Code is rebuilt on client using new Function Syntax (https://javascript.info/new-function)
42+
43+ Args:
44+ js_code (str): javascript function code as str
45+ """
46+ import re
47+ match_js_comment_expression = r"\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$"
48+ js_code = re .sub (re .compile (match_js_comment_expression , re .MULTILINE ), r"\1" , js_code )
49+
50+ match_js_spaces = r"\s+(?=(?:[^\'\"]*[\'\"][^\'\"]*[\'\"])*[^\'\"]*$)"
51+ one_line_jscode = re .sub (match_js_spaces , " " , js_code , flags = re .MULTILINE )
52+
53+ js_placeholder = "::JSCODE::"
54+ one_line_jscode = re .sub (r"\s+|\r\s*|\n+" , " " , js_code , flags = re .MULTILINE )
55+
56+ self .js_code = f"{ js_placeholder } { one_line_jscode } { js_placeholder } "
57+
58+
59+ # Stole from https://github.com/PablocFonseca/streamlit-aggrid/blob/main/st_aggrid/shared.py
60+ # Thanks PablocFonseca
61+ def walk_config (config , func ):
62+ """Recursively walk config applying func at each leaf node
63+
64+ Args:
65+ config (dict): config dictionary
66+ func (callable): a function to apply at leaf nodes
67+ """
68+ from collections .abc import Mapping
69+
70+ if isinstance (config , (Mapping , list )):
71+ for i , k in enumerate (config ):
72+
73+ if isinstance (config [k ], Mapping ):
74+ walk_config (config [k ], func )
75+ elif isinstance (config [k ], list ):
76+ for j in config [k ]:
77+ walk_config (j , func )
78+ else :
79+ config [k ] = func (config [k ])
80+
81+
3382def config_from_dataframe (dataframe ):
3483 """Return a basic configuration from dataframe columns"""
3584
@@ -58,7 +107,8 @@ def condition_tree(config: dict,
58107 min_height : int = 400 ,
59108 placeholder : str = '' ,
60109 always_show_buttons : bool = False ,
61- key : str = None ):
110+ key : str = None ,
111+ allow_unsafe_jscode : bool = False , ):
62112 """Create a new instance of condition_tree.
63113
64114 Parameters
@@ -88,6 +138,9 @@ def condition_tree(config: dict,
88138 None, and the component's arguments are changed, the component will
89139 be re-mounted in the Streamlit frontend and lose its current state.
90140 Can also be used to access the condition tree through st.session_state.
141+ allow_unsafe_jscode: bool
142+ Allows jsCode to be injected in gridOptions.
143+ Defaults to False.
91144
92145 Returns
93146 -------
@@ -97,7 +150,7 @@ def condition_tree(config: dict,
97150 """
98151
99152 if return_type == 'queryString' :
100- # Add backticks to fields with space in their name
153+ # Add backticks to fields having spaces in their name
101154 fields = {}
102155 for field_name , field_config in config ['fields' ].items ():
103156 if ' ' in field_name :
@@ -106,19 +159,31 @@ def condition_tree(config: dict,
106159
107160 config ['fields' ] = fields
108161
109- output_tree , component_value = _component_func (
110- config = config ,
111- return_type = return_type ,
112- tree = tree ,
113- key = '_' + key if key else None ,
114- min_height = min_height ,
115- placeholder = placeholder ,
116- always_show_buttons = always_show_buttons ,
117- default = ['' , '' ]
118- )
162+ if allow_unsafe_jscode :
163+ walk_config (config , lambda v : v .js_code if isinstance (v , JsCode ) else v )
164+
165+ try :
166+ output_tree , component_value = _component_func (
167+ config = config ,
168+ return_type = return_type ,
169+ tree = tree ,
170+ key = '_' + key if key else None ,
171+ min_height = min_height ,
172+ placeholder = placeholder ,
173+ always_show_buttons = always_show_buttons ,
174+ default = ['' , '' ],
175+ allow_unsafe_jscode = allow_unsafe_jscode ,
176+ )
177+
178+ except MarshallComponentException as e :
179+ # Uses a more complete error message.
180+ args = list (e .args )
181+ args [0 ] += ". If you're using custom JsCode objects on config, ensure that allow_unsafe_jscode is True."
182+ e = MarshallComponentException (* args )
183+ raise (e )
119184
120185 if return_type == 'queryString' and not component_value :
121- # Default string that returns all the values in DataFrame.query
186+ # Default string that applies no filter in DataFrame.query
122187 component_value = 'index in index'
123188
124189 st .session_state [key ] = output_tree
0 commit comments