dara-components 1.8.5__py3-none-any.whl → 1.22.1__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.
- dara/components/__init__.py +18 -0
- dara/components/_assets/__init__.py +30 -0
- dara/components/_assets/auto_js/.gitkeep +0 -0
- dara/components/_assets/auto_js/dara.components.css +1494 -0
- dara/components/_assets/auto_js/dara.components.umd.js +182837 -0
- dara/components/_assets/common/bokeh-3.1.1.min.js +690 -0
- dara/components/_assets/common/bokeh-api-3.1.1.min.js +60 -0
- dara/components/_assets/common/bokeh-gl-3.1.1.min.js +67 -0
- dara/components/_assets/common/bokeh-mathjax-3.1.1.min.js +329 -0
- dara/components/_assets/common/bokeh-tables-3.1.1.min.js +132 -0
- dara/components/_assets/common/bokeh-widgets-3.1.1.min.js +129 -0
- dara/components/_assets/common/pixi-filters.min.js +17 -0
- dara/components/_assets/common/pixi.min.js +2214 -0
- dara/components/_assets/common/pixi_viewport.js +1 -0
- dara/components/_assets/common/plotly.min.js +8 -0
- dara/components/common/__init__.py +11 -2
- dara/components/common/accordion.py +20 -26
- dara/components/common/anchor.py +9 -10
- dara/components/common/base_component.py +23 -36
- dara/components/common/bullet_list.py +1 -3
- dara/components/common/button.py +35 -26
- dara/components/common/button_bar.py +25 -20
- dara/components/common/card.py +4 -5
- dara/components/common/carousel.py +9 -9
- dara/components/common/checkbox_group.py +26 -19
- dara/components/common/code.py +8 -5
- dara/components/common/component_select_list.py +9 -13
- dara/components/common/datepicker.py +16 -16
- dara/components/common/dropdown_menu.py +161 -0
- dara/components/common/dropzone.py +42 -33
- dara/components/common/form.py +5 -7
- dara/components/common/form_page.py +4 -6
- dara/components/common/grid.py +21 -18
- dara/components/common/heading.py +5 -4
- dara/components/common/icon.py +1 -3
- dara/components/common/if_cmp.py +23 -17
- dara/components/common/image.py +2 -2
- dara/components/common/input.py +9 -11
- dara/components/common/label.py +13 -14
- dara/components/common/markdown.py +3 -5
- dara/components/common/modal.py +2 -2
- dara/components/common/overlay.py +8 -14
- dara/components/common/paragraph.py +2 -2
- dara/components/common/progress_bar.py +6 -8
- dara/components/common/radio_group.py +38 -21
- dara/components/common/select.py +33 -30
- dara/components/common/slider.py +74 -29
- dara/components/common/spacer.py +4 -6
- dara/components/common/stack.py +7 -4
- dara/components/common/switch.py +6 -8
- dara/components/common/tabbed_card.py +8 -11
- dara/components/common/table.py +224 -73
- dara/components/common/text.py +7 -9
- dara/components/common/textarea.py +7 -7
- dara/components/common/time_utils.py +2 -5
- dara/components/common/tooltip.py +4 -6
- dara/components/common/utils.py +29 -35
- dara/components/graphs/__init__.py +1 -0
- dara/components/graphs/components/base_graph_component.py +34 -22
- dara/components/graphs/components/causal_graph_viewer.py +13 -15
- dara/components/graphs/components/edge_encoder.py +49 -26
- dara/components/graphs/components/node_hierarchy_builder.py +17 -16
- dara/components/graphs/definitions.py +27 -20
- dara/components/graphs/graph_layout.py +90 -53
- dara/components/plotting/__init__.py +2 -1
- dara/components/plotting/bokeh/bokeh.py +7 -10
- dara/components/plotting/bokeh/utils.py +5 -3
- dara/components/plotting/plotly/plotly.py +24 -19
- dara/components/plotting/plotly/themes.py +7 -5
- dara/components/smart/__init__.py +7 -1
- dara/components/smart/chat/chat.py +7 -8
- dara/components/smart/chat/config.py +1 -1
- dara/components/smart/chat/types.py +4 -6
- dara/components/smart/code_editor/code_editor.py +18 -4
- dara/components/smart/code_editor/util.py +11 -11
- dara/components/smart/data_slicer/__init__.py +4 -0
- dara/components/smart/data_slicer/data_slicer.py +14 -18
- dara/components/smart/data_slicer/data_slicer_modal.py +4 -6
- dara/components/smart/data_slicer/extension/data_slicer_filter.py +3 -4
- dara/components/smart/data_slicer/extension/filter_status_button.py +1 -3
- dara/components/smart/data_slicer/utils/core.py +23 -23
- dara/components/smart/data_slicer/utils/data_preview.py +1 -3
- dara/components/smart/data_slicer/utils/plotting.py +8 -6
- dara/components/smart/hierarchy.py +9 -10
- {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/METADATA +7 -7
- dara_components-1.22.1.dist-info/RECORD +100 -0
- {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/WHEEL +1 -1
- dara_components-1.22.1.dist-info/entry_points.txt +3 -0
- dara/components/umd/dara.components.umd.js +0 -396288
- dara/components/umd/style.css +0 -745
- dara_components-1.8.5.dist-info/RECORD +0 -86
- {dara_components-1.8.5.dist-info → dara_components-1.22.1.dist-info}/LICENSE +0 -0
dara/components/common/table.py
CHANGED
|
@@ -15,22 +15,23 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from
|
|
18
|
+
from collections.abc import Sequence
|
|
19
19
|
from enum import Enum
|
|
20
|
-
from typing import
|
|
20
|
+
from typing import Any, ClassVar, Literal
|
|
21
21
|
|
|
22
|
-
from
|
|
22
|
+
from fastapi.encoders import jsonable_encoder
|
|
23
|
+
from pandas import DataFrame
|
|
24
|
+
from pydantic import ConfigDict, Field, SerializerFunctionWrapHandler, ValidationInfo, field_serializer, field_validator
|
|
23
25
|
|
|
24
26
|
from dara.components.common.base_component import ContentComponent
|
|
25
|
-
from dara.components.common.time_utils import coerce_to_timemilli
|
|
26
27
|
from dara.core.base_definitions import Action
|
|
27
|
-
from dara.core.
|
|
28
|
+
from dara.core.base_definitions import DaraBaseModel as BaseModel
|
|
28
29
|
from dara.core.interactivity import (
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
UrlVariable,
|
|
30
|
+
AnyVariable,
|
|
31
|
+
ClientVariable,
|
|
32
32
|
Variable,
|
|
33
33
|
)
|
|
34
|
+
from dara.core.logging import dev_logger
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
class TableFormatterType(Enum):
|
|
@@ -47,6 +48,9 @@ class TableFormatterType(Enum):
|
|
|
47
48
|
THRESHOLD = 'threshold'
|
|
48
49
|
|
|
49
50
|
|
|
51
|
+
TFormatterType = type[TableFormatterType]
|
|
52
|
+
|
|
53
|
+
|
|
50
54
|
class TableFormatterCompareCondition(Enum):
|
|
51
55
|
EQUAL = 'equal'
|
|
52
56
|
|
|
@@ -58,6 +62,9 @@ class TableFilter(Enum):
|
|
|
58
62
|
DATETIME = 'datetime'
|
|
59
63
|
|
|
60
64
|
|
|
65
|
+
TFilterType = type[TableFilter]
|
|
66
|
+
|
|
67
|
+
|
|
61
68
|
class Column(BaseModel):
|
|
62
69
|
"""
|
|
63
70
|
Internal representation of a Table column, required for serialization to work correctly
|
|
@@ -261,7 +268,7 @@ class Column(BaseModel):
|
|
|
261
268
|
|
|
262
269
|
from pandas import DataFrame
|
|
263
270
|
from dara.components.common import Table
|
|
264
|
-
from dara.core import
|
|
271
|
+
from dara.core import ServerVariable
|
|
265
272
|
|
|
266
273
|
Table.column(
|
|
267
274
|
col_id='col1',
|
|
@@ -285,7 +292,7 @@ class Column(BaseModel):
|
|
|
285
292
|
.
|
|
286
293
|
.
|
|
287
294
|
])
|
|
288
|
-
data_var =
|
|
295
|
+
data_var = ServerVariable(data)
|
|
289
296
|
|
|
290
297
|
```
|
|
291
298
|
|
|
@@ -373,28 +380,35 @@ class Column(BaseModel):
|
|
|
373
380
|
:param type: Optional prop to specify type of column, used to determine e.g. filter type. If not specified, inferred from formatters
|
|
374
381
|
"""
|
|
375
382
|
|
|
376
|
-
align:
|
|
383
|
+
align: str | None = None
|
|
377
384
|
col_id: str
|
|
378
|
-
unique_items:
|
|
379
|
-
filter:
|
|
380
|
-
formatter:
|
|
381
|
-
label:
|
|
382
|
-
sticky:
|
|
383
|
-
tooltip:
|
|
384
|
-
width:
|
|
385
|
-
type:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
385
|
+
unique_items: list[str] | None = None
|
|
386
|
+
filter: TableFilter | None = None
|
|
387
|
+
formatter: dict | None = None
|
|
388
|
+
label: str | None = Field(default=None, validate_default=True) # mimics always=True in pydantic v1
|
|
389
|
+
sticky: str | None = None
|
|
390
|
+
tooltip: str | None = None
|
|
391
|
+
width: int | str | None = None
|
|
392
|
+
type: (
|
|
393
|
+
Literal['number']
|
|
394
|
+
| Literal['string']
|
|
395
|
+
| Literal['datetime']
|
|
396
|
+
| Literal['datetime64[ns]']
|
|
397
|
+
| Literal['datetime64[ms]']
|
|
398
|
+
| Literal['datetime64[s]']
|
|
399
|
+
| None
|
|
400
|
+
) = None
|
|
401
|
+
|
|
402
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
403
|
+
|
|
404
|
+
@field_validator('label')
|
|
391
405
|
@classmethod
|
|
392
|
-
def validate_label(cls, value,
|
|
406
|
+
def validate_label(cls, value, info: ValidationInfo):
|
|
393
407
|
if value is None:
|
|
394
|
-
return
|
|
408
|
+
return info.data.get('col_id')
|
|
395
409
|
return value
|
|
396
410
|
|
|
397
|
-
@
|
|
411
|
+
@field_validator('formatter')
|
|
398
412
|
@classmethod
|
|
399
413
|
def vaildate_formatter_dict(cls, formatter):
|
|
400
414
|
"""
|
|
@@ -405,13 +419,12 @@ class Column(BaseModel):
|
|
|
405
419
|
formatter_type = formatter.get('type')
|
|
406
420
|
if formatter_type not in TableFormatterType:
|
|
407
421
|
raise ValueError(
|
|
408
|
-
f
|
|
422
|
+
f'Invalid formatter type: {formatter.get("type")}, accepted values {list(TableFormatterType)}'
|
|
409
423
|
)
|
|
410
424
|
if formatter_type in (TableFormatterType.NUMBER, TableFormatterType.PERCENT):
|
|
411
425
|
precision = formatter.get('precision')
|
|
412
|
-
if precision is not None:
|
|
413
|
-
|
|
414
|
-
raise ValueError(f'Invalid precision value: {precision}, must be positive integer')
|
|
426
|
+
if precision is not None and (not isinstance(precision, int) or precision <= 0):
|
|
427
|
+
raise ValueError(f'Invalid precision value: {precision}, must be positive integer')
|
|
415
428
|
elif formatter_type == TableFormatterType.NUMBER_INTL:
|
|
416
429
|
locales = formatter.get('locales')
|
|
417
430
|
if not isinstance(locales, str) or not (
|
|
@@ -445,9 +458,9 @@ class Column(BaseModel):
|
|
|
445
458
|
)
|
|
446
459
|
lower_bound = threshold.get('bounds')[0]
|
|
447
460
|
upper_bound = threshold.get('bounds')[1]
|
|
448
|
-
if not (isinstance(lower_bound, int
|
|
461
|
+
if not (isinstance(lower_bound, (int, float))):
|
|
449
462
|
raise ValueError(f'Invalid bound type: {lower_bound}, must be int or float')
|
|
450
|
-
if not (isinstance(upper_bound, int
|
|
463
|
+
if not (isinstance(upper_bound, (int, float))):
|
|
451
464
|
raise ValueError(f'Invalid bound type: {upper_bound}, must be int or float')
|
|
452
465
|
elif formatter_type == TableFormatterType.BADGE:
|
|
453
466
|
badges = formatter.get('badges')
|
|
@@ -464,11 +477,11 @@ class Column(BaseModel):
|
|
|
464
477
|
)
|
|
465
478
|
if not isinstance(badges[badge].get('color'), str):
|
|
466
479
|
raise ValueError(
|
|
467
|
-
f
|
|
480
|
+
f'Invalid color: {badges[badge].get("color")} for badge: {badges[badge]}, must be a string'
|
|
468
481
|
)
|
|
469
482
|
return formatter
|
|
470
483
|
|
|
471
|
-
@
|
|
484
|
+
@field_validator('sticky')
|
|
472
485
|
@classmethod
|
|
473
486
|
def validate_sticky(cls, sticky):
|
|
474
487
|
"""
|
|
@@ -478,15 +491,15 @@ class Column(BaseModel):
|
|
|
478
491
|
raise ValueError(f'Invalid sticky value: {sticky}, accepted values: left, right')
|
|
479
492
|
return sticky
|
|
480
493
|
|
|
481
|
-
@
|
|
494
|
+
@field_validator('filter')
|
|
482
495
|
@classmethod
|
|
483
|
-
def validate_unique_items(cls, filter,
|
|
496
|
+
def validate_unique_items(cls, filter, info: ValidationInfo):
|
|
484
497
|
"""
|
|
485
498
|
Validate that for categorical filters unique_items is provided
|
|
486
499
|
"""
|
|
487
500
|
if filter == 'categorical':
|
|
488
|
-
unique_items =
|
|
489
|
-
col_id =
|
|
501
|
+
unique_items = info.data.get('unique_items')
|
|
502
|
+
col_id = info.data.get('col_id')
|
|
490
503
|
if unique_items is None:
|
|
491
504
|
raise ValueError(
|
|
492
505
|
f'Invalid unique_items: {col_id} has categorical filter, it must have unique_items defined'
|
|
@@ -499,10 +512,113 @@ class Table(ContentComponent):
|
|
|
499
512
|

|
|
500
513
|
|
|
501
514
|
A Table Component for drawing a simple table into a document. Table's can be created by passing data
|
|
502
|
-
as a
|
|
503
|
-
|
|
504
|
-
|
|
515
|
+
as a ServerVariable, DerivedVariable, DataFrame or correctly formatted raw tabular data.
|
|
516
|
+
|
|
517
|
+
For simple client-side tables, you can directly pass data as list of records, where each record is a dict with string keys.
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
from dara.components.common import Table
|
|
521
|
+
|
|
522
|
+
Table(
|
|
523
|
+
data=[
|
|
524
|
+
{
|
|
525
|
+
'col1': 'a',
|
|
526
|
+
'col2': 1,
|
|
527
|
+
'col3': 'F',
|
|
528
|
+
'col4': '1990-02-12T00:00:00.000Z',
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
'col1': 'b',
|
|
532
|
+
'col2': 2,
|
|
533
|
+
'col3': 'M',
|
|
534
|
+
'col4': '1991-02-12T00:00:00.000Z',
|
|
535
|
+
},
|
|
536
|
+
]
|
|
537
|
+
)
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
You can also pass a DataFrame directly:
|
|
541
|
+
|
|
542
|
+
```python
|
|
543
|
+
import pandas
|
|
544
|
+
from dara.components.common import Table
|
|
545
|
+
|
|
546
|
+
data = pandas.DataFrame([
|
|
547
|
+
{
|
|
548
|
+
'col1': 'a',
|
|
549
|
+
'col2': 1,
|
|
550
|
+
'col3': 'F',
|
|
551
|
+
'col4': '1990-02-12T00:00:00.000Z',
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
'col1': 'b',
|
|
555
|
+
'col2': 2,
|
|
556
|
+
'col3': 'M',
|
|
557
|
+
'col4': '1991-02-12T00:00:00.000Z',
|
|
558
|
+
},
|
|
559
|
+
])
|
|
505
560
|
|
|
561
|
+
Table(data=data)
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
When working with larger datasets, it is recommended to use a ServerVariable or DerivedVariable to avoid sending the entire dataset to the client.
|
|
565
|
+
They have built-in server-side filtering and pagination which is utilized by the Table component and integrated into its UI. They both support customization
|
|
566
|
+
of the filtering and pagination behavior, respectively via a custom `ServerVariable.backend` or a custom `DerivedVariable.filter_resolver`.
|
|
567
|
+
|
|
568
|
+
ServerVariables are ideal for static or mutable dataset that can be shared among users or specific to a user (with `scope='user'`) via e.g. data upload.
|
|
569
|
+
If possible, avoid recreating ServerVariables within e.g. `py_components`, as each instance will store a new copy of the data in memory.
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
from dara.core import ServerVariable
|
|
573
|
+
from dara.components.common import Table
|
|
574
|
+
|
|
575
|
+
data = ServerVariable(pandas.DataFrame([
|
|
576
|
+
{
|
|
577
|
+
'col1': 'a',
|
|
578
|
+
'col2': 1,
|
|
579
|
+
'col3': 'F',
|
|
580
|
+
'col4': '1990-02-12T00:00:00.000Z',
|
|
581
|
+
},
|
|
582
|
+
{
|
|
583
|
+
'col1': 'b',
|
|
584
|
+
'col2': 2,
|
|
585
|
+
'col3': 'M',
|
|
586
|
+
'col4': '1991-02-12T00:00:00.000Z',
|
|
587
|
+
},
|
|
588
|
+
]))
|
|
589
|
+
|
|
590
|
+
Table(data=data)
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
DerivedVariables allow you to create or resolve datasets on the fly, e.g. depending on user input.
|
|
594
|
+
|
|
595
|
+
```python
|
|
596
|
+
import pandas
|
|
597
|
+
from dara.core import DerivedVariable
|
|
598
|
+
from dara.components.common import Table
|
|
599
|
+
|
|
600
|
+
async def create_data(user_input: str, row_count: int):
|
|
601
|
+
df = pandas.DataFrame()
|
|
602
|
+
for i in range(row_count):
|
|
603
|
+
df = df.append({
|
|
604
|
+
'col1': user_input,
|
|
605
|
+
'col2': i,
|
|
606
|
+
'col3': 'F',
|
|
607
|
+
'col4': '1990-02-12T00:00:00.000Z',
|
|
608
|
+
}, ignore_index=True)
|
|
609
|
+
return df
|
|
610
|
+
|
|
611
|
+
user_input = Variable('a')
|
|
612
|
+
row_count = Variable(10)
|
|
613
|
+
|
|
614
|
+
data = DerivedVariable(create_data, variables=[user_input, row_count])
|
|
615
|
+
|
|
616
|
+
Table(data=data)
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
By default, the `Table` component will display all columns of the dataset. You can choose to customize which columns to display, their order, types and more.
|
|
620
|
+
Columns can be passed as lists of dicts, where the col_id of the cols must match the column name in the dataframe. Alternatively they can be Variables/DerivedVariables,
|
|
621
|
+
and if left undefined it will be inferred from the data passed.
|
|
506
622
|
The list of columns can be a mix of dicts and strings. If a string is passed then it will be used as
|
|
507
623
|
the col_id and label for the column whereas, passing a dict allows more control over the options. The
|
|
508
624
|
available options for columns are as follows (see Column class for more detail):
|
|
@@ -520,9 +636,9 @@ class Table(ContentComponent):
|
|
|
520
636
|
|
|
521
637
|
from pandas import DataFrame
|
|
522
638
|
from dara.components.common import Table
|
|
523
|
-
from dara.core import
|
|
639
|
+
from dara.core import ServerVariable
|
|
524
640
|
|
|
525
|
-
data =
|
|
641
|
+
data = ServerVariable(
|
|
526
642
|
DataFrame([
|
|
527
643
|
{
|
|
528
644
|
'col1': 1,
|
|
@@ -549,9 +665,9 @@ class Table(ContentComponent):
|
|
|
549
665
|
|
|
550
666
|
from pandas import DataFrame
|
|
551
667
|
from dara.components.common import Table
|
|
552
|
-
from dara.core import
|
|
668
|
+
from dara.core import ServerVariable
|
|
553
669
|
|
|
554
|
-
data =
|
|
670
|
+
data = ServerVariable(
|
|
555
671
|
DataFrame([
|
|
556
672
|
{
|
|
557
673
|
'col1': 1,
|
|
@@ -590,9 +706,9 @@ class Table(ContentComponent):
|
|
|
590
706
|
|
|
591
707
|
from pandas import DataFrame
|
|
592
708
|
from dara.components.common import Table
|
|
593
|
-
from dara.core import
|
|
709
|
+
from dara.core import ServerVariable, Variable
|
|
594
710
|
|
|
595
|
-
data =
|
|
711
|
+
data = ServerVariable(
|
|
596
712
|
DataFrame([
|
|
597
713
|
{
|
|
598
714
|
'col1': 1,
|
|
@@ -626,9 +742,9 @@ class Table(ContentComponent):
|
|
|
626
742
|
|
|
627
743
|
from pandas import DataFrame
|
|
628
744
|
from dara.components.common import Table
|
|
629
|
-
from dara.core import
|
|
745
|
+
from dara.core import ServerVariable
|
|
630
746
|
|
|
631
|
-
data =
|
|
747
|
+
data = ServerVariable(
|
|
632
748
|
DataFrame([
|
|
633
749
|
{
|
|
634
750
|
'col1': 1,
|
|
@@ -659,7 +775,7 @@ class Table(ContentComponent):
|
|
|
659
775
|
|
|
660
776
|
import pandas
|
|
661
777
|
from dara.components.common import Table
|
|
662
|
-
from dara.core import
|
|
778
|
+
from dara.core import ServerVariable
|
|
663
779
|
|
|
664
780
|
data = [{
|
|
665
781
|
'age': 27,
|
|
@@ -672,7 +788,7 @@ class Table(ContentComponent):
|
|
|
672
788
|
}]
|
|
673
789
|
df = pandas.DataFrame(data, columns=['age', 'name', 'surname'])
|
|
674
790
|
df.reset_index()
|
|
675
|
-
Table(data=
|
|
791
|
+
Table(data=ServerVariable(df), columns=[{'col_id': 'age', 'label': 'Age'}, 'index'])
|
|
676
792
|
|
|
677
793
|
```
|
|
678
794
|
|
|
@@ -681,9 +797,9 @@ class Table(ContentComponent):
|
|
|
681
797
|
```python
|
|
682
798
|
from pandas import DataFrame
|
|
683
799
|
from dara.components.common import Table
|
|
684
|
-
from dara.core import
|
|
800
|
+
from dara.core import ServerVariable
|
|
685
801
|
|
|
686
|
-
data =
|
|
802
|
+
data = ServerVariable(
|
|
687
803
|
DataFrame(
|
|
688
804
|
[
|
|
689
805
|
{
|
|
@@ -720,42 +836,77 @@ class Table(ContentComponent):
|
|
|
720
836
|
```
|
|
721
837
|
|
|
722
838
|
:param columns: The table's columns, this can be a list, a Variable/DerivedVariable or if left undefined it will be inferred from the data
|
|
723
|
-
:param data: The table's data,
|
|
839
|
+
:param data: The table's data, can be a list of records or a Variable resolving to a list of records
|
|
724
840
|
:param multi_select: Whether to allow selection of multiple rows, works with onclick_row and defaults to False
|
|
725
841
|
:param show_checkboxes: Whether to show or hide checkboxes column when onclick_row is set. Defaults to True
|
|
726
842
|
:param onclick_row: An action handler for when a row is clicked on the table
|
|
843
|
+
:param onselect_row: An action handler for when a row is selected via the checkbox column
|
|
727
844
|
:param selected_indices: Optional variable to store the selected rows indices, must be a list of numbers. Note that these indices are
|
|
728
845
|
the sequential indices of the rows as accepted by `DataFrame.iloc`, not the `row.index` value. If you would like the selection to persist over
|
|
729
|
-
page reloads, you must
|
|
846
|
+
page reloads, you must use a `BrowserStore` on a `Variable`.
|
|
730
847
|
:param search_columns: Optional list defining the columns to be searched, only the columns passed are searchable
|
|
731
848
|
:param searchable: Boolean, if True table can be searched via Input and will only render matching rows
|
|
849
|
+
:param include_index: Boolean, if True the table will render the index column(s), defaults to True
|
|
732
850
|
:param max_rows: if specified, table height will be fixed to accommodate the specified number of rows
|
|
851
|
+
:param suppress_click_events_for_selection: Whether to suppress click events for clicks in select boxes. Defaults to False
|
|
733
852
|
"""
|
|
734
853
|
|
|
735
|
-
|
|
736
|
-
|
|
854
|
+
model_config = ConfigDict(ser_json_timedelta='float', use_enum_values=True, arbitrary_types_allowed=True)
|
|
855
|
+
|
|
856
|
+
columns: Sequence[Column | dict | str] | ClientVariable | None = None
|
|
857
|
+
data: AnyVariable | DataFrame | list
|
|
737
858
|
multi_select: bool = False
|
|
738
859
|
show_checkboxes: bool = True
|
|
739
|
-
onclick_row:
|
|
740
|
-
|
|
741
|
-
|
|
860
|
+
onclick_row: Action | None = None
|
|
861
|
+
onselect_row: Action | None = None
|
|
862
|
+
selected_indices: list[int] | Variable | None = None
|
|
863
|
+
search_columns: list[str] | None = None
|
|
742
864
|
searchable: bool = False
|
|
743
|
-
|
|
744
|
-
|
|
865
|
+
include_index: bool = True
|
|
866
|
+
max_rows: int | None = None
|
|
867
|
+
suppress_click_events_for_selection: bool | None = False
|
|
745
868
|
|
|
746
|
-
TableFormatterType = TableFormatterType
|
|
747
|
-
TableFilter = TableFilter
|
|
869
|
+
TableFormatterType: ClassVar[TFormatterType] = TableFormatterType
|
|
870
|
+
TableFilter: ClassVar[TFilterType] = TableFilter
|
|
871
|
+
|
|
872
|
+
@field_validator('data')
|
|
873
|
+
@classmethod
|
|
874
|
+
def validate_data(cls, data):
|
|
875
|
+
# variables are fine, can't validate here
|
|
876
|
+
if isinstance(data, (DataFrame, AnyVariable)):
|
|
877
|
+
return data
|
|
878
|
+
if isinstance(data, list):
|
|
879
|
+
if not all(isinstance(item, dict) and all(isinstance(key, str) for key in item) for item in data):
|
|
880
|
+
raise ValueError(f'Invalid data passed to Table: {data}, expected a list of dicts with string keys')
|
|
881
|
+
return data
|
|
882
|
+
raise ValueError(f'Invalid data passed to Table: {type(data)}, expected a DataFrame or a variable')
|
|
883
|
+
|
|
884
|
+
@field_serializer('data', mode='wrap')
|
|
885
|
+
def serialize_field(self, value: Any, nxt: SerializerFunctionWrapHandler):
|
|
886
|
+
if isinstance(value, AnyVariable):
|
|
887
|
+
return nxt(value)
|
|
888
|
+
|
|
889
|
+
from dara.core.internal.encoder_registry import get_jsonable_encoder
|
|
890
|
+
|
|
891
|
+
try:
|
|
892
|
+
if isinstance(value, DataFrame):
|
|
893
|
+
value = value.to_dict(orient='records')
|
|
894
|
+
return jsonable_encoder(value, custom_encoder=get_jsonable_encoder())
|
|
895
|
+
except Exception as e:
|
|
896
|
+
dev_logger.error(
|
|
897
|
+
'Error serializing raw data in Table, falling back to default serialization.'
|
|
898
|
+
'Alternatively, you can provide a JSON-serializable dictionary in the "records format", i.e. `[{"col_a": 1}, {"col_a": 2}]`.',
|
|
899
|
+
error=e,
|
|
900
|
+
)
|
|
748
901
|
|
|
749
|
-
|
|
750
|
-
json_encoders = {datetime: coerce_to_timemilli}
|
|
751
|
-
use_enum_values = True
|
|
902
|
+
return nxt(value)
|
|
752
903
|
|
|
753
|
-
@
|
|
904
|
+
@field_validator('columns')
|
|
754
905
|
@classmethod
|
|
755
906
|
def validate_columns(cls, columns):
|
|
756
907
|
if columns is None:
|
|
757
908
|
return None
|
|
758
|
-
if isinstance(columns,
|
|
909
|
+
if isinstance(columns, list):
|
|
759
910
|
cols = []
|
|
760
911
|
for col in columns:
|
|
761
912
|
if isinstance(col, Column):
|
|
@@ -765,14 +916,14 @@ class Table(ContentComponent):
|
|
|
765
916
|
else:
|
|
766
917
|
cols.append(Column(**col))
|
|
767
918
|
return cols
|
|
768
|
-
elif isinstance(columns,
|
|
919
|
+
elif isinstance(columns, ClientVariable):
|
|
769
920
|
return columns
|
|
770
921
|
else:
|
|
771
922
|
raise ValueError(f'Invalid list passed to Table columns: {columns}, expected a list')
|
|
772
923
|
|
|
773
|
-
def add_column(self, col:
|
|
924
|
+
def add_column(self, col: str | dict | Column):
|
|
774
925
|
"""Adds a new column to the data"""
|
|
775
|
-
if not isinstance(self.columns,
|
|
926
|
+
if not isinstance(self.columns, list):
|
|
776
927
|
raise ValueError('You cannot add_columns to a Variable type columns or if they are undefined')
|
|
777
928
|
if isinstance(col, str):
|
|
778
929
|
self.columns.append(Column(col_id=col))
|
dara/components/common/text.py
CHANGED
|
@@ -15,12 +15,10 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from
|
|
19
|
-
|
|
20
|
-
from pydantic import validator
|
|
18
|
+
from pydantic import field_validator
|
|
21
19
|
|
|
22
20
|
from dara.components.common.base_component import ContentComponent
|
|
23
|
-
from dara.core.interactivity import
|
|
21
|
+
from dara.core.interactivity import ClientVariable
|
|
24
22
|
|
|
25
23
|
|
|
26
24
|
class Text(ContentComponent):
|
|
@@ -53,16 +51,16 @@ class Text(ContentComponent):
|
|
|
53
51
|
:param formatted: Whether to display the text with existing formatting intact or not, default False
|
|
54
52
|
"""
|
|
55
53
|
|
|
56
|
-
text:
|
|
57
|
-
align: str = 'left'
|
|
54
|
+
text: str | ClientVariable
|
|
55
|
+
align: str | None = 'left' # type: ignore # this is actually textAlign not align-items
|
|
58
56
|
formatted: bool = False
|
|
59
57
|
|
|
60
|
-
@
|
|
58
|
+
@field_validator('text')
|
|
61
59
|
@classmethod
|
|
62
60
|
def only_strings(cls, value: str):
|
|
63
|
-
if not isinstance(value, (str,
|
|
61
|
+
if not isinstance(value, (str, ClientVariable)):
|
|
64
62
|
raise ValueError(f'Invalid text passed to Text: {value}, expected a string')
|
|
65
63
|
return value
|
|
66
64
|
|
|
67
|
-
def __init__(self, text:
|
|
65
|
+
def __init__(self, text: str | ClientVariable, **kwargs):
|
|
68
66
|
super().__init__(text=text, **kwargs)
|
|
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from typing import Any, Literal
|
|
18
|
+
from typing import Any, Literal
|
|
19
19
|
|
|
20
20
|
from dara.components.common.base_component import FormComponent
|
|
21
21
|
from dara.core.base_definitions import Action
|
|
@@ -33,12 +33,12 @@ class Textarea(FormComponent):
|
|
|
33
33
|
A Textarea component is created via:
|
|
34
34
|
|
|
35
35
|
```python
|
|
36
|
-
|
|
37
36
|
from dara.core import Variable
|
|
38
37
|
from dara.components.common import Textarea
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
value_var = Variable('initial textarea content')
|
|
41
40
|
|
|
41
|
+
Textarea(value=value_var)
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
:param autofocus: Boolean, if True then then initially render with the cursor in the component
|
|
@@ -49,7 +49,7 @@ class Textarea(FormComponent):
|
|
|
49
49
|
"""
|
|
50
50
|
|
|
51
51
|
autofocus: bool = False
|
|
52
|
-
value:
|
|
53
|
-
onchange:
|
|
54
|
-
id:
|
|
55
|
-
resize:
|
|
52
|
+
value: Variable[Any] | None = None
|
|
53
|
+
onchange: Action | None = None
|
|
54
|
+
id: str | None = None
|
|
55
|
+
resize: Literal['none', 'both', 'horizontal', 'vertical', 'block', 'inline'] | None = None
|
|
@@ -16,7 +16,6 @@ limitations under the License.
|
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
18
|
import datetime
|
|
19
|
-
from typing import Union
|
|
20
19
|
|
|
21
20
|
import numpy
|
|
22
21
|
|
|
@@ -37,11 +36,9 @@ def date_to_datetime(d: datetime.date, hh=23, mm=59, ss=59) -> datetime.datetime
|
|
|
37
36
|
return datetime.datetime(d.year, d.month, d.day, hh, mm, ss)
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
def coerce_to_timemilli(t:
|
|
39
|
+
def coerce_to_timemilli(t: int | float | datetime.date | datetime.datetime) -> float:
|
|
41
40
|
"""Convert any of int/float/date/datetime into a timemilli."""
|
|
42
|
-
if isinstance(t, int
|
|
43
|
-
return float(t)
|
|
44
|
-
elif isinstance(t, float) or isinstance(t, numpy.float_):
|
|
41
|
+
if isinstance(t, (int, numpy.signedinteger, float, numpy.floating)):
|
|
45
42
|
return float(t)
|
|
46
43
|
# The order matters - datetime.datetime is a subclass of the datetime.date
|
|
47
44
|
elif isinstance(t, datetime.datetime):
|
|
@@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
from typing import Union
|
|
19
|
-
|
|
20
18
|
from dara.components.common.base_component import ModifierComponent
|
|
21
19
|
from dara.components.common.stack import Stack
|
|
22
20
|
from dara.core.definitions import ComponentInstance
|
|
@@ -74,17 +72,17 @@ class Tooltip(ModifierComponent):
|
|
|
74
72
|
:param styling: Defines the style of the tooltip, can be 'default' or 'error'
|
|
75
73
|
"""
|
|
76
74
|
|
|
77
|
-
content:
|
|
75
|
+
content: str | ComponentInstance | Variable[str] | DerivedVariable[str]
|
|
78
76
|
placement: str = 'auto'
|
|
79
77
|
styling: str = 'default'
|
|
80
78
|
|
|
81
79
|
def __init__(
|
|
82
80
|
self,
|
|
83
|
-
*components: ComponentInstance,
|
|
84
|
-
content:
|
|
81
|
+
*components: ComponentInstance | None,
|
|
82
|
+
content: str | ComponentInstance | Variable[str] | DerivedVariable[str],
|
|
85
83
|
placement: str = 'auto',
|
|
86
84
|
styling: str = 'default',
|
|
87
|
-
**kwargs
|
|
85
|
+
**kwargs,
|
|
88
86
|
):
|
|
89
87
|
# Unless there's one component and it's Stack, wrap components in Stack
|
|
90
88
|
if not (len(components) == 1 and isinstance(components[0], Stack)):
|