steer-core 0.1.9__tar.gz → 0.1.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {steer_core-0.1.9 → steer_core-0.1.10}/PKG-INFO +1 -1
- steer_core-0.1.10/steer_core/Apps/Components/MaterialSelectors.py +694 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Components/SliderComponents.py +17 -3
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Utils/SliderControls.py +38 -17
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Data/database.db +0 -0
- steer_core-0.1.10/steer_core/__init__.py +1 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core.egg-info/PKG-INFO +1 -1
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core.egg-info/SOURCES.txt +1 -1
- steer_core-0.1.9/steer_core/Apps/Components/CompoundComponents.py +0 -0
- steer_core-0.1.9/steer_core/__init__.py +0 -1
- {steer_core-0.1.9 → steer_core-0.1.10}/README.md +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/setup.cfg +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/setup.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Components/RangeSliderComponents.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Components/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/ContextManagers.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Performance/CallbackTimer.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Performance/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/Utils/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Apps/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Constants/Units.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Constants/Universal.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Constants/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/ContextManagers/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Data/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/DataManager.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Decorators/Coordinates.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Decorators/Electrochemical.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Decorators/General.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Decorators/Objects.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Decorators/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/Colors.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/Coordinates.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/Data.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/Serializer.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/Validators.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core/Mixins/__init__.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core.egg-info/dependency_links.txt +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core.egg-info/requires.txt +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/steer_core.egg-info/top_level.txt +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/test/test_compound_components.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/test/test_compound_components_clean.py +0 -0
- {steer_core-0.1.9 → steer_core-0.1.10}/test/test_slider_controls.py +0 -0
@@ -0,0 +1,694 @@
|
|
1
|
+
import dash as ds
|
2
|
+
from dash import Input, Output, dcc, html
|
3
|
+
from typing import Union, List, Dict
|
4
|
+
from .SliderComponents import SliderWithTextInput
|
5
|
+
|
6
|
+
|
7
|
+
class MaterialSelector:
|
8
|
+
"""
|
9
|
+
A custom Dash component that combines material selection controls in a horizontal layout.
|
10
|
+
|
11
|
+
This component creates a horizontal row containing:
|
12
|
+
- A dropdown menu for selecting material names
|
13
|
+
- An input box for specifying weight fraction of the material
|
14
|
+
- Two SliderWithTextInput components for specific cost and density
|
15
|
+
|
16
|
+
The component is designed for material composition interfaces where users need to
|
17
|
+
select materials and specify their properties and proportions.
|
18
|
+
|
19
|
+
Attributes:
|
20
|
+
id_base (dict): Base identifier dictionary used to construct unique IDs for child components
|
21
|
+
material_options (List[Dict]): List of material options for the dropdown
|
22
|
+
default_material (str): Default selected material name
|
23
|
+
default_weight_fraction (float): Default weight fraction value
|
24
|
+
cost_config (dict): Configuration for the cost slider (min_val, max_val, step, etc.)
|
25
|
+
density_config (dict): Configuration for the density slider (min_val, max_val, step, etc.)
|
26
|
+
property_name (str): Property identifier used in component ID construction
|
27
|
+
title (str): Display title for the component
|
28
|
+
slider_disable (bool): Whether the sliders should be disabled
|
29
|
+
dropdown_id (dict): Computed ID for the dropdown component
|
30
|
+
weight_fraction_id (dict): Computed ID for the weight fraction input
|
31
|
+
cost_slider (SliderWithTextInput): Cost slider component
|
32
|
+
density_slider (SliderWithTextInput): Density slider component
|
33
|
+
|
34
|
+
Example:
|
35
|
+
>>> material_selector = MaterialSelector(
|
36
|
+
... id_base={'type': 'material', 'index': 0},
|
37
|
+
... material_options=[
|
38
|
+
... {'label': 'Aluminum', 'value': 'aluminum'},
|
39
|
+
... {'label': 'Steel', 'value': 'steel'},
|
40
|
+
... {'label': 'Carbon Fiber', 'value': 'carbon_fiber'}
|
41
|
+
... ],
|
42
|
+
... default_material='aluminum',
|
43
|
+
... default_weight_fraction=0.5,
|
44
|
+
... cost_config={
|
45
|
+
... 'min_val': 0.0,
|
46
|
+
... 'max_val': 100.0,
|
47
|
+
... 'step': 0.1,
|
48
|
+
... 'mark_interval': 10.0,
|
49
|
+
... 'default_val': 25.0,
|
50
|
+
... 'title': 'Cost ($/kg)'
|
51
|
+
... },
|
52
|
+
... density_config={
|
53
|
+
... 'min_val': 0.0,
|
54
|
+
... 'max_val': 10.0,
|
55
|
+
... 'step': 0.01,
|
56
|
+
... 'mark_interval': 1.0,
|
57
|
+
... 'default_val': 2.7,
|
58
|
+
... 'title': 'Density (g/cm³)'
|
59
|
+
... },
|
60
|
+
... property_name='material_1',
|
61
|
+
... title='Material 1'
|
62
|
+
... )
|
63
|
+
>>> layout_element = material_selector() # Returns Dash HTML Div component
|
64
|
+
"""
|
65
|
+
|
66
|
+
def __init__(
|
67
|
+
self,
|
68
|
+
id_base: dict,
|
69
|
+
material_options: List[Dict] = None,
|
70
|
+
slider_configs: dict = None,
|
71
|
+
cost_config: dict = None,
|
72
|
+
density_config: dict = None,
|
73
|
+
title: str = "Material",
|
74
|
+
default_material: str = None,
|
75
|
+
default_weight_percent: float = 0,
|
76
|
+
slider_disable: bool = False,
|
77
|
+
div_width: str = '100%',
|
78
|
+
hidden: bool = False,
|
79
|
+
):
|
80
|
+
"""
|
81
|
+
Initialize the MaterialSelector component.
|
82
|
+
|
83
|
+
Args:
|
84
|
+
id_base (dict): Base dictionary for generating component IDs.
|
85
|
+
material_options (List[Dict], optional): List of material options for dropdown.
|
86
|
+
Each dict should have 'label' and 'value' keys.
|
87
|
+
If None, defaults to empty list (no options).
|
88
|
+
slider_configs (dict, optional): Output from create_slider_config with arrays where:
|
89
|
+
- Index 0 = density configuration
|
90
|
+
- Index 1 = cost configuration
|
91
|
+
If provided, cost_config and density_config are ignored.
|
92
|
+
If empty dictionary {}, uses sensible defaults with preset values.
|
93
|
+
If None (default), uses sensible defaults with no initial values.
|
94
|
+
cost_config (dict, optional): Legacy cost slider configuration. Ignored if slider_config provided.
|
95
|
+
density_config (dict, optional): Legacy density slider configuration. Ignored if slider_config provided.
|
96
|
+
title (str, optional): Title displayed for the component. Defaults to "Material".
|
97
|
+
default_material (str, optional): Default selected material. If None, no material
|
98
|
+
will be initially selected in the dropdown.
|
99
|
+
default_weight_percent (float, optional): Default weight percentage (0.0-100.0). Defaults to 100.0.
|
100
|
+
slider_disable (bool, optional): Whether to disable sliders. Defaults to False.
|
101
|
+
div_width (str, optional): CSS width specification for the container div.
|
102
|
+
Defaults to '100%'.
|
103
|
+
hidden (bool, optional): Whether to hide the entire component. When True,
|
104
|
+
the component will be rendered with display: none.
|
105
|
+
Defaults to False.
|
106
|
+
"""
|
107
|
+
self.id_base = id_base
|
108
|
+
self.material_options = material_options or [] # Default to empty list if None
|
109
|
+
self.default_material = default_material # Keep as None if not provided
|
110
|
+
self.default_weight_percent = default_weight_percent
|
111
|
+
self.cost_config = cost_config
|
112
|
+
self.density_config = density_config
|
113
|
+
self.title = title
|
114
|
+
self.slider_disable = slider_disable
|
115
|
+
self.div_width = div_width
|
116
|
+
self.hidden = hidden
|
117
|
+
|
118
|
+
# Generate component IDs
|
119
|
+
self.dropdown_id = self._make_id('dropdown')
|
120
|
+
self.weight_fraction_id = self._make_id('weight_fraction')
|
121
|
+
|
122
|
+
# Normalize configurations to handle both legacy and create_slider_config formats
|
123
|
+
if slider_configs is not None and slider_configs: # Check if not empty
|
124
|
+
# Use slider_configs arrays (index 0 = density, index 1 = cost)
|
125
|
+
density_normalized = self._normalize_config_from_arrays(slider_configs, 0, 'Density')
|
126
|
+
cost_normalized = self._normalize_config_from_arrays(slider_configs, 1, 'Cost')
|
127
|
+
elif slider_configs is not None and not slider_configs: # Empty dictionary
|
128
|
+
# Use sensible defaults for materials
|
129
|
+
density_normalized = {
|
130
|
+
'min_val': 0.0,
|
131
|
+
'max_val': 0.1,
|
132
|
+
'step': 0.01,
|
133
|
+
'mark_interval': 0.1,
|
134
|
+
'title': 'Density',
|
135
|
+
'default_val': 0.05
|
136
|
+
}
|
137
|
+
cost_normalized = {
|
138
|
+
'min_val': 0.0,
|
139
|
+
'max_val': 0.1,
|
140
|
+
'step': 0.01,
|
141
|
+
'mark_interval': 0.1,
|
142
|
+
'title': 'Cost',
|
143
|
+
'default_val': 0.05
|
144
|
+
}
|
145
|
+
else:
|
146
|
+
# slider_configs is None - use sensible defaults with None values
|
147
|
+
density_normalized = {
|
148
|
+
'min_val': 0.0,
|
149
|
+
'max_val': 0.1,
|
150
|
+
'step': 0.01,
|
151
|
+
'mark_interval': 0.1,
|
152
|
+
'title': 'Density',
|
153
|
+
'default_val': None # No initial value
|
154
|
+
}
|
155
|
+
cost_normalized = {
|
156
|
+
'min_val': 0.0,
|
157
|
+
'max_val': 0.1,
|
158
|
+
'step': 0.01,
|
159
|
+
'mark_interval': 0.1,
|
160
|
+
'title': 'Cost',
|
161
|
+
'default_val': None # No initial value
|
162
|
+
}
|
163
|
+
|
164
|
+
# Create slider components
|
165
|
+
cost_slider_kwargs = {
|
166
|
+
'id_base': id_base,
|
167
|
+
'min_val': cost_normalized['min_val'],
|
168
|
+
'max_val': cost_normalized['max_val'],
|
169
|
+
'step': cost_normalized['step'],
|
170
|
+
'mark_interval': cost_normalized['mark_interval'],
|
171
|
+
'property_name': "specific_cost",
|
172
|
+
'title': cost_normalized['title'],
|
173
|
+
'default_val': cost_normalized.get('default_val', cost_normalized['min_val']),
|
174
|
+
'with_slider_titles': True,
|
175
|
+
'slider_disable': slider_disable,
|
176
|
+
'div_width': '100%'
|
177
|
+
}
|
178
|
+
# Add marks if available
|
179
|
+
if 'marks' in cost_normalized:
|
180
|
+
cost_slider_kwargs['marks'] = cost_normalized['marks']
|
181
|
+
|
182
|
+
self.cost_slider = SliderWithTextInput(**cost_slider_kwargs)
|
183
|
+
|
184
|
+
density_slider_kwargs = {
|
185
|
+
'id_base': id_base,
|
186
|
+
'min_val': density_normalized['min_val'],
|
187
|
+
'max_val': density_normalized['max_val'],
|
188
|
+
'step': density_normalized['step'],
|
189
|
+
'mark_interval': density_normalized['mark_interval'],
|
190
|
+
'property_name': "density",
|
191
|
+
'title': density_normalized['title'],
|
192
|
+
'default_val': density_normalized.get('default_val', density_normalized['min_val']),
|
193
|
+
'with_slider_titles': True,
|
194
|
+
'slider_disable': slider_disable,
|
195
|
+
'div_width': '100%'
|
196
|
+
}
|
197
|
+
# Add marks if available
|
198
|
+
if 'marks' in density_normalized:
|
199
|
+
density_slider_kwargs['marks'] = density_normalized['marks']
|
200
|
+
|
201
|
+
self.density_slider = SliderWithTextInput(**density_slider_kwargs)
|
202
|
+
|
203
|
+
def _normalize_config_from_arrays(self, config: dict, index: int, default_title: str) -> dict:
|
204
|
+
"""
|
205
|
+
Normalize slider configuration from create_slider_config output arrays.
|
206
|
+
|
207
|
+
Args:
|
208
|
+
config (dict): create_slider_config output with array values
|
209
|
+
index (int): Index to extract from each array (0 = density, 1 = cost)
|
210
|
+
default_title (str): Default title if not provided
|
211
|
+
|
212
|
+
Returns:
|
213
|
+
dict: Normalized configuration with min_val, max_val, step, mark_interval, title keys
|
214
|
+
"""
|
215
|
+
normalized = {
|
216
|
+
'min_val': config['min_vals'][index],
|
217
|
+
'max_val': config['max_vals'][index],
|
218
|
+
'step': config['step_vals'][index],
|
219
|
+
'title': default_title # Use provided title
|
220
|
+
}
|
221
|
+
|
222
|
+
# Get pre-computed marks if available, otherwise calculate mark_interval
|
223
|
+
if 'mark_vals' in config and len(config['mark_vals']) > index:
|
224
|
+
marks = config['mark_vals'][index]
|
225
|
+
normalized['marks'] = marks # Pass the actual marks dictionary
|
226
|
+
if len(marks) >= 2:
|
227
|
+
# Calculate interval from first two marks
|
228
|
+
mark_positions = sorted(marks.keys())
|
229
|
+
normalized['mark_interval'] = mark_positions[1] - mark_positions[0]
|
230
|
+
else:
|
231
|
+
# Fallback: use step as mark interval
|
232
|
+
normalized['mark_interval'] = normalized['step']
|
233
|
+
else:
|
234
|
+
normalized['mark_interval'] = normalized['step']
|
235
|
+
|
236
|
+
# Add default value if available from grid values
|
237
|
+
if 'grid_slider_vals' in config and len(config['grid_slider_vals']) > index:
|
238
|
+
normalized['default_val'] = config['grid_slider_vals'][index]
|
239
|
+
else:
|
240
|
+
normalized['default_val'] = normalized['min_val']
|
241
|
+
|
242
|
+
return normalized
|
243
|
+
|
244
|
+
def _normalize_config(self, config: dict, config_type: str) -> dict:
|
245
|
+
"""
|
246
|
+
Normalize slider configuration to handle both legacy and create_slider_config formats.
|
247
|
+
|
248
|
+
Args:
|
249
|
+
config (dict): Either legacy format or create_slider_config output
|
250
|
+
config_type (str): Type identifier ('cost' or 'density') for default title
|
251
|
+
|
252
|
+
Returns:
|
253
|
+
dict: Normalized configuration with min_val, max_val, step, mark_interval, title keys
|
254
|
+
"""
|
255
|
+
# Check if this is a create_slider_config output (has min_vals, max_vals, etc.)
|
256
|
+
if 'min_vals' in config and 'max_vals' in config:
|
257
|
+
# This is create_slider_config output - use first element from arrays
|
258
|
+
normalized = {
|
259
|
+
'min_val': config['min_vals'][0],
|
260
|
+
'max_val': config['max_vals'][0],
|
261
|
+
'step': config['step_vals'][0],
|
262
|
+
'title': config_type.capitalize() # Default title
|
263
|
+
}
|
264
|
+
|
265
|
+
# Get pre-computed marks if available, otherwise calculate mark_interval
|
266
|
+
if 'mark_vals' in config and len(config['mark_vals']) > 0:
|
267
|
+
marks = config['mark_vals'][0]
|
268
|
+
normalized['marks'] = marks # Pass the actual marks dictionary
|
269
|
+
if len(marks) >= 2:
|
270
|
+
# Calculate interval from first two marks
|
271
|
+
mark_positions = sorted(marks.keys())
|
272
|
+
normalized['mark_interval'] = mark_positions[1] - mark_positions[0]
|
273
|
+
else:
|
274
|
+
# Fallback: use step as mark interval
|
275
|
+
normalized['mark_interval'] = normalized['step']
|
276
|
+
else:
|
277
|
+
normalized['mark_interval'] = normalized['step']
|
278
|
+
|
279
|
+
# Add default value if available from grid values
|
280
|
+
if 'grid_slider_vals' in config and len(config['grid_slider_vals']) > 0:
|
281
|
+
normalized['default_val'] = config['grid_slider_vals'][0]
|
282
|
+
else:
|
283
|
+
normalized['default_val'] = normalized['min_val']
|
284
|
+
|
285
|
+
else:
|
286
|
+
# This is legacy format - use as-is but ensure all required keys exist
|
287
|
+
normalized = config.copy()
|
288
|
+
if 'title' not in normalized:
|
289
|
+
normalized['title'] = config_type.capitalize()
|
290
|
+
if 'default_val' not in normalized:
|
291
|
+
normalized['default_val'] = normalized.get('min_val', 0)
|
292
|
+
|
293
|
+
return normalized
|
294
|
+
|
295
|
+
def _make_id(self, subtype: str):
|
296
|
+
"""
|
297
|
+
Generate a unique ID dictionary for component sub-elements.
|
298
|
+
|
299
|
+
Args:
|
300
|
+
subtype (str): The specific component subtype.
|
301
|
+
|
302
|
+
Returns:
|
303
|
+
dict: Complete ID dictionary containing base ID information plus subtype.
|
304
|
+
"""
|
305
|
+
return {**self.id_base, 'subtype': subtype}
|
306
|
+
|
307
|
+
def _make_dropdown(self):
|
308
|
+
"""
|
309
|
+
Create and configure the material selection dropdown.
|
310
|
+
|
311
|
+
Returns:
|
312
|
+
dash.dcc.Dropdown: Configured dropdown component for material selection.
|
313
|
+
"""
|
314
|
+
return dcc.Dropdown(
|
315
|
+
id=self.dropdown_id,
|
316
|
+
options=self.material_options,
|
317
|
+
value=self.default_material, # Can be None for no initial selection
|
318
|
+
disabled=self.slider_disable,
|
319
|
+
style={'width': '100%', 'margin-bottom': '10px'}
|
320
|
+
)
|
321
|
+
|
322
|
+
def _make_weight_fraction_input(self):
|
323
|
+
"""
|
324
|
+
Create and configure the weight percentage input.
|
325
|
+
|
326
|
+
Returns:
|
327
|
+
dash.dcc.Input: Configured numeric input for weight percentage.
|
328
|
+
"""
|
329
|
+
return dcc.Input(
|
330
|
+
id=self.weight_fraction_id,
|
331
|
+
type='number',
|
332
|
+
value=self.default_weight_percent, # Use percentage directly
|
333
|
+
min=0.0,
|
334
|
+
max=100.0,
|
335
|
+
step=0.1,
|
336
|
+
disabled=self.slider_disable,
|
337
|
+
style={'width': '100%', 'margin-bottom': '10px'}
|
338
|
+
)
|
339
|
+
|
340
|
+
def __call__(self):
|
341
|
+
"""
|
342
|
+
Generate the complete component layout as a callable object.
|
343
|
+
|
344
|
+
Creates and returns a Dash HTML Div containing all components arranged
|
345
|
+
in a horizontal layout with proper spacing and styling.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
dash.html.Div: Complete component layout with horizontal arrangement.
|
349
|
+
"""
|
350
|
+
return html.Div([
|
351
|
+
# Main horizontal layout
|
352
|
+
html.Div([
|
353
|
+
# Material selection column
|
354
|
+
html.Div([
|
355
|
+
html.P("Material:", style={'margin': '0px 0px 5px 0px', 'font-weight': 'bold'}),
|
356
|
+
self._make_dropdown()
|
357
|
+
], style={'width': '20%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '25px'}),
|
358
|
+
|
359
|
+
# Weight percentage column
|
360
|
+
html.Div([
|
361
|
+
html.P("Weight Percentage:", style={'margin': '0px 0px 5px 0px', 'font-weight': 'bold'}),
|
362
|
+
self._make_weight_fraction_input()
|
363
|
+
], style={'width': '15%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '25px'}),
|
364
|
+
|
365
|
+
# Density slider column
|
366
|
+
html.Div([
|
367
|
+
self.density_slider()
|
368
|
+
], style={'width': '30%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '25px'}),
|
369
|
+
|
370
|
+
# Cost slider column
|
371
|
+
html.Div([
|
372
|
+
self.cost_slider()
|
373
|
+
], style={'width': '30%', 'display': 'inline-block', 'vertical-align': 'top'})
|
374
|
+
|
375
|
+
], style={'display': 'flex', 'align-items': 'flex-start', 'width': '100%', 'gap': '15px'})
|
376
|
+
|
377
|
+
],
|
378
|
+
id=self.id_base,
|
379
|
+
style={
|
380
|
+
'border': '1px solid #ddd',
|
381
|
+
'border-radius': '5px',
|
382
|
+
'padding': '15px',
|
383
|
+
'margin': '10px 0px',
|
384
|
+
'background-color': '#f9f9f9',
|
385
|
+
'width': self.div_width,
|
386
|
+
'display': 'none' if self.hidden else 'block'
|
387
|
+
})
|
388
|
+
|
389
|
+
@property
|
390
|
+
def components(self):
|
391
|
+
"""
|
392
|
+
Get a dictionary mapping component types to their IDs.
|
393
|
+
|
394
|
+
Returns:
|
395
|
+
dict: Dictionary with component type keys mapping to their ID dictionaries.
|
396
|
+
"""
|
397
|
+
return {
|
398
|
+
'dropdown': self.dropdown_id,
|
399
|
+
'weight_fraction': self.weight_fraction_id,
|
400
|
+
'cost_slider': self.cost_slider.slider_id,
|
401
|
+
'cost_input': self.cost_slider.input_id,
|
402
|
+
'density_slider': self.density_slider.slider_id,
|
403
|
+
'density_input': self.density_slider.input_id
|
404
|
+
}
|
405
|
+
|
406
|
+
def get_all_inputs(self):
|
407
|
+
"""
|
408
|
+
Get Input objects for all component values.
|
409
|
+
|
410
|
+
Returns:
|
411
|
+
list: List containing Input objects for all components.
|
412
|
+
"""
|
413
|
+
return [
|
414
|
+
Input(self.dropdown_id, 'value'),
|
415
|
+
Input(self.weight_fraction_id, 'value'),
|
416
|
+
Input(self.cost_slider.slider_id, 'value'),
|
417
|
+
Input(self.cost_slider.input_id, 'value'),
|
418
|
+
Input(self.density_slider.slider_id, 'value'),
|
419
|
+
Input(self.density_slider.input_id, 'value')
|
420
|
+
]
|
421
|
+
|
422
|
+
def get_all_outputs(self):
|
423
|
+
"""
|
424
|
+
Get Output objects for updating all component values.
|
425
|
+
|
426
|
+
Returns:
|
427
|
+
list: List containing Output objects for all components.
|
428
|
+
"""
|
429
|
+
return [
|
430
|
+
Output(self.dropdown_id, 'value'),
|
431
|
+
Output(self.weight_fraction_id, 'value'),
|
432
|
+
Output(self.cost_slider.slider_id, 'value'),
|
433
|
+
Output(self.cost_slider.input_id, 'value'),
|
434
|
+
Output(self.density_slider.slider_id, 'value'),
|
435
|
+
Output(self.density_slider.input_id, 'value')
|
436
|
+
]
|
437
|
+
|
438
|
+
|
439
|
+
class ActiveMaterialSelector(MaterialSelector):
|
440
|
+
"""
|
441
|
+
A custom Dash component for active material selection with capacity controls.
|
442
|
+
|
443
|
+
This component extends MaterialSelector functionality by adding reversible and
|
444
|
+
irreversible capacity sliders alongside the standard density and cost controls.
|
445
|
+
It creates a horizontal row containing:
|
446
|
+
- A dropdown menu for selecting material names
|
447
|
+
- An input box for specifying weight percentage of the material
|
448
|
+
- Four SliderWithTextInput components for:
|
449
|
+
* Density
|
450
|
+
* Specific cost
|
451
|
+
* Reversible capacity scaling
|
452
|
+
* Irreversible capacity scaling
|
453
|
+
|
454
|
+
This component is designed for battery active material interfaces where users need to
|
455
|
+
specify both physical properties (cost, density) and electrochemical scaling factors
|
456
|
+
(reversible/irreversible capacity scaling).
|
457
|
+
"""
|
458
|
+
|
459
|
+
def __init__(
|
460
|
+
self,
|
461
|
+
id_base: dict,
|
462
|
+
material_options: List[Dict] = None,
|
463
|
+
slider_configs: dict = None,
|
464
|
+
title: str = "Active Material",
|
465
|
+
default_material: str = None,
|
466
|
+
default_weight_percent: float = 0,
|
467
|
+
slider_disable: bool = False,
|
468
|
+
div_width: str = '100%',
|
469
|
+
hidden: bool = False,
|
470
|
+
):
|
471
|
+
"""
|
472
|
+
Initialize the ActiveMaterialSelector component.
|
473
|
+
|
474
|
+
Args:
|
475
|
+
id_base (dict): Base dictionary for generating component IDs.
|
476
|
+
material_options (List[Dict], optional): List of material options for dropdown.
|
477
|
+
Each dict should have 'label' and 'value' keys.
|
478
|
+
If None, defaults to empty list (no options).
|
479
|
+
slider_configs (dict): Output from create_slider_config with arrays where:
|
480
|
+
- Index 0 = density configuration
|
481
|
+
- Index 1 = cost configuration
|
482
|
+
- Index 2 = reversible capacity scaling configuration
|
483
|
+
- Index 3 = irreversible capacity scaling configuration
|
484
|
+
If empty dictionary {}, uses sensible defaults with preset values.
|
485
|
+
If None (default), uses sensible defaults with no initial values.
|
486
|
+
title (str, optional): Title displayed for the component. Defaults to "Active Material".
|
487
|
+
default_material (str, optional): Default selected material. If None, no material
|
488
|
+
will be initially selected in the dropdown.
|
489
|
+
default_weight_percent (float, optional): Default weight percentage (0.0-100.0). Defaults to 100.0.
|
490
|
+
slider_disable (bool, optional): Whether to disable sliders. Defaults to False.
|
491
|
+
div_width (str, optional): CSS width specification for the container div.
|
492
|
+
Defaults to '100%'.
|
493
|
+
hidden (bool, optional): Whether to hide the entire component. When True,
|
494
|
+
the component will be rendered with display: none.
|
495
|
+
Defaults to False.
|
496
|
+
"""
|
497
|
+
# Handle different slider_configs scenarios
|
498
|
+
if slider_configs is not None and slider_configs:
|
499
|
+
# Use provided configuration
|
500
|
+
pass # slider_configs will be passed to parent
|
501
|
+
elif slider_configs is not None and not slider_configs:
|
502
|
+
# Empty dictionary - create sensible defaults with preset values
|
503
|
+
from ..Utils.SliderControls import create_slider_config
|
504
|
+
min_vals = [0.0, 0.0, 0.0, 0.0] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
505
|
+
max_vals = [0.1, 0.1, 0.1, 0.1] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
506
|
+
default_vals = [0.05, 0.05, 0.05, 0.05] # density, cost, rev_cap_scaling, irrev_cap_scaling
|
507
|
+
slider_configs = create_slider_config(min_vals, max_vals, default_vals)
|
508
|
+
else:
|
509
|
+
# slider_configs is None - create sensible defaults with None values
|
510
|
+
# Create manual config since create_slider_config doesn't handle None values
|
511
|
+
slider_configs = {
|
512
|
+
'min_vals': [0.0, 0.0, 0.0, 0.0],
|
513
|
+
'max_vals': [0.1, 0.1, 0.1, 0.1],
|
514
|
+
'step_vals': [0.01, 0.01, 0.01, 0.01],
|
515
|
+
'grid_slider_vals': [None, None, None, None],
|
516
|
+
'mark_vals': [
|
517
|
+
{0.0: '', 0.1: ''}, # density
|
518
|
+
{0.0: '', 0.1: ''}, # cost
|
519
|
+
{0.0: '', 0.1: ''}, # rev_cap_scaling
|
520
|
+
{0.0: '', 0.1: ''} # irrev_cap_scaling
|
521
|
+
]
|
522
|
+
}
|
523
|
+
|
524
|
+
# Initialize the parent MaterialSelector with first two sliders (density, cost)
|
525
|
+
super().__init__(
|
526
|
+
id_base=id_base,
|
527
|
+
material_options=material_options,
|
528
|
+
slider_configs=slider_configs, # Parent will use indices 0 and 1
|
529
|
+
title=title,
|
530
|
+
default_material=default_material,
|
531
|
+
default_weight_percent=default_weight_percent,
|
532
|
+
slider_disable=slider_disable,
|
533
|
+
div_width=div_width,
|
534
|
+
hidden=hidden
|
535
|
+
)
|
536
|
+
|
537
|
+
# Add the additional capacity sliders (indices 2 and 3)
|
538
|
+
reversible_capacity_normalized = self._normalize_config_from_arrays(slider_configs, 2, 'Reversible Capacity Scaling')
|
539
|
+
irreversible_capacity_normalized = self._normalize_config_from_arrays(slider_configs, 3, 'Irreversible Capacity Scaling')
|
540
|
+
|
541
|
+
# Create additional slider components
|
542
|
+
reversible_capacity_slider_kwargs = {
|
543
|
+
'id_base': id_base,
|
544
|
+
'min_val': reversible_capacity_normalized['min_val'],
|
545
|
+
'max_val': reversible_capacity_normalized['max_val'],
|
546
|
+
'step': reversible_capacity_normalized['step'],
|
547
|
+
'mark_interval': reversible_capacity_normalized['mark_interval'],
|
548
|
+
'property_name': "reversible_capacity_scaling",
|
549
|
+
'title': reversible_capacity_normalized['title'],
|
550
|
+
'default_val': reversible_capacity_normalized.get('default_val', reversible_capacity_normalized['min_val']),
|
551
|
+
'with_slider_titles': True,
|
552
|
+
'slider_disable': slider_disable,
|
553
|
+
'div_width': '100%'
|
554
|
+
}
|
555
|
+
if 'marks' in reversible_capacity_normalized:
|
556
|
+
reversible_capacity_slider_kwargs['marks'] = reversible_capacity_normalized['marks']
|
557
|
+
self.reversible_capacity_slider = SliderWithTextInput(**reversible_capacity_slider_kwargs)
|
558
|
+
|
559
|
+
irreversible_capacity_slider_kwargs = {
|
560
|
+
'id_base': id_base,
|
561
|
+
'min_val': irreversible_capacity_normalized['min_val'],
|
562
|
+
'max_val': irreversible_capacity_normalized['max_val'],
|
563
|
+
'step': irreversible_capacity_normalized['step'],
|
564
|
+
'mark_interval': irreversible_capacity_normalized['mark_interval'],
|
565
|
+
'property_name': "irreversible_capacity_scaling",
|
566
|
+
'title': irreversible_capacity_normalized['title'],
|
567
|
+
'default_val': irreversible_capacity_normalized.get('default_val', irreversible_capacity_normalized['min_val']),
|
568
|
+
'with_slider_titles': True,
|
569
|
+
'slider_disable': slider_disable,
|
570
|
+
'div_width': '100%'
|
571
|
+
}
|
572
|
+
if 'marks' in irreversible_capacity_normalized:
|
573
|
+
irreversible_capacity_slider_kwargs['marks'] = irreversible_capacity_normalized['marks']
|
574
|
+
self.irreversible_capacity_slider = SliderWithTextInput(**irreversible_capacity_slider_kwargs)
|
575
|
+
|
576
|
+
def __call__(self):
|
577
|
+
"""
|
578
|
+
Generate the complete component layout as a callable object.
|
579
|
+
|
580
|
+
Creates and returns a Dash HTML Div containing all components arranged
|
581
|
+
in a horizontal layout with proper spacing and styling.
|
582
|
+
|
583
|
+
Returns:
|
584
|
+
dash.html.Div: Complete component layout with horizontal arrangement.
|
585
|
+
"""
|
586
|
+
return html.Div([
|
587
|
+
# Main horizontal layout
|
588
|
+
html.Div([
|
589
|
+
# Material selection column (15%)
|
590
|
+
html.Div([
|
591
|
+
html.P("Material:", style={'margin': '0px 0px 5px 0px', 'font-weight': 'bold'}),
|
592
|
+
self._make_dropdown()
|
593
|
+
], style={'width': '15%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '15px'}),
|
594
|
+
|
595
|
+
# Weight percentage column (15%)
|
596
|
+
html.Div([
|
597
|
+
html.P("Weight %:", style={'margin': '0px 0px 5px 0px', 'font-weight': 'bold'}),
|
598
|
+
self._make_weight_fraction_input()
|
599
|
+
], style={'width': '15%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '15px'}),
|
600
|
+
|
601
|
+
# Density slider column (17.5%)
|
602
|
+
html.Div([
|
603
|
+
self.density_slider()
|
604
|
+
], style={'width': '17.5%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '15px'}),
|
605
|
+
|
606
|
+
# Cost slider column (17.5%)
|
607
|
+
html.Div([
|
608
|
+
self.cost_slider()
|
609
|
+
], style={'width': '17.5%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '15px'}),
|
610
|
+
|
611
|
+
# Reversible capacity slider column (17.5%)
|
612
|
+
html.Div([
|
613
|
+
self.reversible_capacity_slider()
|
614
|
+
], style={'width': '17.5%', 'display': 'inline-block', 'vertical-align': 'top', 'padding-right': '15px'}),
|
615
|
+
|
616
|
+
# Irreversible capacity slider column (17.5%)
|
617
|
+
html.Div([
|
618
|
+
self.irreversible_capacity_slider()
|
619
|
+
], style={'width': '17.5%', 'display': 'inline-block', 'vertical-align': 'top'})
|
620
|
+
|
621
|
+
], style={'display': 'flex', 'align-items': 'flex-start', 'width': '100%', 'gap': '10px'})
|
622
|
+
|
623
|
+
],
|
624
|
+
id=self.id_base,
|
625
|
+
style={
|
626
|
+
'border': '1px solid #ddd',
|
627
|
+
'border-radius': '5px',
|
628
|
+
'padding': '15px',
|
629
|
+
'margin': '10px 0px',
|
630
|
+
'background-color': '#f9f9f9',
|
631
|
+
'width': self.div_width,
|
632
|
+
'display': 'none' if self.hidden else 'block'
|
633
|
+
})
|
634
|
+
|
635
|
+
@property
|
636
|
+
def components(self):
|
637
|
+
"""
|
638
|
+
Get a dictionary mapping component types to their IDs.
|
639
|
+
|
640
|
+
Returns:
|
641
|
+
dict: Dictionary with component type keys mapping to their ID dictionaries.
|
642
|
+
"""
|
643
|
+
# Get base components from parent
|
644
|
+
base_components = super().components
|
645
|
+
|
646
|
+
# Add the additional capacity slider components
|
647
|
+
base_components.update({
|
648
|
+
'reversible_capacity_scaling_slider': self.reversible_capacity_slider.slider_id,
|
649
|
+
'reversible_capacity_scaling_input': self.reversible_capacity_slider.input_id,
|
650
|
+
'irreversible_capacity_scaling_slider': self.irreversible_capacity_slider.slider_id,
|
651
|
+
'irreversible_capacity_scaling_input': self.irreversible_capacity_slider.input_id
|
652
|
+
})
|
653
|
+
|
654
|
+
return base_components
|
655
|
+
|
656
|
+
def get_all_inputs(self):
|
657
|
+
"""
|
658
|
+
Get Input objects for all component values.
|
659
|
+
|
660
|
+
Returns:
|
661
|
+
list: List containing Input objects for all components.
|
662
|
+
"""
|
663
|
+
# Get base inputs from parent
|
664
|
+
base_inputs = super().get_all_inputs()
|
665
|
+
|
666
|
+
# Add the additional capacity slider inputs
|
667
|
+
capacity_inputs = [
|
668
|
+
Input(self.reversible_capacity_slider.slider_id, 'value'),
|
669
|
+
Input(self.reversible_capacity_slider.input_id, 'value'),
|
670
|
+
Input(self.irreversible_capacity_slider.slider_id, 'value'),
|
671
|
+
Input(self.irreversible_capacity_slider.input_id, 'value')
|
672
|
+
]
|
673
|
+
|
674
|
+
return base_inputs + capacity_inputs
|
675
|
+
|
676
|
+
def get_all_outputs(self):
|
677
|
+
"""
|
678
|
+
Get Output objects for updating all component values.
|
679
|
+
|
680
|
+
Returns:
|
681
|
+
list: List containing Output objects for all components.
|
682
|
+
"""
|
683
|
+
# Get base outputs from parent
|
684
|
+
base_outputs = super().get_all_outputs()
|
685
|
+
|
686
|
+
# Add the additional capacity slider outputs
|
687
|
+
capacity_outputs = [
|
688
|
+
Output(self.reversible_capacity_slider.slider_id, 'value'),
|
689
|
+
Output(self.reversible_capacity_slider.input_id, 'value'),
|
690
|
+
Output(self.irreversible_capacity_slider.slider_id, 'value'),
|
691
|
+
Output(self.irreversible_capacity_slider.input_id, 'value')
|
692
|
+
]
|
693
|
+
|
694
|
+
return base_outputs + capacity_outputs
|
@@ -62,6 +62,7 @@ class SliderWithTextInput:
|
|
62
62
|
slider_disable: bool = False,
|
63
63
|
div_width: str = 'calc(90%)',
|
64
64
|
message: str = None,
|
65
|
+
marks: dict = None,
|
65
66
|
):
|
66
67
|
"""
|
67
68
|
Initialize the SliderWithTextInput component.
|
@@ -94,6 +95,11 @@ class SliderWithTextInput:
|
|
94
95
|
message (str, optional): Optional message to display between the title
|
95
96
|
and slider. If None, no message is displayed.
|
96
97
|
Defaults to None.
|
98
|
+
marks (dict, optional): Pre-computed marks dictionary for the slider.
|
99
|
+
If provided, these marks will be used instead of
|
100
|
+
auto-generating marks from mark_interval.
|
101
|
+
Keys should be numeric positions, values should be labels.
|
102
|
+
Defaults to None.
|
97
103
|
|
98
104
|
Raises:
|
99
105
|
ValueError: If min_val >= max_val, or if step <= 0, or if mark_interval <= 0.
|
@@ -112,6 +118,7 @@ class SliderWithTextInput:
|
|
112
118
|
self.div_width = div_width
|
113
119
|
self.slider_disable = slider_disable
|
114
120
|
self.message = message
|
121
|
+
self.marks = marks
|
115
122
|
|
116
123
|
self.slider_id = self._make_id('slider')
|
117
124
|
self.input_id = self._make_id('input')
|
@@ -149,10 +156,17 @@ class SliderWithTextInput:
|
|
149
156
|
tick marks, and styling options.
|
150
157
|
|
151
158
|
Note:
|
152
|
-
-
|
159
|
+
- Uses pre-computed marks if provided, otherwise generates tick marks
|
160
|
+
at intervals specified by mark_interval
|
153
161
|
- updatemode is set to 'mouseup' to reduce callback frequency
|
154
162
|
- The slider can be disabled via the slider_disable attribute
|
155
163
|
"""
|
164
|
+
# Use provided marks or generate them from mark_interval
|
165
|
+
if self.marks is not None:
|
166
|
+
slider_marks = self.marks
|
167
|
+
else:
|
168
|
+
slider_marks = {int(i): "" for i in np.arange(self.min_val, self.max_val + self.mark_interval, self.mark_interval)}
|
169
|
+
|
156
170
|
return ds.dcc.Slider(
|
157
171
|
id=self.slider_id,
|
158
172
|
min=self.min_val,
|
@@ -160,7 +174,7 @@ class SliderWithTextInput:
|
|
160
174
|
value=self.default_val,
|
161
175
|
step=self.step,
|
162
176
|
disabled=self.slider_disable,
|
163
|
-
marks=
|
177
|
+
marks=slider_marks,
|
164
178
|
updatemode='mouseup'
|
165
179
|
)
|
166
180
|
|
@@ -218,7 +232,7 @@ class SliderWithTextInput:
|
|
218
232
|
|
219
233
|
# Build the component list
|
220
234
|
components = [
|
221
|
-
ds.html.P(slider_title, style={'margin-left': '20px', 'margin-bottom': '0px'})
|
235
|
+
ds.html.P(slider_title, style={'margin-left': '20px', 'margin-bottom': '0px', 'font-weight': 'bold'})
|
222
236
|
]
|
223
237
|
|
224
238
|
# Add optional message if provided
|
@@ -7,7 +7,6 @@ slider configurations based on parameter ranges.
|
|
7
7
|
|
8
8
|
import math
|
9
9
|
from typing import List, Union, Dict
|
10
|
-
import numpy as np
|
11
10
|
|
12
11
|
|
13
12
|
|
@@ -179,11 +178,17 @@ def calculate_mark_intervals(min_values: List[float], max_values: List[float],
|
|
179
178
|
"""
|
180
179
|
Calculate mark intervals for sliders based on range magnitude.
|
181
180
|
|
182
|
-
Creates marks at regular intervals that align with intuitive values
|
183
|
-
|
184
|
-
- Range <
|
185
|
-
- Range <
|
186
|
-
- Range
|
181
|
+
Creates marks at regular intervals that align with intuitive values while
|
182
|
+
avoiding overcrowding by ensuring no more than ~5-6 marks per slider:
|
183
|
+
- Range < 0.5: marks on every 0.1, but max 5 marks
|
184
|
+
- Range < 1: marks on every 0.2 (interval = 0.2)
|
185
|
+
- Range < 5: marks on every 1.0 (interval = 1.0)
|
186
|
+
- Range < 10: marks on every 2.0 (interval = 2.0)
|
187
|
+
- Range < 50: marks on every 10.0 (interval = 10.0)
|
188
|
+
- Range < 100: marks on every 20.0 (interval = 20.0)
|
189
|
+
- Range < 500: marks on every 100.0 (interval = 100.0)
|
190
|
+
- Range < 1000: marks on every 200.0 (interval = 200.0)
|
191
|
+
- Range >= 1000: marks on every 500.0 (interval = 500.0)
|
187
192
|
|
188
193
|
Args:
|
189
194
|
min_values (List[float]): List of minimum values for each parameter
|
@@ -195,10 +200,10 @@ def calculate_mark_intervals(min_values: List[float], max_values: List[float],
|
|
195
200
|
|
196
201
|
Examples:
|
197
202
|
>>> min_vals = [0, 0, 0, 0]
|
198
|
-
>>> max_vals = [0.
|
203
|
+
>>> max_vals = [0.3, 2, 250, 2000]
|
199
204
|
>>> intervals = calculate_mark_intervals(min_vals, max_vals)
|
200
205
|
>>> intervals
|
201
|
-
[0.1, 1.0,
|
206
|
+
[0.1, 1.0, 100.0, 500.0]
|
202
207
|
"""
|
203
208
|
if len(min_values) != len(max_values):
|
204
209
|
raise ValueError("min_values and max_values must have the same length")
|
@@ -212,24 +217,40 @@ def calculate_mark_intervals(min_values: List[float], max_values: List[float],
|
|
212
217
|
intervals.append(1.0)
|
213
218
|
continue
|
214
219
|
|
215
|
-
# Determine interval based on range magnitude
|
216
|
-
|
217
|
-
|
218
|
-
interval = 0.1
|
220
|
+
# Determine interval based on range magnitude to avoid overcrowding
|
221
|
+
if range_val < 0.5:
|
222
|
+
# Very small ranges: use 0.1 but ensure max 5 marks
|
223
|
+
interval = 0.1
|
224
|
+
num_marks = range_val / interval
|
225
|
+
if num_marks > 5:
|
226
|
+
interval = range_val / 4 # Limit to ~4 marks
|
227
|
+
elif range_val < 1:
|
228
|
+
interval = 0.2 # Every 0.2 for ranges like 0.5-0.9
|
229
|
+
elif range_val < 5:
|
230
|
+
interval = 1.0 # Every integer for small ranges
|
219
231
|
elif range_val < 10:
|
220
|
-
interval =
|
221
|
-
elif range_val <
|
232
|
+
interval = 2.0 # Every 2 units
|
233
|
+
elif range_val < 50:
|
222
234
|
interval = 10.0 # Every multiple of 10
|
223
|
-
|
235
|
+
elif range_val < 100:
|
236
|
+
interval = 20.0 # Every multiple of 20
|
237
|
+
elif range_val < 500:
|
224
238
|
interval = 100.0 # Every multiple of 100
|
239
|
+
elif range_val < 1000:
|
240
|
+
interval = 200.0 # Every multiple of 200
|
241
|
+
else:
|
242
|
+
interval = 500.0 # Every multiple of 500 for very large ranges
|
225
243
|
|
226
244
|
intervals.append(interval)
|
227
245
|
|
228
246
|
return intervals
|
229
247
|
|
230
248
|
|
231
|
-
def create_slider_config(
|
232
|
-
|
249
|
+
def create_slider_config(
|
250
|
+
min_values: List[float],
|
251
|
+
max_values: List[float],
|
252
|
+
property_values: List[float] = None
|
253
|
+
) -> dict:
|
233
254
|
"""
|
234
255
|
Create complete slider configurations with automatically calculated steps and marks.
|
235
256
|
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
__version__ = "0.1.10"
|
@@ -9,7 +9,7 @@ steer_core.egg-info/requires.txt
|
|
9
9
|
steer_core.egg-info/top_level.txt
|
10
10
|
steer_core/Apps/ContextManagers.py
|
11
11
|
steer_core/Apps/__init__.py
|
12
|
-
steer_core/Apps/Components/
|
12
|
+
steer_core/Apps/Components/MaterialSelectors.py
|
13
13
|
steer_core/Apps/Components/RangeSliderComponents.py
|
14
14
|
steer_core/Apps/Components/SliderComponents.py
|
15
15
|
steer_core/Apps/Components/__init__.py
|
File without changes
|
@@ -1 +0,0 @@
|
|
1
|
-
__version__ = "0.1.9"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|