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.
- ansys/solutions/dash_super_components/__init__.py +44 -0
- ansys/solutions/dash_super_components/assets/__init__.py +20 -0
- ansys/solutions/dash_super_components/assets/dashAgGridComponentFunctions.js +71 -0
- ansys/solutions/dash_super_components/authenticator.py +277 -0
- ansys/solutions/dash_super_components/dual_input_range_slider.py +397 -0
- ansys/solutions/dash_super_components/folder_selector.py +830 -0
- ansys/solutions/dash_super_components/input_form.py +908 -0
- ansys/solutions/dash_super_components/input_row_array.py +368 -0
- ansys/solutions/dash_super_components/logs_supervisor.py +887 -0
- ansys/solutions/dash_super_components/transaction_method_status_badge.py +366 -0
- ansys/solutions/dash_super_components/transaction_supervisor.py +587 -0
- ansys/solutions/dash_super_components/tree.py +396 -0
- ansys/solutions/dash_super_components/utils/__init__.py +18 -0
- ansys/solutions/dash_super_components/utils/aio_ids.py +83 -0
- ansys/solutions/dash_super_components/utils/api_requests.py +69 -0
- ansys/solutions/dash_super_components/utils/assets_server.py +52 -0
- ansys/solutions/dash_super_components/utils/config.py +77 -0
- ansys/solutions/dash_super_components/utils/logs_parser.py +151 -0
- ansys/solutions/dash_super_components/utils/path_validator.py +163 -0
- ansys/solutions/dash_super_components/utils/status_badge_properties.py +60 -0
- ansys/solutions/dash_super_components/utils/svg_icons.py +395 -0
- ansys_solutions_dash_super_components-0.2.dev7.dist-info/METADATA +169 -0
- ansys_solutions_dash_super_components-0.2.dev7.dist-info/RECORD +26 -0
- ansys_solutions_dash_super_components-0.2.dev7.dist-info/WHEEL +4 -0
- ansys_solutions_dash_super_components-0.2.dev7.dist-info/licenses/AUTHORS +12 -0
- 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
|