steer-core 0.1.16__py3-none-any.whl → 0.1.17__py3-none-any.whl

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.
@@ -7,16 +7,16 @@ from dash import Input, Output
7
7
  class RangeSliderWithTextInput:
8
8
  """
9
9
  A custom Dash component that combines a range slider with two text inputs for synchronized range control.
10
-
10
+
11
11
  This component creates a user interface element consisting of a range slider and two numeric input fields
12
12
  that can be used together to set numeric ranges. The range slider provides visual feedback and easy
13
- adjustment of both start and end values, while the text inputs allow for precise value entry. All
13
+ adjustment of both start and end values, while the text inputs allow for precise value entry. All
14
14
  components are synchronized and share the same value constraints.
15
-
15
+
16
16
  The component is designed for use in Dash applications where users need to input numeric ranges
17
17
  within a specified domain, with the flexibility of both visual (range slider) and precise (text inputs)
18
18
  control methods.
19
-
19
+
20
20
  Attributes:
21
21
  id_base (dict): Base identifier dictionary used to construct unique IDs for child components
22
22
  min_val (float): Minimum allowed value for both slider and inputs
@@ -33,7 +33,7 @@ class RangeSliderWithTextInput:
33
33
  slider_id (dict): Computed ID for the range slider component
34
34
  input_start_id (dict): Computed ID for the start value input component
35
35
  input_end_id (dict): Computed ID for the end value input component
36
-
36
+
37
37
  Example:
38
38
  >>> range_component = RangeSliderWithTextInput(
39
39
  ... id_base={'type': 'parameter', 'index': 0},
@@ -61,12 +61,12 @@ class RangeSliderWithTextInput:
61
61
  default_val: Union[list[float], None] = None,
62
62
  with_slider_titles: bool = True,
63
63
  slider_disable: bool = False,
64
- div_width: str = 'calc(90%)',
64
+ div_width: str = "calc(90%)",
65
65
  message: str = None,
66
66
  ):
67
67
  """
68
68
  Initialize the RangeSliderWithTextInput component.
69
-
69
+
70
70
  Args:
71
71
  id_base (dict): Base dictionary for generating component IDs. Should contain
72
72
  identifying information that will be extended with component-specific
@@ -94,27 +94,31 @@ class RangeSliderWithTextInput:
94
94
  message (str, optional): Optional message to display between the title
95
95
  and slider. If None, no message is displayed.
96
96
  Defaults to None.
97
-
97
+
98
98
  Raises:
99
99
  ValueError: If min_val >= max_val, or if step <= 0, or if mark_interval <= 0,
100
100
  or if default_val contains invalid range values.
101
101
  TypeError: If default_val is provided but not a list of numeric values.
102
102
  """
103
-
103
+
104
104
  # Validate inputs
105
105
  if min_val >= max_val:
106
- raise ValueError(f"min_val ({min_val}) must be less than max_val ({max_val})")
106
+ raise ValueError(
107
+ f"min_val ({min_val}) must be less than max_val ({max_val})"
108
+ )
107
109
  if step <= 0:
108
110
  raise ValueError(f"step ({step}) must be positive")
109
111
  if mark_interval <= 0:
110
112
  raise ValueError(f"mark_interval ({mark_interval}) must be positive")
111
-
113
+
112
114
  # Validate and set default values
113
115
  if default_val is None:
114
116
  default_val = [min_val, max_val]
115
117
  else:
116
118
  if not isinstance(default_val, list) or len(default_val) != 2:
117
- raise TypeError("default_val must be a list of two numeric values [start, end]")
119
+ raise TypeError(
120
+ "default_val must be a list of two numeric values [start, end]"
121
+ )
118
122
  try:
119
123
  default_val = [float(default_val[0]), float(default_val[1])]
120
124
  except (ValueError, TypeError):
@@ -135,42 +139,42 @@ class RangeSliderWithTextInput:
135
139
  self.slider_disable = slider_disable
136
140
  self.message = message
137
141
 
138
- self.slider_id = self._make_id('rangeslider')
139
- self.input_start_id = self._make_id('input_start')
140
- self.input_end_id = self._make_id('input_end')
142
+ self.slider_id = self._make_id("rangeslider")
143
+ self.input_start_id = self._make_id("input_start")
144
+ self.input_end_id = self._make_id("input_end")
141
145
 
142
146
  def _make_id(self, subtype: str):
143
147
  """
144
148
  Generate a unique ID dictionary for component sub-elements.
145
-
149
+
146
150
  Combines the base ID with component-specific subtype and property information
147
151
  to create unique identifiers for Dash callbacks and component references.
148
-
152
+
149
153
  Args:
150
154
  subtype (str): The specific component subtype (e.g., 'rangeslider', 'input_start', 'input_end').
151
-
155
+
152
156
  Returns:
153
157
  dict: Complete ID dictionary containing base ID information plus subtype
154
158
  and property specifications.
155
-
159
+
156
160
  Example:
157
161
  >>> component._make_id('rangeslider')
158
162
  {'type': 'parameter', 'index': 0, 'subtype': 'rangeslider', 'property': 'temperature_range'}
159
163
  """
160
- return {**self.id_base, 'subtype': subtype, 'property': self.property_name}
164
+ return {**self.id_base, "subtype": subtype, "property": self.property_name}
161
165
 
162
166
  def _make_range_slider(self):
163
167
  """
164
168
  Create and configure the Dash range slider component.
165
-
169
+
166
170
  Generates a dcc.RangeSlider with the specified range, step size, default values,
167
171
  and tick marks. The range slider provides visual feedback for range selection
168
172
  and is synchronized with the text input components.
169
-
173
+
170
174
  Returns:
171
175
  dash.dcc.RangeSlider: Configured range slider component with ID, value constraints,
172
176
  tick marks, and styling options.
173
-
177
+
174
178
  Note:
175
179
  - Tick marks are generated at intervals specified by mark_interval
176
180
  - updatemode is set to 'mouseup' to reduce callback frequency
@@ -183,22 +187,27 @@ class RangeSliderWithTextInput:
183
187
  value=self.default_val,
184
188
  step=self.step,
185
189
  disabled=self.slider_disable,
186
- marks={int(i): "" for i in np.arange(self.min_val, self.max_val + self.mark_interval, self.mark_interval)},
187
- updatemode='mouseup'
190
+ marks={
191
+ int(i): ""
192
+ for i in np.arange(
193
+ self.min_val, self.max_val + self.mark_interval, self.mark_interval
194
+ )
195
+ },
196
+ updatemode="mouseup",
188
197
  )
189
198
 
190
199
  def _make_start_input(self):
191
200
  """
192
201
  Create and configure the Dash numeric input component for the start value.
193
-
202
+
194
203
  Generates a dcc.Input with number type for precise start value entry.
195
204
  The input is synchronized with the range slider and provides an alternative
196
205
  method for users to specify exact start values.
197
-
206
+
198
207
  Returns:
199
208
  dash.dcc.Input: Configured numeric input component with ID, type,
200
209
  value constraints, styling, and step specification.
201
-
210
+
202
211
  Note:
203
212
  - Input type is set to 'number' for numeric validation
204
213
  - Width and margin styling provides visual alignment
@@ -206,25 +215,25 @@ class RangeSliderWithTextInput:
206
215
  """
207
216
  return ds.dcc.Input(
208
217
  id=self.input_start_id,
209
- type='number',
218
+ type="number",
210
219
  value=self.default_val[0],
211
220
  step=self.step,
212
- style={'width': '80px'},
221
+ style={"width": "80px"},
213
222
  disabled=self.slider_disable,
214
223
  )
215
224
 
216
225
  def _make_end_input(self):
217
226
  """
218
227
  Create and configure the Dash numeric input component for the end value.
219
-
228
+
220
229
  Generates a dcc.Input with number type for precise end value entry.
221
230
  The input is synchronized with the range slider and provides an alternative
222
231
  method for users to specify exact end values.
223
-
232
+
224
233
  Returns:
225
234
  dash.dcc.Input: Configured numeric input component with ID, type,
226
235
  value constraints, styling, and step specification.
227
-
236
+
228
237
  Note:
229
238
  - Input type is set to 'number' for numeric validation
230
239
  - Width and margin styling provides visual alignment
@@ -232,22 +241,22 @@ class RangeSliderWithTextInput:
232
241
  """
233
242
  return ds.dcc.Input(
234
243
  id=self.input_end_id,
235
- type='number',
244
+ type="number",
236
245
  value=self.default_val[1],
237
246
  step=self.step,
238
- style={'margin-left': '10px', 'width': '80px'},
247
+ style={"margin-left": "10px", "width": "80px"},
239
248
  disabled=self.slider_disable,
240
249
  )
241
250
 
242
251
  def __call__(self):
243
252
  """
244
253
  Generate the complete component layout as a callable object.
245
-
254
+
246
255
  Creates and returns a Dash HTML Div containing the title, optional message,
247
256
  range slider, and input components arranged in a cohesive layout. This method allows
248
257
  the class instance to be used as a callable that returns the complete
249
258
  component structure.
250
-
259
+
251
260
  Returns:
252
261
  dash.html.Div: Complete component layout containing:
253
262
  - Title paragraph (conditional based on with_slider_titles)
@@ -255,7 +264,7 @@ class RangeSliderWithTextInput:
255
264
  - Range slider component in a styled container
256
265
  - Start and end numeric input components with labels
257
266
  - Spacing elements (line breaks)
258
-
267
+
259
268
  Note:
260
269
  - Title display is controlled by with_slider_titles attribute
261
270
  - When title is hidden, a non-breaking space maintains layout
@@ -265,50 +274,70 @@ class RangeSliderWithTextInput:
265
274
  - Input labels provide clear indication of start/end values
266
275
  """
267
276
  slider_title = self.title if self.with_slider_titles else "\u00A0"
268
-
277
+
269
278
  # Build the component list
270
279
  components = [
271
- ds.html.P(slider_title, style={'margin-left': '20px', 'margin-bottom': '0px'})
280
+ ds.html.P(
281
+ slider_title, style={"margin-left": "20px", "margin-bottom": "0px"}
282
+ )
272
283
  ]
273
-
284
+
274
285
  # Add optional message if provided
275
286
  if self.message:
276
287
  components.append(
277
- ds.html.P(self.message, style={
278
- 'margin-left': '20px',
279
- 'margin-bottom': '5px',
280
- 'margin-top': '2px',
281
- 'font-size': '0.9em',
282
- 'color': '#666666',
283
- 'font-style': 'italic'
284
- })
288
+ ds.html.P(
289
+ self.message,
290
+ style={
291
+ "margin-left": "20px",
292
+ "margin-bottom": "5px",
293
+ "margin-top": "2px",
294
+ "font-size": "0.9em",
295
+ "color": "#666666",
296
+ "font-style": "italic",
297
+ },
298
+ )
285
299
  )
286
-
300
+
287
301
  # Add slider and input components
288
- components.extend([
289
- ds.html.Div([self._make_range_slider()], style={'margin-bottom': '-18px'}),
290
- ds.html.Div([
291
- ds.html.Span("Start:", style={'margin-left': '20px', 'margin-right': '5px'}),
292
- self._make_start_input(),
293
- ds.html.Span("End:", style={'margin-left': '15px', 'margin-right': '5px'}),
294
- self._make_end_input(),
295
- ], style={'display': 'flex', 'align-items': 'center'}),
296
- ds.html.Br(), ds.html.Br()
297
- ])
298
-
299
- return ds.html.Div(components, style={'width': self.div_width, 'margin-left': '-20px'})
302
+ components.extend(
303
+ [
304
+ ds.html.Div(
305
+ [self._make_range_slider()], style={"margin-bottom": "-18px"}
306
+ ),
307
+ ds.html.Div(
308
+ [
309
+ ds.html.Span(
310
+ "Start:",
311
+ style={"margin-left": "20px", "margin-right": "5px"},
312
+ ),
313
+ self._make_start_input(),
314
+ ds.html.Span(
315
+ "End:", style={"margin-left": "15px", "margin-right": "5px"}
316
+ ),
317
+ self._make_end_input(),
318
+ ],
319
+ style={"display": "flex", "align-items": "center"},
320
+ ),
321
+ ds.html.Br(),
322
+ ds.html.Br(),
323
+ ]
324
+ )
325
+
326
+ return ds.html.Div(
327
+ components, style={"width": self.div_width, "margin-left": "-20px"}
328
+ )
300
329
 
301
330
  @property
302
331
  def components(self):
303
332
  """
304
333
  Get a dictionary mapping component types to their IDs.
305
-
334
+
306
335
  Provides easy access to the IDs of the range slider and input components
307
336
  for use in Dash callbacks and component interactions.
308
-
337
+
309
338
  Returns:
310
339
  dict: Dictionary with component type keys mapping to their ID dictionaries.
311
-
340
+
312
341
  Example:
313
342
  >>> component.components
314
343
  {
@@ -316,27 +345,27 @@ class RangeSliderWithTextInput:
316
345
  'input_start': {'type': 'parameter', 'subtype': 'input_start', 'property': 'temp_range'},
317
346
  'input_end': {'type': 'parameter', 'subtype': 'input_end', 'property': 'temp_range'}
318
347
  }
319
-
348
+
320
349
  Note:
321
350
  This property is particularly useful for setting up Dash callbacks
322
351
  that need to reference the specific component IDs.
323
352
  """
324
353
  return {
325
- 'rangeslider': self.slider_id,
326
- 'input_start': self.input_start_id,
327
- 'input_end': self.input_end_id
354
+ "rangeslider": self.slider_id,
355
+ "input_start": self.input_start_id,
356
+ "input_end": self.input_end_id,
328
357
  }
329
-
358
+
330
359
  def get_value_inputs(self):
331
360
  """
332
361
  Get Input objects for listening to component value changes.
333
-
362
+
334
363
  Returns a list of Dash Input objects that can be used in callbacks to
335
364
  listen for value changes from the range slider or input components.
336
-
365
+
337
366
  Returns:
338
367
  list: List containing [Input(slider_id, 'value'), Input(input_start_id, 'value'), Input(input_end_id, 'value')]
339
-
368
+
340
369
  Example:
341
370
  >>> @app.callback(
342
371
  ... Output('some-output', 'children'),
@@ -346,21 +375,21 @@ class RangeSliderWithTextInput:
346
375
  ... return f"Range: {slider_range or [start_val, end_val]}"
347
376
  """
348
377
  return [
349
- Input(self.slider_id, 'value'),
350
- Input(self.input_start_id, 'value'),
351
- Input(self.input_end_id, 'value')
378
+ Input(self.slider_id, "value"),
379
+ Input(self.input_start_id, "value"),
380
+ Input(self.input_end_id, "value"),
352
381
  ]
353
-
382
+
354
383
  def get_value_outputs(self):
355
384
  """
356
385
  Get Output objects for updating component values.
357
-
386
+
358
387
  Returns a list of Dash Output objects that can be used in callbacks to
359
388
  update the values of the range slider and input components.
360
-
389
+
361
390
  Returns:
362
391
  list: List containing [Output(slider_id, 'value'), Output(input_start_id, 'value'), Output(input_end_id, 'value')]
363
-
392
+
364
393
  Example:
365
394
  >>> @app.callback(
366
395
  ... component.get_value_outputs(),
@@ -370,25 +399,25 @@ class RangeSliderWithTextInput:
370
399
  ... return [new_range, new_range[0], new_range[1]]
371
400
  """
372
401
  return [
373
- Output(self.slider_id, 'value'),
374
- Output(self.input_start_id, 'value'),
375
- Output(self.input_end_id, 'value')
402
+ Output(self.slider_id, "value"),
403
+ Output(self.input_start_id, "value"),
404
+ Output(self.input_end_id, "value"),
376
405
  ]
377
-
378
- def get_pattern_matching_value_inputs(self, property_name='ALL'):
406
+
407
+ def get_pattern_matching_value_inputs(self, property_name="ALL"):
379
408
  """
380
409
  Get pattern-matching Input objects for listening to multiple component instances.
381
-
410
+
382
411
  Returns Input objects using pattern-matching to listen to all components
383
412
  with the specified property name, useful for multi-component callbacks.
384
-
413
+
385
414
  Args:
386
415
  property_name: The property to match. Use 'ALL' for all properties,
387
416
  or specify a specific property name.
388
-
417
+
389
418
  Returns:
390
419
  list: List containing pattern-matching Input objects
391
-
420
+
392
421
  Example:
393
422
  >>> @app.callback(
394
423
  ... Output('summary', 'children'),
@@ -397,30 +426,42 @@ class RangeSliderWithTextInput:
397
426
  ... def update_summary(slider_ranges, start_values, end_values):
398
427
  ... return f"Total range sliders: {len(slider_ranges)}"
399
428
  """
400
- slider_pattern = {'type': 'parameter', 'subtype': 'rangeslider', 'property': property_name}
401
- start_pattern = {'type': 'parameter', 'subtype': 'input_start', 'property': property_name}
402
- end_pattern = {'type': 'parameter', 'subtype': 'input_end', 'property': property_name}
403
-
429
+ slider_pattern = {
430
+ "type": "parameter",
431
+ "subtype": "rangeslider",
432
+ "property": property_name,
433
+ }
434
+ start_pattern = {
435
+ "type": "parameter",
436
+ "subtype": "input_start",
437
+ "property": property_name,
438
+ }
439
+ end_pattern = {
440
+ "type": "parameter",
441
+ "subtype": "input_end",
442
+ "property": property_name,
443
+ }
444
+
404
445
  return [
405
- Input(slider_pattern, 'value'),
406
- Input(start_pattern, 'value'),
407
- Input(end_pattern, 'value')
446
+ Input(slider_pattern, "value"),
447
+ Input(start_pattern, "value"),
448
+ Input(end_pattern, "value"),
408
449
  ]
409
-
410
- def get_pattern_matching_value_outputs(self, property_name='ALL'):
450
+
451
+ def get_pattern_matching_value_outputs(self, property_name="ALL"):
411
452
  """
412
453
  Get pattern-matching Output objects for updating multiple component instances.
413
-
454
+
414
455
  Returns Output objects using pattern-matching to update all components
415
456
  with the specified property name.
416
-
457
+
417
458
  Args:
418
459
  property_name: The property to match. Use 'ALL' for all properties,
419
460
  or specify a specific property name.
420
-
461
+
421
462
  Returns:
422
463
  list: List containing pattern-matching Output objects
423
-
464
+
424
465
  Example:
425
466
  >>> @app.callback(
426
467
  ... component.get_pattern_matching_value_outputs('ALL'),
@@ -431,70 +472,80 @@ class RangeSliderWithTextInput:
431
472
  ... return [[[0, 100]] * len(ranges), [0] * len(starts), [100] * len(ends)]
432
473
  ... return [dash.no_update] * 3
433
474
  """
434
- slider_pattern = {'type': 'parameter', 'subtype': 'rangeslider', 'property': property_name}
435
- start_pattern = {'type': 'parameter', 'subtype': 'input_start', 'property': property_name}
436
- end_pattern = {'type': 'parameter', 'subtype': 'input_end', 'property': property_name}
437
-
475
+ slider_pattern = {
476
+ "type": "parameter",
477
+ "subtype": "rangeslider",
478
+ "property": property_name,
479
+ }
480
+ start_pattern = {
481
+ "type": "parameter",
482
+ "subtype": "input_start",
483
+ "property": property_name,
484
+ }
485
+ end_pattern = {
486
+ "type": "parameter",
487
+ "subtype": "input_end",
488
+ "property": property_name,
489
+ }
490
+
438
491
  return [
439
- Output(slider_pattern, 'value'),
440
- Output(start_pattern, 'value'),
441
- Output(end_pattern, 'value')
492
+ Output(slider_pattern, "value"),
493
+ Output(start_pattern, "value"),
494
+ Output(end_pattern, "value"),
442
495
  ]
443
-
496
+
444
497
  def _validate_and_clamp_range(self, range_values):
445
498
  """
446
499
  Validate and clamp range values to the component's valid domain.
447
-
500
+
448
501
  Ensures that range values are within the min_val to max_val domain,
449
502
  maintains proper ordering (start <= end), and handles invalid values.
450
-
503
+
451
504
  Args:
452
505
  range_values (list): The range values to validate and clamp [start, end].
453
-
506
+
454
507
  Returns:
455
508
  list[float]: The clamped range values within the valid domain,
456
509
  with proper ordering maintained.
457
510
  """
458
511
  if not isinstance(range_values, list) or len(range_values) != 2:
459
512
  return [self.min_val, self.max_val]
460
-
513
+
461
514
  try:
462
515
  start, end = float(range_values[0]), float(range_values[1])
463
-
516
+
464
517
  # Clamp to valid domain
465
518
  start = max(self.min_val, min(self.max_val, start))
466
519
  end = max(self.min_val, min(self.max_val, end))
467
-
520
+
468
521
  # Ensure proper ordering
469
522
  if start > end:
470
523
  start, end = end, start
471
-
524
+
472
525
  return [start, end]
473
-
526
+
474
527
  except (ValueError, TypeError):
475
528
  return [self.min_val, self.max_val]
476
-
529
+
477
530
  def _validate_and_clamp_value(self, value):
478
531
  """
479
532
  Validate and clamp a single value to the component's valid range.
480
-
533
+
481
534
  Ensures that any input value is within the min_val to max_val range
482
535
  and handles None values appropriately.
483
-
536
+
484
537
  Args:
485
538
  value: The value to validate and clamp.
486
-
539
+
487
540
  Returns:
488
541
  float: The clamped value within the valid range, or min_val
489
542
  if the input value is invalid.
490
543
  """
491
544
  if value is None:
492
545
  return self.min_val
493
-
546
+
494
547
  try:
495
548
  numeric_value = float(value)
496
549
  return max(self.min_val, min(self.max_val, numeric_value))
497
550
  except (ValueError, TypeError):
498
551
  return self.min_val
499
-
500
-