1
1
import os
2
- import streamlit . components . v1 as components
2
+
3
3
import streamlit as st
4
+ import streamlit .components .v1 as components
5
+ from streamlit .components .v1 .custom_component import MarshallComponentException
4
6
5
7
_RELEASE = False
6
8
14
16
build_dir = os .path .join (parent_dir , "frontend/build" )
15
17
_component_func = components .declare_component ("streamlit_condition_tree" , path = build_dir )
16
18
17
-
18
19
type_mapper = {
19
20
'b' : 'boolean' ,
20
21
'i' : 'number' ,
30
31
}
31
32
32
33
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
+
33
82
def config_from_dataframe (dataframe ):
34
83
"""Return a basic configuration from dataframe columns"""
35
84
@@ -58,7 +107,8 @@ def condition_tree(config: dict,
58
107
min_height : int = 400 ,
59
108
placeholder : str = '' ,
60
109
always_show_buttons : bool = False ,
61
- key : str = None ):
110
+ key : str = None ,
111
+ allow_unsafe_jscode : bool = False , ):
62
112
"""Create a new instance of condition_tree.
63
113
64
114
Parameters
@@ -88,6 +138,9 @@ def condition_tree(config: dict,
88
138
None, and the component's arguments are changed, the component will
89
139
be re-mounted in the Streamlit frontend and lose its current state.
90
140
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.
91
144
92
145
Returns
93
146
-------
@@ -97,7 +150,7 @@ def condition_tree(config: dict,
97
150
"""
98
151
99
152
if return_type == 'queryString' :
100
- # Add backticks to fields with space in their name
153
+ # Add backticks to fields having spaces in their name
101
154
fields = {}
102
155
for field_name , field_config in config ['fields' ].items ():
103
156
if ' ' in field_name :
@@ -106,19 +159,31 @@ def condition_tree(config: dict,
106
159
107
160
config ['fields' ] = fields
108
161
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 )
119
184
120
185
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
122
187
component_value = 'index in index'
123
188
124
189
st .session_state [key ] = output_tree
0 commit comments