ansys-solutions-dash-super-components 0.2.dev7__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.
Files changed (26) hide show
  1. ansys/solutions/dash_super_components/__init__.py +44 -0
  2. ansys/solutions/dash_super_components/assets/__init__.py +20 -0
  3. ansys/solutions/dash_super_components/assets/dashAgGridComponentFunctions.js +71 -0
  4. ansys/solutions/dash_super_components/authenticator.py +277 -0
  5. ansys/solutions/dash_super_components/dual_input_range_slider.py +397 -0
  6. ansys/solutions/dash_super_components/folder_selector.py +830 -0
  7. ansys/solutions/dash_super_components/input_form.py +908 -0
  8. ansys/solutions/dash_super_components/input_row_array.py +368 -0
  9. ansys/solutions/dash_super_components/logs_supervisor.py +887 -0
  10. ansys/solutions/dash_super_components/transaction_method_status_badge.py +366 -0
  11. ansys/solutions/dash_super_components/transaction_supervisor.py +587 -0
  12. ansys/solutions/dash_super_components/tree.py +396 -0
  13. ansys/solutions/dash_super_components/utils/__init__.py +18 -0
  14. ansys/solutions/dash_super_components/utils/aio_ids.py +83 -0
  15. ansys/solutions/dash_super_components/utils/api_requests.py +69 -0
  16. ansys/solutions/dash_super_components/utils/assets_server.py +52 -0
  17. ansys/solutions/dash_super_components/utils/config.py +77 -0
  18. ansys/solutions/dash_super_components/utils/logs_parser.py +151 -0
  19. ansys/solutions/dash_super_components/utils/path_validator.py +163 -0
  20. ansys/solutions/dash_super_components/utils/status_badge_properties.py +60 -0
  21. ansys/solutions/dash_super_components/utils/svg_icons.py +395 -0
  22. ansys_solutions_dash_super_components-0.2.dev7.dist-info/METADATA +169 -0
  23. ansys_solutions_dash_super_components-0.2.dev7.dist-info/RECORD +26 -0
  24. ansys_solutions_dash_super_components-0.2.dev7.dist-info/WHEEL +4 -0
  25. ansys_solutions_dash_super_components-0.2.dev7.dist-info/licenses/AUTHORS +12 -0
  26. ansys_solutions_dash_super_components-0.2.dev7.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,397 @@
1
+ # Copyright (C) 2026 ANSYS, Inc. and/or its affiliates.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+
18
+ """Provides a Dash component combining a range slider with number inputs for upper/lower limits."""
19
+
20
+ import copy
21
+ from typing import Any
22
+ import uuid
23
+
24
+ try:
25
+ # dash >=3.2.0
26
+ from dash import NoUpdate
27
+ except ImportError:
28
+ # dash >=2.18.2, <3.2.0
29
+ from dash._callback import NoUpdate # pyright: ignore[reportPrivateImportUsage]
30
+ from dash.exceptions import PreventUpdate
31
+
32
+ try:
33
+ # dash-extensions < 2.0.5
34
+ from dash_extensions.enrich import _Wildcard # pyright: ignore[reportAttributeAccessIssue]
35
+ except ImportError:
36
+ # dash-extensions >= 2.0.5
37
+ from dash_extensions.enrich import (
38
+ Wildcard as _Wildcard, # pyright: ignore[reportAttributeAccessIssue]
39
+ )
40
+ from dash_extensions.enrich import (
41
+ MATCH,
42
+ Input,
43
+ Output,
44
+ State,
45
+ callback,
46
+ callback_context,
47
+ html,
48
+ no_update,
49
+ )
50
+ import dash_mantine_components as dmc
51
+
52
+ from ansys.solutions.dash_super_components.utils.aio_ids import AIOIds
53
+
54
+
55
+ class DualInputRangeSlider(html.Div):
56
+ """
57
+ A Dash component combining a range slider with two number inputs for precise range control.
58
+
59
+ DualInputRangeSlider is based on the Dash All-in-One (AIO) component pattern. It provides
60
+ a bidirectional interface: dragging the slider updates both number inputs, and typing into
61
+ either input updates the slider. When a new lower bound would exceed the current upper
62
+ bound (or vice versa), the other bound is automatically adjusted to maintain validity.
63
+
64
+ Parameters
65
+ ----------
66
+ min : int or float
67
+ The minimum value of the slider.
68
+ max : int or float
69
+ The maximum value of the slider. Must be greater than ``min``.
70
+ step : int or float, optional
71
+ Step size for the slider and number inputs. Must be positive and
72
+ ``<= (max - min)``. Default is ``(max - min) / 10.0``.
73
+ decimal_scale : int, optional
74
+ Number of decimal places displayed in the number inputs. Must be
75
+ non-negative. Default is ``1``.
76
+ value : list of float, optional
77
+ Initial ``[lower, upper]`` values. Both must lie in ``[min, max]`` with
78
+ ``lower <= upper``. Defaults to ``[min, max]``.
79
+ slider_props : dict, optional
80
+ Additional properties forwarded to :class:`dmc.RangeSlider`. The keys
81
+ ``min``, ``max``, ``step``, ``value``, and ``minRange`` are controlled by
82
+ the component and will be overridden. Default style: ``{"width": "20%"}``.
83
+ lower_bound_input_props : dict, optional
84
+ Additional properties forwarded to the lower bound :class:`dmc.NumberInput`.
85
+ The keys ``min``, ``max``, ``step``, ``value``, and ``decimalScale`` are
86
+ controlled by the component and will be overridden.
87
+ Default: label ``"Min"``, variant ``"filled"``, style ``{"width": "5%"}``.
88
+ upper_bound_input_props : dict, optional
89
+ Additional properties forwarded to the upper bound :class:`dmc.NumberInput`.
90
+ The keys ``min``, ``max``, ``step``, ``value``, and ``decimalScale`` are
91
+ controlled by the component and will be overridden.
92
+ Default: label ``"Max"``, variant ``"filled"``, style ``{"width": "5%"}``.
93
+ aio_id : str, optional
94
+ Unique identifier for this component instance. A UUID is generated when
95
+ not provided.
96
+ """
97
+
98
+ class DualInputRangeSliderIds(AIOIds):
99
+ """Provides IDs for the subcomponents of :class:`DualInputRangeSlider`."""
100
+
101
+ @classmethod
102
+ def slider(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
103
+ """Return the ID for the slider component.
104
+
105
+ Parameters
106
+ ----------
107
+ aio_id : str or _Wildcard
108
+ The component's unique identifier.
109
+
110
+ Returns
111
+ -------
112
+ dict[str, Any]
113
+ The unique ID dictionary for the slider subcomponent.
114
+ """
115
+ return cls.make_id_dict("dual-input-range-slider", "slider", aio_id)
116
+
117
+ @classmethod
118
+ def lower_bound_input(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
119
+ """Return the ID for the lower bound input component.
120
+
121
+ Parameters
122
+ ----------
123
+ aio_id : str or _Wildcard
124
+ The component's unique identifier.
125
+
126
+ Returns
127
+ -------
128
+ dict[str, Any]
129
+ The unique ID dictionary for the lower bound input subcomponent.
130
+ """
131
+ return cls.make_id_dict("dual-input-range-slider", "lower-bound-input", aio_id)
132
+
133
+ @classmethod
134
+ def upper_bound_input(cls, aio_id: str | _Wildcard) -> dict[str, Any]:
135
+ """Return the ID for the upper bound input component.
136
+
137
+ Parameters
138
+ ----------
139
+ aio_id : str or _Wildcard
140
+ The component's unique identifier.
141
+
142
+ Returns
143
+ -------
144
+ dict[str, Any]
145
+ The unique ID dictionary for the upper bound input subcomponent.
146
+ """
147
+ return cls.make_id_dict("dual-input-range-slider", "upper-bound-input", aio_id)
148
+
149
+ ids = DualInputRangeSliderIds
150
+
151
+ def __init__(
152
+ self,
153
+ min: float, # noqa: A002 - min is the conventional name for this parameter in DMC
154
+ max: float, # noqa: A002 - max is the conventional name for this parameter in DMC
155
+ step: float | None = None,
156
+ decimal_scale: int | None = None,
157
+ value: list[float] | None = None,
158
+ slider_props: dict[str, Any] | None = None,
159
+ lower_bound_input_props: dict[str, Any] | None = None,
160
+ upper_bound_input_props: dict[str, Any] | None = None,
161
+ aio_id: str | None = None,
162
+ ):
163
+ # First, check if the mandatory inputs min and max are valid as the defaults calculation
164
+ # is based on them
165
+ self._check_mandatory_inputs(min, max)
166
+
167
+ # Set default values for the optional inputs if not given
168
+ if slider_props is None:
169
+ slider_props = {}
170
+ if lower_bound_input_props is None:
171
+ lower_bound_input_props = {}
172
+ if upper_bound_input_props is None:
173
+ upper_bound_input_props = {}
174
+ if aio_id is None:
175
+ aio_id = str(uuid.uuid4())
176
+ if value is None:
177
+ value = [min, max]
178
+ if decimal_scale is None:
179
+ decimal_scale = 1
180
+ if step is None:
181
+ step = (max - min) / 10.0
182
+
183
+ # check if the optional inputs and defaults are valid
184
+ self._check_optional_inputs_and_defaults(
185
+ checked_min=min,
186
+ checked_max=max,
187
+ step=step,
188
+ decimal_scale=decimal_scale,
189
+ value=value,
190
+ )
191
+
192
+ self._slider_props = copy.deepcopy(slider_props)
193
+ self._lower_bound_input_props = copy.deepcopy(lower_bound_input_props)
194
+ self._upper_bound_input_props = copy.deepcopy(upper_bound_input_props)
195
+ self._min = min
196
+ self._max = max
197
+ self._step = step
198
+ self._value = value
199
+ self._decimal_scale = decimal_scale
200
+ self.aio_id = aio_id
201
+
202
+ default_slider_props: dict[str, Any] = {
203
+ "style": {"width": "60%"},
204
+ }
205
+
206
+ controlled_slider_props: dict[str, Any] = {
207
+ "value": self._value,
208
+ "min": self._min,
209
+ "max": self._max,
210
+ "step": self._step,
211
+ "minRange": 0, # Ensure that lower bound<=upper bound, overwrite dmc default behavior
212
+ }
213
+ self._populate_with_defaults(self._slider_props, default_slider_props)
214
+ self._enforce_controlled_props(self._slider_props, controlled_slider_props)
215
+
216
+ default_lower_bound_input_props: dict[str, Any] = {
217
+ "label": "Min",
218
+ "variant": "filled",
219
+ "style": {"width": "15%"},
220
+ }
221
+
222
+ controlled_lower_bound_input_props: dict[str, Any] = {
223
+ "value": self._value[0],
224
+ "min": self._min,
225
+ "max": self._max,
226
+ "step": self._step,
227
+ "decimalScale": self._decimal_scale,
228
+ }
229
+ self._populate_with_defaults(self._lower_bound_input_props, default_lower_bound_input_props)
230
+ self._enforce_controlled_props(
231
+ self._lower_bound_input_props, controlled_lower_bound_input_props
232
+ )
233
+
234
+ default_upper_bound_input_props: dict[str, Any] = {
235
+ "label": "Max",
236
+ "variant": "filled",
237
+ "style": {"width": "15%"},
238
+ }
239
+
240
+ controlled_upper_bound_input_props: dict[str, Any] = {
241
+ "value": self._value[1],
242
+ "min": self._min,
243
+ "max": self._max,
244
+ "step": self._step,
245
+ "decimalScale": self._decimal_scale,
246
+ }
247
+ self._populate_with_defaults(self._upper_bound_input_props, default_upper_bound_input_props)
248
+ self._enforce_controlled_props(
249
+ self._upper_bound_input_props, controlled_upper_bound_input_props
250
+ )
251
+
252
+ super().__init__(
253
+ [
254
+ html.Div(
255
+ dmc.Group(
256
+ [
257
+ dmc.NumberInput(
258
+ id=self.ids.lower_bound_input(self.aio_id),
259
+ **self._lower_bound_input_props,
260
+ ),
261
+ dmc.RangeSlider(
262
+ id=self.ids.slider(self.aio_id),
263
+ **self._slider_props,
264
+ ),
265
+ dmc.NumberInput(
266
+ id=self.ids.upper_bound_input(self.aio_id),
267
+ **self._upper_bound_input_props,
268
+ ),
269
+ ],
270
+ justify="center",
271
+ gap="xs",
272
+ ),
273
+ ),
274
+ ],
275
+ )
276
+
277
+ def _check_mandatory_inputs(
278
+ self,
279
+ input_min: int | float,
280
+ input_max: int | float,
281
+ ) -> None:
282
+ if input_max is None or input_min is None: # type: ignore - deliberately checking input value types
283
+ raise ValueError("Both 'min' and 'max' parameters must be provided.")
284
+
285
+ if not isinstance(input_min, int | float): # type: ignore - deliberately checking input value types
286
+ raise ValueError("'min' parameter must be a number.")
287
+
288
+ if not isinstance(input_max, int | float): # type: ignore - deliberately checking input value types
289
+ raise ValueError("'max' parameter must be a number.")
290
+
291
+ if input_max <= input_min:
292
+ raise ValueError("'max' parameter must be greater than 'min' parameter.")
293
+
294
+ def _check_optional_inputs_and_defaults(
295
+ self,
296
+ checked_min: int | float,
297
+ checked_max: int | float,
298
+ step: int | float,
299
+ decimal_scale: int,
300
+ value: list[int | float],
301
+ ) -> None:
302
+ self._check_step(checked_min, checked_max, step)
303
+ self._check_decimal_scale(decimal_scale)
304
+ self._check_value(checked_min, checked_max, value)
305
+
306
+ def _check_step(
307
+ self, checked_min: int | float, checked_max: int | float, step: int | float
308
+ ) -> None:
309
+ if not isinstance(step, int | float): # type: ignore - deliberately checking input value types
310
+ raise ValueError("'step' parameter must be a number.")
311
+ if step <= 0:
312
+ raise ValueError("'step' parameter must be positive.")
313
+ if step > (checked_max - checked_min):
314
+ raise ValueError(
315
+ "'step' parameter must be less than or equal to the range defined by 'min' "
316
+ "and 'max'."
317
+ )
318
+
319
+ def _check_decimal_scale(self, decimal_scale: int) -> None:
320
+ if not isinstance(decimal_scale, int): # type: ignore - checking inputs
321
+ raise ValueError("'decimal_scale' parameter must be an integer.")
322
+ if decimal_scale < 0:
323
+ raise ValueError("'decimal_scale' parameter must be non-negative.")
324
+
325
+ def _check_value(
326
+ self, checked_min: int | float, checked_max: int | float, value: list[int | float]
327
+ ) -> None:
328
+ if len(value) != 2:
329
+ raise ValueError("'value' parameter must be a list of two elements.")
330
+ if not all(isinstance(v, int | float) for v in value): # type: ignore - checking inputs
331
+ raise ValueError("'value' parameter must be a list of numbers.")
332
+ if not (checked_min <= value[0] <= checked_max) or not (
333
+ checked_min <= value[1] <= checked_max
334
+ ):
335
+ raise ValueError(
336
+ "'value' parameter elements must each be within the range defined by 'min' "
337
+ "and 'max'.",
338
+ )
339
+ if value[0] > value[1]:
340
+ raise ValueError(
341
+ "The first element of the 'value' parameter must be less than or equal to the "
342
+ "second element."
343
+ )
344
+
345
+ def _populate_with_defaults(
346
+ self, properties: dict[str, Any], default_properties: dict[str, Any]
347
+ ) -> None:
348
+ for key, val in default_properties.items():
349
+ if key not in properties:
350
+ properties[key] = val
351
+
352
+ def _enforce_controlled_props(
353
+ self, properties: dict[str, Any], controlled_properties: dict[str, Any]
354
+ ) -> None:
355
+ for key, val in controlled_properties.items():
356
+ properties[key] = val
357
+
358
+ @staticmethod
359
+ @callback(
360
+ Output(ids.slider(MATCH), "value"),
361
+ Output(ids.lower_bound_input(MATCH), "value"),
362
+ Output(ids.upper_bound_input(MATCH), "value"),
363
+ Input(ids.slider(MATCH), "value"),
364
+ Input(ids.lower_bound_input(MATCH), "value"),
365
+ Input(ids.upper_bound_input(MATCH), "value"),
366
+ State(ids.slider(MATCH), "id"),
367
+ State(ids.lower_bound_input(MATCH), "id"),
368
+ State(ids.upper_bound_input(MATCH), "id"),
369
+ )
370
+ def update_slider_or_inputs_values(
371
+ slider_value: list[float | int],
372
+ lower_bound_value: float | int,
373
+ upper_bound_value: float | int,
374
+ slider_id: dict[str, Any],
375
+ lower_bound_id: dict[str, Any],
376
+ upper_bound_id: dict[str, Any],
377
+ ) -> tuple[list[float] | NoUpdate, float | NoUpdate, float | NoUpdate]:
378
+ """Update slider or inputs values."""
379
+ ctx = callback_context
380
+ if not ctx.triggered_id: # type: ignore - Dash typing issue
381
+ raise PreventUpdate
382
+
383
+ trigger_id: dict[str, Any] = ctx.triggered_id # type: ignore - Dash typing issue
384
+ if trigger_id == slider_id:
385
+ return no_update, slider_value[0], slider_value[1]
386
+ elif trigger_id == lower_bound_id:
387
+ # make sure that minRange=0 is respected (i.e., lower bound <= upper bound)
388
+ # if not respected, shift upper bound (comparable to RangeSlider behavior)
389
+ valid_upper_bound_value = max(lower_bound_value, slider_value[1])
390
+ return [lower_bound_value, valid_upper_bound_value], no_update, valid_upper_bound_value
391
+ elif trigger_id == upper_bound_id:
392
+ # make sure that minRange=0 is respected (i.e., lower bound <= upper bound)
393
+ # if not respected, shift lower bound (comparable to RangeSlider behavior)
394
+ valid_lower_bound_value = min(upper_bound_value, slider_value[0])
395
+ return [valid_lower_bound_value, upper_bound_value], valid_lower_bound_value, no_update
396
+ else:
397
+ raise PreventUpdate