nicegui-bootstrap-components 0.1.0__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.
- nicegui_bootstrap_components/__init__.py +7 -0
- nicegui_bootstrap_components/_base.py +590 -0
- nicegui_bootstrap_components/_host.py +259 -0
- nicegui_bootstrap_components/assets.py +371 -0
- nicegui_bootstrap_components/bs/__init__.py +267 -0
- nicegui_bootstrap_components/bs/_actions.py +232 -0
- nicegui_bootstrap_components/bs/_alert_spinner.py +423 -0
- nicegui_bootstrap_components/bs/_carousel.py +312 -0
- nicegui_bootstrap_components/bs/_choices.py +779 -0
- nicegui_bootstrap_components/bs/_content.py +1343 -0
- nicegui_bootstrap_components/bs/_forms.py +378 -0
- nicegui_bootstrap_components/bs/_inputs.py +560 -0
- nicegui_bootstrap_components/bs/_layout.py +341 -0
- nicegui_bootstrap_components/bs/_nav.py +650 -0
- nicegui_bootstrap_components/bs/_navbar.py +445 -0
- nicegui_bootstrap_components/bs/_overlay_root.py +73 -0
- nicegui_bootstrap_components/bs/_overlays.py +2633 -0
- nicegui_bootstrap_components/bs/_scope.py +28 -0
- nicegui_bootstrap_components/bs/_tabs.py +605 -0
- nicegui_bootstrap_components/bs/_toast.py +137 -0
- nicegui_bootstrap_components/bs/_tooltip_popover.py +537 -0
- nicegui_bootstrap_components/contracts/dbc-2.0.4.json +16811 -0
- nicegui_bootstrap_components/dbc/__init__.py +108 -0
- nicegui_bootstrap_components/icons.py +29 -0
- nicegui_bootstrap_components/persist.py +201 -0
- nicegui_bootstrap_components/py.typed +0 -0
- nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff +0 -0
- nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff2 +0 -0
- nicegui_bootstrap_components/static/dist/ngbs-bootstrap-unscoped.css +12047 -0
- nicegui_bootstrap_components/static/dist/ngbs-bootstrap.css +12057 -0
- nicegui_bootstrap_components/static/dist/ngbs-cerulean-unscoped.css +12338 -0
- nicegui_bootstrap_components/static/dist/ngbs-cerulean.css +12348 -0
- nicegui_bootstrap_components/static/dist/ngbs-compat-unscoped.css +12 -0
- nicegui_bootstrap_components/static/dist/ngbs-compat.css +12 -0
- nicegui_bootstrap_components/static/dist/ngbs-cosmo-unscoped.css +11825 -0
- nicegui_bootstrap_components/static/dist/ngbs-cosmo.css +11835 -0
- nicegui_bootstrap_components/static/dist/ngbs-cyborg-unscoped.css +12213 -0
- nicegui_bootstrap_components/static/dist/ngbs-cyborg.css +12223 -0
- nicegui_bootstrap_components/static/dist/ngbs-darkly-unscoped.css +12143 -0
- nicegui_bootstrap_components/static/dist/ngbs-darkly.css +12153 -0
- nicegui_bootstrap_components/static/dist/ngbs-flatly-unscoped.css +12129 -0
- nicegui_bootstrap_components/static/dist/ngbs-flatly.css +12139 -0
- nicegui_bootstrap_components/static/dist/ngbs-grid-unscoped.css +4084 -0
- nicegui_bootstrap_components/static/dist/ngbs-grid.css +4094 -0
- nicegui_bootstrap_components/static/dist/ngbs-host-unscoped.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-host.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-icons-bootstrap.css +5 -0
- nicegui_bootstrap_components/static/dist/ngbs-icons-fontawesome.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-journal-unscoped.css +12112 -0
- nicegui_bootstrap_components/static/dist/ngbs-journal.css +12122 -0
- nicegui_bootstrap_components/static/dist/ngbs-litera-unscoped.css +12214 -0
- nicegui_bootstrap_components/static/dist/ngbs-litera.css +12224 -0
- nicegui_bootstrap_components/static/dist/ngbs-lumen-unscoped.css +12377 -0
- nicegui_bootstrap_components/static/dist/ngbs-lumen.css +12387 -0
- nicegui_bootstrap_components/static/dist/ngbs-lux-unscoped.css +12048 -0
- nicegui_bootstrap_components/static/dist/ngbs-lux.css +12058 -0
- nicegui_bootstrap_components/static/dist/ngbs-materia-unscoped.css +13197 -0
- nicegui_bootstrap_components/static/dist/ngbs-materia.css +13207 -0
- nicegui_bootstrap_components/static/dist/ngbs-minty-unscoped.css +12184 -0
- nicegui_bootstrap_components/static/dist/ngbs-minty.css +12194 -0
- nicegui_bootstrap_components/static/dist/ngbs-morph-unscoped.css +12829 -0
- nicegui_bootstrap_components/static/dist/ngbs-morph.css +12839 -0
- nicegui_bootstrap_components/static/dist/ngbs-pulse-unscoped.css +11901 -0
- nicegui_bootstrap_components/static/dist/ngbs-pulse.css +11911 -0
- nicegui_bootstrap_components/static/dist/ngbs-quartz-unscoped.css +12625 -0
- nicegui_bootstrap_components/static/dist/ngbs-quartz.css +12635 -0
- nicegui_bootstrap_components/static/dist/ngbs-sandstone-unscoped.css +12214 -0
- nicegui_bootstrap_components/static/dist/ngbs-sandstone.css +12224 -0
- nicegui_bootstrap_components/static/dist/ngbs-simplex-unscoped.css +12199 -0
- nicegui_bootstrap_components/static/dist/ngbs-simplex.css +12209 -0
- nicegui_bootstrap_components/static/dist/ngbs-sketchy-unscoped.css +12518 -0
- nicegui_bootstrap_components/static/dist/ngbs-sketchy.css +12528 -0
- nicegui_bootstrap_components/static/dist/ngbs-slate-unscoped.css +12527 -0
- nicegui_bootstrap_components/static/dist/ngbs-slate.css +12537 -0
- nicegui_bootstrap_components/static/dist/ngbs-solar-unscoped.css +12152 -0
- nicegui_bootstrap_components/static/dist/ngbs-solar.css +12162 -0
- nicegui_bootstrap_components/static/dist/ngbs-spacelab-unscoped.css +12279 -0
- nicegui_bootstrap_components/static/dist/ngbs-spacelab.css +12289 -0
- nicegui_bootstrap_components/static/dist/ngbs-superhero-unscoped.css +12223 -0
- nicegui_bootstrap_components/static/dist/ngbs-superhero.css +12233 -0
- nicegui_bootstrap_components/static/dist/ngbs-united-unscoped.css +12090 -0
- nicegui_bootstrap_components/static/dist/ngbs-united.css +12100 -0
- nicegui_bootstrap_components/static/dist/ngbs-vapor-unscoped.css +12556 -0
- nicegui_bootstrap_components/static/dist/ngbs-vapor.css +12566 -0
- nicegui_bootstrap_components/static/dist/ngbs-yeti-unscoped.css +12333 -0
- nicegui_bootstrap_components/static/dist/ngbs-yeti.css +12343 -0
- nicegui_bootstrap_components/static/dist/ngbs-zephyr-unscoped.css +12296 -0
- nicegui_bootstrap_components/static/dist/ngbs-zephyr.css +12306 -0
- nicegui_bootstrap_components/static/prototype-ngbs.css +541 -0
- nicegui_bootstrap_components/static/webfonts/fa-brands-400.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-brands-400.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-regular-400.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-regular-400.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-solid-900.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-solid-900.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.woff2 +0 -0
- nicegui_bootstrap_components/theme.py +238 -0
- nicegui_bootstrap_components/themes.py +131 -0
- nicegui_bootstrap_components-0.1.0.dist-info/METADATA +234 -0
- nicegui_bootstrap_components-0.1.0.dist-info/RECORD +103 -0
- nicegui_bootstrap_components-0.1.0.dist-info/WHEEL +4 -0
- nicegui_bootstrap_components-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1343 @@
|
|
|
1
|
+
"""Content family: Card, ListGroup, Table, Badge, Progress, Placeholder."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Sequence
|
|
6
|
+
from contextvars import ContextVar, Token
|
|
7
|
+
from typing import Any, cast
|
|
8
|
+
|
|
9
|
+
from nicegui import ui
|
|
10
|
+
|
|
11
|
+
from .._base import (
|
|
12
|
+
BootstrapElement,
|
|
13
|
+
ChildrenError,
|
|
14
|
+
PropConflictError,
|
|
15
|
+
UnsupportedPropError,
|
|
16
|
+
make_surface_classes,
|
|
17
|
+
normalize_style,
|
|
18
|
+
resolve_class_name,
|
|
19
|
+
style_to_css,
|
|
20
|
+
)
|
|
21
|
+
from .._host import set_element_text
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Badge",
|
|
25
|
+
"Card",
|
|
26
|
+
"CardBody",
|
|
27
|
+
"CardFooter",
|
|
28
|
+
"CardGroup",
|
|
29
|
+
"CardHeader",
|
|
30
|
+
"CardImg",
|
|
31
|
+
"CardImgOverlay",
|
|
32
|
+
"CardLink",
|
|
33
|
+
"DbcBadge",
|
|
34
|
+
"DbcCard",
|
|
35
|
+
"DbcCardBody",
|
|
36
|
+
"DbcCardFooter",
|
|
37
|
+
"DbcCardGroup",
|
|
38
|
+
"DbcCardHeader",
|
|
39
|
+
"DbcCardImg",
|
|
40
|
+
"DbcCardImgOverlay",
|
|
41
|
+
"DbcCardLink",
|
|
42
|
+
"DbcListGroup",
|
|
43
|
+
"DbcListGroupItem",
|
|
44
|
+
"DbcPlaceholder",
|
|
45
|
+
"DbcProgress",
|
|
46
|
+
"DbcTable",
|
|
47
|
+
"ListGroup",
|
|
48
|
+
"ListGroupItem",
|
|
49
|
+
"Placeholder",
|
|
50
|
+
"Progress",
|
|
51
|
+
"Table",
|
|
52
|
+
"badge",
|
|
53
|
+
"badge_structural_classes",
|
|
54
|
+
"card",
|
|
55
|
+
"card_body",
|
|
56
|
+
"card_footer",
|
|
57
|
+
"card_group",
|
|
58
|
+
"card_header",
|
|
59
|
+
"card_img",
|
|
60
|
+
"card_img_overlay",
|
|
61
|
+
"card_img_structural_classes",
|
|
62
|
+
"card_link",
|
|
63
|
+
"card_structural_classes",
|
|
64
|
+
"format_table_cell",
|
|
65
|
+
"grouped_spans",
|
|
66
|
+
"list_group",
|
|
67
|
+
"list_group_item",
|
|
68
|
+
"list_group_item_structural_classes",
|
|
69
|
+
"list_group_structural_classes",
|
|
70
|
+
"placeholder",
|
|
71
|
+
"placeholder_structural_classes",
|
|
72
|
+
"progress",
|
|
73
|
+
"progress_bar_structural_classes",
|
|
74
|
+
"progress_percent",
|
|
75
|
+
"progress_structural_classes",
|
|
76
|
+
"resolve_list_group_item_tag",
|
|
77
|
+
"resolve_list_group_tag",
|
|
78
|
+
"table",
|
|
79
|
+
"table_responsive_class",
|
|
80
|
+
"table_structural_classes",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
_BOOTSTRAP_CONTEXT_COLORS = frozenset(
|
|
84
|
+
{
|
|
85
|
+
"primary",
|
|
86
|
+
"secondary",
|
|
87
|
+
"success",
|
|
88
|
+
"danger",
|
|
89
|
+
"warning",
|
|
90
|
+
"info",
|
|
91
|
+
"light",
|
|
92
|
+
"dark",
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
_TEXT_COLORS = _BOOTSTRAP_CONTEXT_COLORS | frozenset(
|
|
96
|
+
{
|
|
97
|
+
"white",
|
|
98
|
+
"black",
|
|
99
|
+
"muted",
|
|
100
|
+
"body",
|
|
101
|
+
"body-secondary",
|
|
102
|
+
"body-tertiary",
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
_BREAKPOINTS = frozenset({"sm", "md", "lg", "xl", "xxl"})
|
|
106
|
+
_PLACEHOLDER_SIZES = frozenset({"xs", "sm", "lg"})
|
|
107
|
+
_PLACEHOLDER_ANIMATIONS = frozenset({"glow", "wave"})
|
|
108
|
+
_WIDTH_UTILITIES = frozenset({25, 50, 75, 100})
|
|
109
|
+
_DISPLAY_UTILITIES = frozenset(
|
|
110
|
+
{"inline", "inline-block", "block", "flex", "grid", "table", "inline-flex"}
|
|
111
|
+
)
|
|
112
|
+
_UNITLESS_STYLE = frozenset(
|
|
113
|
+
{
|
|
114
|
+
"opacity",
|
|
115
|
+
"flex",
|
|
116
|
+
"grow",
|
|
117
|
+
"shrink",
|
|
118
|
+
"order",
|
|
119
|
+
"z-index",
|
|
120
|
+
"font-weight",
|
|
121
|
+
"line-height",
|
|
122
|
+
}
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
_LIST_GROUP_ITEM_DEFAULT_TAG: ContextVar[str | None] = ContextVar(
|
|
126
|
+
"_list_group_item_default_tag",
|
|
127
|
+
default=None,
|
|
128
|
+
)
|
|
129
|
+
_PROGRESS_NESTED: ContextVar[bool] = ContextVar("_progress_nested", default=False)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _validate_color(color: str, *, allowed: frozenset[str] | None = None) -> str:
|
|
133
|
+
palette = _BOOTSTRAP_CONTEXT_COLORS if allowed is None else allowed
|
|
134
|
+
if color not in palette:
|
|
135
|
+
raise ValueError(f"Unknown color {color!r}")
|
|
136
|
+
return color
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _col_width_classes(breakpoint: str, value: int | bool | None) -> list[str]:
|
|
140
|
+
if value is None or value is False:
|
|
141
|
+
return []
|
|
142
|
+
if value is True:
|
|
143
|
+
return ["col"] if breakpoint == "xs" else [f"col-{breakpoint}"]
|
|
144
|
+
if isinstance(value, bool) or not isinstance(value, int):
|
|
145
|
+
raise ValueError(f"Unknown column width {value!r}")
|
|
146
|
+
if value < 1 or value > 12:
|
|
147
|
+
raise ValueError(f"Unknown column width {value!r}")
|
|
148
|
+
if breakpoint == "xs":
|
|
149
|
+
return [f"col-{value}"]
|
|
150
|
+
return [f"col-{breakpoint}-{value}"]
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _children_present(children: object) -> bool:
|
|
154
|
+
if children is None:
|
|
155
|
+
return False
|
|
156
|
+
return not (children == "" or children == [] or children == ())
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _declared_children(children: object) -> list[object]:
|
|
160
|
+
if children is None:
|
|
161
|
+
return []
|
|
162
|
+
if isinstance(children, (list, tuple)):
|
|
163
|
+
return list(children)
|
|
164
|
+
return [children]
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _is_float_like(value: object) -> bool:
|
|
168
|
+
if isinstance(value, (bool, int)):
|
|
169
|
+
return False
|
|
170
|
+
if isinstance(value, float):
|
|
171
|
+
return True
|
|
172
|
+
return "float" in type(value).__name__.lower()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _format_number(value: float) -> str:
|
|
176
|
+
if value == int(value):
|
|
177
|
+
return str(int(value))
|
|
178
|
+
return str(value)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _merge_width_style(width_pct: float, bar_style: dict[str, Any] | str | None) -> str:
|
|
182
|
+
if isinstance(bar_style, str) and bar_style:
|
|
183
|
+
return f"width: {width_pct}%; {bar_style}"
|
|
184
|
+
style_dict: dict[str, Any] = {"width": f"{width_pct}%"}
|
|
185
|
+
if isinstance(bar_style, dict):
|
|
186
|
+
style_dict.update(bar_style)
|
|
187
|
+
normalized: dict[str, str] | str = normalize_style(style_dict)
|
|
188
|
+
if isinstance(normalized, str):
|
|
189
|
+
return normalized
|
|
190
|
+
converted = style_to_css(normalized)
|
|
191
|
+
return converted if isinstance(converted, str) else str(converted)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _merge_style_kwarg(kwargs: dict[str, Any], extra: dict[str, Any]) -> None:
|
|
195
|
+
style = kwargs.get("style")
|
|
196
|
+
if style is None:
|
|
197
|
+
kwargs["style"] = dict(extra)
|
|
198
|
+
return
|
|
199
|
+
if isinstance(style, dict):
|
|
200
|
+
merged = dict(extra)
|
|
201
|
+
merged.update(style)
|
|
202
|
+
kwargs["style"] = merged
|
|
203
|
+
return
|
|
204
|
+
suffix = "; ".join(f"{key}: {value}" for key, value in extra.items())
|
|
205
|
+
kwargs["style"] = f"{style}; {suffix}"
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _require_pandas() -> Any:
|
|
209
|
+
try:
|
|
210
|
+
import pandas as pd
|
|
211
|
+
except ImportError as exc:
|
|
212
|
+
raise ImportError(
|
|
213
|
+
"pandas is required for Table.from_dataframe. "
|
|
214
|
+
"Install the pandas extra to use this helper."
|
|
215
|
+
) from exc
|
|
216
|
+
return pd
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _apply_interactive_props(
|
|
220
|
+
element: Any,
|
|
221
|
+
*,
|
|
222
|
+
tag: str,
|
|
223
|
+
href: str | None = None,
|
|
224
|
+
disabled: bool = False,
|
|
225
|
+
external_link: bool = False,
|
|
226
|
+
) -> None:
|
|
227
|
+
if tag == "a":
|
|
228
|
+
if href is not None:
|
|
229
|
+
element._props["href"] = href
|
|
230
|
+
if external_link:
|
|
231
|
+
element._props["target"] = "_blank"
|
|
232
|
+
element._props["rel"] = "noopener noreferrer"
|
|
233
|
+
if disabled:
|
|
234
|
+
element._props["aria-disabled"] = "true"
|
|
235
|
+
element._props["tabindex"] = "-1"
|
|
236
|
+
return
|
|
237
|
+
if tag == "button":
|
|
238
|
+
element._props.setdefault("type", "button")
|
|
239
|
+
if disabled:
|
|
240
|
+
element._props["disabled"] = True
|
|
241
|
+
return
|
|
242
|
+
if disabled:
|
|
243
|
+
element._props["aria-disabled"] = "true"
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _place_children(host: Any, children: object) -> None:
|
|
247
|
+
if children is None:
|
|
248
|
+
return
|
|
249
|
+
if isinstance(children, str):
|
|
250
|
+
raise ChildrenError("Table does not accept text children; wrap text in a component")
|
|
251
|
+
for child in _declared_children(children):
|
|
252
|
+
if isinstance(child, str):
|
|
253
|
+
raise ChildrenError("Table does not accept text children; wrap text in a component")
|
|
254
|
+
move = getattr(child, "move", None)
|
|
255
|
+
if callable(move):
|
|
256
|
+
move(host)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _configure_progress_bar(
|
|
260
|
+
target: Any,
|
|
261
|
+
*,
|
|
262
|
+
classes: list[str],
|
|
263
|
+
value: float,
|
|
264
|
+
min_value: float,
|
|
265
|
+
max_value: float,
|
|
266
|
+
label: str | None,
|
|
267
|
+
bar_style: dict[str, Any] | str | None,
|
|
268
|
+
) -> None:
|
|
269
|
+
if classes and hasattr(target, "classes"):
|
|
270
|
+
target.classes(" ".join(classes))
|
|
271
|
+
target._props["role"] = "progressbar"
|
|
272
|
+
target._props["aria-valuenow"] = _format_number(value)
|
|
273
|
+
target._props["aria-valuemin"] = _format_number(min_value)
|
|
274
|
+
target._props["aria-valuemax"] = _format_number(max_value)
|
|
275
|
+
target._props["style"] = _merge_width_style(
|
|
276
|
+
progress_percent(value, min_value, max_value),
|
|
277
|
+
bar_style,
|
|
278
|
+
)
|
|
279
|
+
if label is not None:
|
|
280
|
+
set_element_text(target, str(label))
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def card_structural_classes(
|
|
284
|
+
*,
|
|
285
|
+
color: str | None = None,
|
|
286
|
+
outline: bool = False,
|
|
287
|
+
inverse: bool = False,
|
|
288
|
+
body: bool = False,
|
|
289
|
+
) -> list[str]:
|
|
290
|
+
"""Return Bootstrap ``card`` classes."""
|
|
291
|
+
classes = ["card"]
|
|
292
|
+
if body:
|
|
293
|
+
classes.append("card-body")
|
|
294
|
+
if color is not None:
|
|
295
|
+
_validate_color(color)
|
|
296
|
+
if outline:
|
|
297
|
+
classes.append(f"border-{color}")
|
|
298
|
+
else:
|
|
299
|
+
classes.append(f"text-bg-{color}")
|
|
300
|
+
if inverse:
|
|
301
|
+
classes.append("text-white")
|
|
302
|
+
return classes
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def card_img_structural_classes(
|
|
306
|
+
*,
|
|
307
|
+
top: bool = False,
|
|
308
|
+
bottom: bool = False,
|
|
309
|
+
overlay: bool = False,
|
|
310
|
+
) -> list[str]:
|
|
311
|
+
"""Return Bootstrap ``card-img`` position classes."""
|
|
312
|
+
selected = [
|
|
313
|
+
name for name, flag in (("top", top), ("bottom", bottom), ("overlay", overlay)) if flag
|
|
314
|
+
]
|
|
315
|
+
if len(selected) > 1:
|
|
316
|
+
raise PropConflictError("top")
|
|
317
|
+
if top:
|
|
318
|
+
return ["card-img-top"]
|
|
319
|
+
if bottom:
|
|
320
|
+
return ["card-img-bottom"]
|
|
321
|
+
return ["card-img"]
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
def list_group_structural_classes(
|
|
325
|
+
*,
|
|
326
|
+
flush: bool = False,
|
|
327
|
+
numbered: bool = False,
|
|
328
|
+
horizontal: bool | str = False,
|
|
329
|
+
) -> list[str]:
|
|
330
|
+
"""Return Bootstrap ``list-group`` classes."""
|
|
331
|
+
classes = ["list-group"]
|
|
332
|
+
if flush:
|
|
333
|
+
classes.append("list-group-flush")
|
|
334
|
+
if numbered:
|
|
335
|
+
classes.append("list-group-numbered")
|
|
336
|
+
if horizontal is True:
|
|
337
|
+
classes.append("list-group-horizontal")
|
|
338
|
+
elif horizontal is False:
|
|
339
|
+
pass
|
|
340
|
+
elif isinstance(horizontal, str) and horizontal in _BREAKPOINTS:
|
|
341
|
+
classes.append(f"list-group-horizontal-{horizontal}")
|
|
342
|
+
else:
|
|
343
|
+
raise ValueError(f"Unknown horizontal breakpoint {horizontal!r}")
|
|
344
|
+
return classes
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def list_group_item_structural_classes(
|
|
348
|
+
*,
|
|
349
|
+
active: bool = False,
|
|
350
|
+
disabled: bool = False,
|
|
351
|
+
color: str | None = None,
|
|
352
|
+
action: bool = False,
|
|
353
|
+
href: str | None = None,
|
|
354
|
+
) -> list[str]:
|
|
355
|
+
"""Return Bootstrap ``list-group-item`` classes."""
|
|
356
|
+
classes = ["list-group-item"]
|
|
357
|
+
if color is not None:
|
|
358
|
+
_validate_color(color)
|
|
359
|
+
classes.append(f"list-group-item-{color}")
|
|
360
|
+
if action or href is not None:
|
|
361
|
+
classes.append("list-group-item-action")
|
|
362
|
+
if active:
|
|
363
|
+
classes.append("active")
|
|
364
|
+
if disabled:
|
|
365
|
+
classes.append("disabled")
|
|
366
|
+
return classes
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def resolve_list_group_tag(*, tag: str | None = None, numbered: bool = False) -> str:
|
|
370
|
+
"""Return the ListGroup tag fixed at construction time."""
|
|
371
|
+
if tag is not None:
|
|
372
|
+
return tag
|
|
373
|
+
if numbered:
|
|
374
|
+
return "ol"
|
|
375
|
+
return "div"
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def resolve_list_group_item_tag(
|
|
379
|
+
*,
|
|
380
|
+
tag: str | None = None,
|
|
381
|
+
href: str | None = None,
|
|
382
|
+
action: bool = False,
|
|
383
|
+
context_default: str | None = None,
|
|
384
|
+
) -> str:
|
|
385
|
+
"""Return the ListGroupItem tag from explicit tag, href, action, then context."""
|
|
386
|
+
if tag is not None:
|
|
387
|
+
return tag
|
|
388
|
+
if href is not None:
|
|
389
|
+
return "a"
|
|
390
|
+
if action:
|
|
391
|
+
return "button"
|
|
392
|
+
if context_default is not None:
|
|
393
|
+
return context_default
|
|
394
|
+
return "div"
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def table_structural_classes(
|
|
398
|
+
*,
|
|
399
|
+
bordered: bool = False,
|
|
400
|
+
borderless: bool = False,
|
|
401
|
+
striped: bool = False,
|
|
402
|
+
hover: bool = False,
|
|
403
|
+
small: bool = False,
|
|
404
|
+
size: str | None = None,
|
|
405
|
+
color: str | None = None,
|
|
406
|
+
striped_columns: bool = False,
|
|
407
|
+
) -> list[str]:
|
|
408
|
+
"""Return Bootstrap ``table`` classes."""
|
|
409
|
+
if bordered and borderless:
|
|
410
|
+
raise PropConflictError("bordered")
|
|
411
|
+
if small and size is not None and size != "sm":
|
|
412
|
+
raise PropConflictError("small")
|
|
413
|
+
classes = ["table"]
|
|
414
|
+
if bordered:
|
|
415
|
+
classes.append("table-bordered")
|
|
416
|
+
if borderless:
|
|
417
|
+
classes.append("table-borderless")
|
|
418
|
+
if striped:
|
|
419
|
+
classes.append("table-striped")
|
|
420
|
+
if striped_columns:
|
|
421
|
+
classes.append("table-striped-columns")
|
|
422
|
+
if hover:
|
|
423
|
+
classes.append("table-hover")
|
|
424
|
+
if small or size == "sm":
|
|
425
|
+
classes.append("table-sm")
|
|
426
|
+
elif size is not None:
|
|
427
|
+
raise ValueError(f"Unknown table size {size!r}")
|
|
428
|
+
if color is not None:
|
|
429
|
+
_validate_color(color)
|
|
430
|
+
classes.append(f"table-{color}")
|
|
431
|
+
return classes
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def table_responsive_class(responsive: bool | str) -> str | None:
|
|
435
|
+
"""Return the wrapper class for a responsive table, or ``None``."""
|
|
436
|
+
if responsive is False:
|
|
437
|
+
return None
|
|
438
|
+
if responsive is True:
|
|
439
|
+
return "table-responsive"
|
|
440
|
+
if isinstance(responsive, str) and responsive in _BREAKPOINTS:
|
|
441
|
+
return f"table-responsive-{responsive}"
|
|
442
|
+
raise ValueError(f"Unknown responsive breakpoint {responsive!r}")
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def grouped_spans(values: list[object] | tuple[object, ...]) -> list[tuple[object, int]]:
|
|
446
|
+
"""Group consecutive equal values into ``(value, colspan)`` pairs."""
|
|
447
|
+
if not values:
|
|
448
|
+
return []
|
|
449
|
+
grouped: list[tuple[object, int]] = []
|
|
450
|
+
current = values[0]
|
|
451
|
+
count = 1
|
|
452
|
+
for item in values[1:]:
|
|
453
|
+
if item == current:
|
|
454
|
+
count += 1
|
|
455
|
+
else:
|
|
456
|
+
grouped.append((current, count))
|
|
457
|
+
current = item
|
|
458
|
+
count = 1
|
|
459
|
+
grouped.append((current, count))
|
|
460
|
+
return grouped
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def format_table_cell(
|
|
464
|
+
value: object,
|
|
465
|
+
*,
|
|
466
|
+
date_format: str | None = None,
|
|
467
|
+
float_format: str | Callable[..., Any] | None = None,
|
|
468
|
+
) -> str:
|
|
469
|
+
"""Format a table cell the way DBC ``from_dataframe`` does."""
|
|
470
|
+
if value is None:
|
|
471
|
+
return ""
|
|
472
|
+
if isinstance(value, tuple):
|
|
473
|
+
return " ".join(
|
|
474
|
+
format_table_cell(part, date_format=date_format, float_format=float_format)
|
|
475
|
+
for part in value
|
|
476
|
+
)
|
|
477
|
+
if date_format is not None:
|
|
478
|
+
strftime = getattr(value, "strftime", None)
|
|
479
|
+
if callable(strftime):
|
|
480
|
+
try:
|
|
481
|
+
return str(strftime(date_format))
|
|
482
|
+
except (TypeError, ValueError):
|
|
483
|
+
pass
|
|
484
|
+
if float_format is not None and _is_float_like(value):
|
|
485
|
+
number = float(cast(float, value))
|
|
486
|
+
if callable(float_format):
|
|
487
|
+
return str(float_format(number))
|
|
488
|
+
if "%" in float_format:
|
|
489
|
+
return float_format % number
|
|
490
|
+
try:
|
|
491
|
+
return format(number, float_format)
|
|
492
|
+
except ValueError:
|
|
493
|
+
return str(value)
|
|
494
|
+
return str(value)
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def badge_structural_classes(
|
|
498
|
+
*,
|
|
499
|
+
color: str = "secondary",
|
|
500
|
+
pill: bool = False,
|
|
501
|
+
text_color: str | None = None,
|
|
502
|
+
) -> list[str]:
|
|
503
|
+
"""Return Bootstrap ``badge`` classes."""
|
|
504
|
+
_validate_color(color)
|
|
505
|
+
classes = ["badge", f"text-bg-{color}"]
|
|
506
|
+
if text_color is not None:
|
|
507
|
+
_validate_color(text_color, allowed=_TEXT_COLORS)
|
|
508
|
+
classes.append(f"text-{text_color}")
|
|
509
|
+
if pill:
|
|
510
|
+
classes.append("rounded-pill")
|
|
511
|
+
return classes
|
|
512
|
+
|
|
513
|
+
|
|
514
|
+
def progress_structural_classes() -> list[str]:
|
|
515
|
+
"""Return Bootstrap ``progress`` wrapper classes."""
|
|
516
|
+
return ["progress"]
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def progress_bar_structural_classes(
|
|
520
|
+
*,
|
|
521
|
+
color: str | None = None,
|
|
522
|
+
striped: bool = False,
|
|
523
|
+
animated: bool = False,
|
|
524
|
+
) -> list[str]:
|
|
525
|
+
"""Return Bootstrap ``progress-bar`` classes."""
|
|
526
|
+
classes = ["progress-bar"]
|
|
527
|
+
if color is not None:
|
|
528
|
+
_validate_color(color)
|
|
529
|
+
classes.append(f"bg-{color}")
|
|
530
|
+
if striped or animated:
|
|
531
|
+
classes.append("progress-bar-striped")
|
|
532
|
+
if animated:
|
|
533
|
+
classes.append("progress-bar-animated")
|
|
534
|
+
return classes
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def progress_percent(value: float, min_value: float, max_value: float) -> float:
|
|
538
|
+
"""Return a clamped 0-100 width percentage for a determinate bar."""
|
|
539
|
+
if max_value == min_value:
|
|
540
|
+
return 0.0
|
|
541
|
+
percent = (value - min_value) / (max_value - min_value) * 100.0
|
|
542
|
+
if percent < 0.0:
|
|
543
|
+
return 0.0
|
|
544
|
+
if percent > 100.0:
|
|
545
|
+
return 100.0
|
|
546
|
+
return round(percent, 4)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def placeholder_structural_classes(
|
|
550
|
+
*,
|
|
551
|
+
animation: str | None = None,
|
|
552
|
+
size: str | None = None,
|
|
553
|
+
color: str | None = None,
|
|
554
|
+
button: bool = False,
|
|
555
|
+
display: bool | str | None = None,
|
|
556
|
+
width: int | str | None = None,
|
|
557
|
+
xs: int | bool | None = None,
|
|
558
|
+
sm: int | bool | None = None,
|
|
559
|
+
md: int | bool | None = None,
|
|
560
|
+
lg: int | bool | None = None,
|
|
561
|
+
xl: int | bool | None = None,
|
|
562
|
+
xxl: int | bool | None = None,
|
|
563
|
+
) -> list[str]:
|
|
564
|
+
"""Return Bootstrap ``placeholder`` classes."""
|
|
565
|
+
classes = ["placeholder"]
|
|
566
|
+
if button:
|
|
567
|
+
classes.append("btn")
|
|
568
|
+
classes.append("disabled")
|
|
569
|
+
if color is not None:
|
|
570
|
+
_validate_color(color)
|
|
571
|
+
classes.append(f"btn-{color}")
|
|
572
|
+
elif color is not None:
|
|
573
|
+
_validate_color(color)
|
|
574
|
+
classes.append(f"bg-{color}")
|
|
575
|
+
if animation is not None:
|
|
576
|
+
if animation not in _PLACEHOLDER_ANIMATIONS:
|
|
577
|
+
raise ValueError(f"Unknown placeholder animation {animation!r}")
|
|
578
|
+
classes.append(f"placeholder-{animation}")
|
|
579
|
+
if size is not None:
|
|
580
|
+
if size not in _PLACEHOLDER_SIZES:
|
|
581
|
+
raise ValueError(f"Unknown placeholder size {size!r}")
|
|
582
|
+
classes.append(f"placeholder-{size}")
|
|
583
|
+
for breakpoint, value in (
|
|
584
|
+
("xs", xs),
|
|
585
|
+
("sm", sm),
|
|
586
|
+
("md", md),
|
|
587
|
+
("lg", lg),
|
|
588
|
+
("xl", xl),
|
|
589
|
+
("xxl", xxl),
|
|
590
|
+
):
|
|
591
|
+
classes.extend(_col_width_classes(breakpoint, value))
|
|
592
|
+
if isinstance(width, bool) or (width is not None and not isinstance(width, (int, str))):
|
|
593
|
+
raise ValueError(f"Unknown placeholder width {width!r}")
|
|
594
|
+
if isinstance(width, int):
|
|
595
|
+
if width in _WIDTH_UTILITIES:
|
|
596
|
+
classes.append(f"w-{width}")
|
|
597
|
+
elif 1 <= width <= 12:
|
|
598
|
+
classes.append(f"col-{width}")
|
|
599
|
+
else:
|
|
600
|
+
raise ValueError(f"Unknown placeholder width {width!r}")
|
|
601
|
+
if display is False or display == "none":
|
|
602
|
+
classes.append("d-none")
|
|
603
|
+
elif display is True or display in {None, "show", "auto"}:
|
|
604
|
+
pass
|
|
605
|
+
elif isinstance(display, str) and display in _DISPLAY_UTILITIES:
|
|
606
|
+
classes.append(f"d-{display}")
|
|
607
|
+
elif display is not None:
|
|
608
|
+
raise ValueError(f"Unknown display {display!r}")
|
|
609
|
+
return classes
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def _index_header_label(df: Any, index_label: str | None) -> str:
|
|
613
|
+
if index_label is not None:
|
|
614
|
+
return index_label
|
|
615
|
+
name = getattr(df.index, "name", None)
|
|
616
|
+
if name is not None:
|
|
617
|
+
return str(name)
|
|
618
|
+
names = getattr(df.index, "names", None)
|
|
619
|
+
if names:
|
|
620
|
+
parts = [str(part) for part in names if part is not None]
|
|
621
|
+
return " ".join(parts)
|
|
622
|
+
return ""
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _write_dataframe_header(df: Any, *, index: bool, index_label: str | None) -> None:
|
|
626
|
+
index_label_text = _index_header_label(df, index_label) if index else ""
|
|
627
|
+
columns = df.columns
|
|
628
|
+
nlevels = int(getattr(columns, "nlevels", 1))
|
|
629
|
+
if nlevels > 1 and hasattr(columns, "get_level_values"):
|
|
630
|
+
for level in range(nlevels):
|
|
631
|
+
with ui.element("tr"):
|
|
632
|
+
if index:
|
|
633
|
+
th = ui.element("th")
|
|
634
|
+
set_element_text(th, index_label_text if level == nlevels - 1 else "")
|
|
635
|
+
values = list(columns.get_level_values(level))
|
|
636
|
+
for value, span in grouped_spans(values):
|
|
637
|
+
th = ui.element("th")
|
|
638
|
+
set_element_text(th, "" if value is None else str(value))
|
|
639
|
+
if span > 1:
|
|
640
|
+
th._props["colspan"] = span
|
|
641
|
+
return
|
|
642
|
+
with ui.element("tr"):
|
|
643
|
+
if index:
|
|
644
|
+
th = ui.element("th")
|
|
645
|
+
set_element_text(th, index_label_text)
|
|
646
|
+
for col in columns:
|
|
647
|
+
th = ui.element("th")
|
|
648
|
+
set_element_text(th, "" if col is None else str(col))
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def _write_dataframe_body(
|
|
652
|
+
df: Any,
|
|
653
|
+
*,
|
|
654
|
+
index: bool,
|
|
655
|
+
date_format: str | None,
|
|
656
|
+
float_format: str | Callable[..., Any] | None,
|
|
657
|
+
) -> None:
|
|
658
|
+
for idx, row in df.iterrows():
|
|
659
|
+
with ui.element("tr"):
|
|
660
|
+
if index:
|
|
661
|
+
th = ui.element("th")
|
|
662
|
+
set_element_text(
|
|
663
|
+
th, format_table_cell(idx, date_format=date_format, float_format=float_format)
|
|
664
|
+
)
|
|
665
|
+
th._props["scope"] = "row"
|
|
666
|
+
for col in df.columns:
|
|
667
|
+
td = ui.element("td")
|
|
668
|
+
set_element_text(
|
|
669
|
+
td,
|
|
670
|
+
format_table_cell(row[col], date_format=date_format, float_format=float_format),
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
class _CardImpl(BootstrapElement):
|
|
675
|
+
"""Private Card implementation."""
|
|
676
|
+
|
|
677
|
+
component_name = "Card"
|
|
678
|
+
children_kind = "auto"
|
|
679
|
+
reserved_classes = ("card",)
|
|
680
|
+
|
|
681
|
+
def __init__(
|
|
682
|
+
self,
|
|
683
|
+
children: object = None,
|
|
684
|
+
*,
|
|
685
|
+
_surface: str,
|
|
686
|
+
color: str | None = None,
|
|
687
|
+
outline: bool = False,
|
|
688
|
+
inverse: bool = False,
|
|
689
|
+
body: bool = False,
|
|
690
|
+
**kwargs: Any,
|
|
691
|
+
) -> None:
|
|
692
|
+
self._surface = _surface
|
|
693
|
+
self._structural_classes = tuple(
|
|
694
|
+
card_structural_classes(color=color, outline=outline, inverse=inverse, body=body)
|
|
695
|
+
)
|
|
696
|
+
tag = kwargs.pop("tag", "div")
|
|
697
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
Card, DbcCard = make_surface_classes("Card", globals())
|
|
701
|
+
card = Card
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
class _CardBodyImpl(BootstrapElement):
|
|
705
|
+
"""Private CardBody implementation."""
|
|
706
|
+
|
|
707
|
+
component_name = "CardBody"
|
|
708
|
+
children_kind = "auto"
|
|
709
|
+
reserved_classes = ("card-body",)
|
|
710
|
+
|
|
711
|
+
def __init__(self, children: object = None, *, _surface: str, **kwargs: Any) -> None:
|
|
712
|
+
self._surface = _surface
|
|
713
|
+
self._structural_classes = ("card-body",)
|
|
714
|
+
tag = kwargs.pop("tag", "div")
|
|
715
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
CardBody, DbcCardBody = make_surface_classes("CardBody", globals())
|
|
719
|
+
card_body = CardBody
|
|
720
|
+
|
|
721
|
+
|
|
722
|
+
class _CardHeaderImpl(BootstrapElement):
|
|
723
|
+
"""Private CardHeader implementation."""
|
|
724
|
+
|
|
725
|
+
component_name = "CardHeader"
|
|
726
|
+
children_kind = "auto"
|
|
727
|
+
reserved_classes = ("card-header",)
|
|
728
|
+
|
|
729
|
+
def __init__(self, children: object = None, *, _surface: str, **kwargs: Any) -> None:
|
|
730
|
+
self._surface = _surface
|
|
731
|
+
self._structural_classes = ("card-header",)
|
|
732
|
+
tag = kwargs.pop("tag", "div")
|
|
733
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
CardHeader, DbcCardHeader = make_surface_classes("CardHeader", globals())
|
|
737
|
+
card_header = CardHeader
|
|
738
|
+
|
|
739
|
+
|
|
740
|
+
class _CardFooterImpl(BootstrapElement):
|
|
741
|
+
"""Private CardFooter implementation."""
|
|
742
|
+
|
|
743
|
+
component_name = "CardFooter"
|
|
744
|
+
children_kind = "auto"
|
|
745
|
+
reserved_classes = ("card-footer",)
|
|
746
|
+
|
|
747
|
+
def __init__(self, children: object = None, *, _surface: str, **kwargs: Any) -> None:
|
|
748
|
+
self._surface = _surface
|
|
749
|
+
self._structural_classes = ("card-footer",)
|
|
750
|
+
tag = kwargs.pop("tag", "div")
|
|
751
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
CardFooter, DbcCardFooter = make_surface_classes("CardFooter", globals())
|
|
755
|
+
card_footer = CardFooter
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
class _CardGroupImpl(BootstrapElement):
|
|
759
|
+
"""Private CardGroup implementation."""
|
|
760
|
+
|
|
761
|
+
component_name = "CardGroup"
|
|
762
|
+
children_kind = "grouping"
|
|
763
|
+
reserved_classes = ("card-group",)
|
|
764
|
+
|
|
765
|
+
def __init__(self, children: object = None, *, _surface: str, **kwargs: Any) -> None:
|
|
766
|
+
self._surface = _surface
|
|
767
|
+
self._structural_classes = ("card-group",)
|
|
768
|
+
tag = kwargs.pop("tag", "div")
|
|
769
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
CardGroup, DbcCardGroup = make_surface_classes("CardGroup", globals())
|
|
773
|
+
card_group = CardGroup
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
class _CardImgImpl(BootstrapElement):
|
|
777
|
+
"""Private CardImg implementation."""
|
|
778
|
+
|
|
779
|
+
component_name = "CardImg"
|
|
780
|
+
children_kind = "auto"
|
|
781
|
+
reserved_classes = ()
|
|
782
|
+
|
|
783
|
+
def __init__(
|
|
784
|
+
self,
|
|
785
|
+
children: object = None,
|
|
786
|
+
*,
|
|
787
|
+
_surface: str,
|
|
788
|
+
src: str | None = None,
|
|
789
|
+
alt: str = "",
|
|
790
|
+
top: bool = False,
|
|
791
|
+
bottom: bool = False,
|
|
792
|
+
overlay: bool = False,
|
|
793
|
+
**kwargs: Any,
|
|
794
|
+
) -> None:
|
|
795
|
+
self._surface = _surface
|
|
796
|
+
self._structural_classes = tuple(
|
|
797
|
+
card_img_structural_classes(top=top, bottom=bottom, overlay=overlay)
|
|
798
|
+
)
|
|
799
|
+
tag = kwargs.pop("tag", "img")
|
|
800
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
801
|
+
if src is not None:
|
|
802
|
+
self._props["src"] = src
|
|
803
|
+
self._props["alt"] = alt
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
CardImg, DbcCardImg = make_surface_classes("CardImg", globals())
|
|
807
|
+
card_img = CardImg
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
class _CardImgOverlayImpl(BootstrapElement):
|
|
811
|
+
"""Private CardImgOverlay implementation."""
|
|
812
|
+
|
|
813
|
+
component_name = "CardImgOverlay"
|
|
814
|
+
children_kind = "auto"
|
|
815
|
+
reserved_classes = ("card-img-overlay",)
|
|
816
|
+
|
|
817
|
+
def __init__(self, children: object = None, *, _surface: str, **kwargs: Any) -> None:
|
|
818
|
+
self._surface = _surface
|
|
819
|
+
self._structural_classes = ("card-img-overlay",)
|
|
820
|
+
tag = kwargs.pop("tag", "div")
|
|
821
|
+
super().__init__(children, tag=tag, **kwargs)
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
CardImgOverlay, DbcCardImgOverlay = make_surface_classes("CardImgOverlay", globals())
|
|
825
|
+
card_img_overlay = CardImgOverlay
|
|
826
|
+
|
|
827
|
+
|
|
828
|
+
class _CardLinkImpl(BootstrapElement):
|
|
829
|
+
"""Private CardLink implementation. Click listener is registered in ``__init__`` only."""
|
|
830
|
+
|
|
831
|
+
component_name = "CardLink"
|
|
832
|
+
children_kind = "phrasing"
|
|
833
|
+
reserved_classes = ("card-link",)
|
|
834
|
+
|
|
835
|
+
def __init__(
|
|
836
|
+
self,
|
|
837
|
+
children: object = None,
|
|
838
|
+
*,
|
|
839
|
+
_surface: str,
|
|
840
|
+
href: str | None = None,
|
|
841
|
+
external_link: bool = False,
|
|
842
|
+
n_clicks: int = 0,
|
|
843
|
+
on_click: Callable[..., Any] | None = None,
|
|
844
|
+
**kwargs: Any,
|
|
845
|
+
) -> None:
|
|
846
|
+
self._surface = _surface
|
|
847
|
+
self._structural_classes = ("card-link",)
|
|
848
|
+
self._n_clicks = int(n_clicks)
|
|
849
|
+
self._on_click = on_click
|
|
850
|
+
kwargs.pop("tag", None)
|
|
851
|
+
super().__init__(children, tag="a", **kwargs)
|
|
852
|
+
_apply_interactive_props(self, tag="a", href=href, external_link=external_link)
|
|
853
|
+
self.on("click", self._handle_click)
|
|
854
|
+
|
|
855
|
+
def _handle_click(self, *_args: Any, **_kwargs: Any) -> None:
|
|
856
|
+
self._n_clicks += 1
|
|
857
|
+
if self._on_click is not None:
|
|
858
|
+
self._on_click()
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
CardLink, DbcCardLink = make_surface_classes("CardLink", globals())
|
|
862
|
+
card_link = CardLink
|
|
863
|
+
|
|
864
|
+
|
|
865
|
+
class _ListGroupImpl(BootstrapElement):
|
|
866
|
+
"""Private ListGroup implementation. Tag is fixed in ``__init__``."""
|
|
867
|
+
|
|
868
|
+
component_name = "ListGroup"
|
|
869
|
+
children_kind = "grouping"
|
|
870
|
+
reserved_classes = ("list-group",)
|
|
871
|
+
|
|
872
|
+
def __init__(
|
|
873
|
+
self,
|
|
874
|
+
children: object = None,
|
|
875
|
+
*,
|
|
876
|
+
_surface: str,
|
|
877
|
+
flush: bool = False,
|
|
878
|
+
numbered: bool = False,
|
|
879
|
+
horizontal: bool | str = False,
|
|
880
|
+
**kwargs: Any,
|
|
881
|
+
) -> None:
|
|
882
|
+
self._surface = _surface
|
|
883
|
+
self._structural_classes = tuple(
|
|
884
|
+
list_group_structural_classes(flush=flush, numbered=numbered, horizontal=horizontal)
|
|
885
|
+
)
|
|
886
|
+
explicit_tag = kwargs.pop("tag", None)
|
|
887
|
+
self._resolved_tag = resolve_list_group_tag(tag=explicit_tag, numbered=numbered)
|
|
888
|
+
super().__init__(children, tag=self._resolved_tag, **kwargs)
|
|
889
|
+
|
|
890
|
+
def __enter__(self) -> Any:
|
|
891
|
+
if self._surface == "native":
|
|
892
|
+
default = "li" if self._resolved_tag in {"ul", "ol"} else "div"
|
|
893
|
+
self._item_tag_token: Token[str | None] | None = _LIST_GROUP_ITEM_DEFAULT_TAG.set(
|
|
894
|
+
default
|
|
895
|
+
)
|
|
896
|
+
return super().__enter__()
|
|
897
|
+
|
|
898
|
+
def __exit__(self, *args: Any) -> Any:
|
|
899
|
+
try:
|
|
900
|
+
return super().__exit__(*args)
|
|
901
|
+
finally:
|
|
902
|
+
token = getattr(self, "_item_tag_token", None)
|
|
903
|
+
if token is not None:
|
|
904
|
+
_LIST_GROUP_ITEM_DEFAULT_TAG.reset(token)
|
|
905
|
+
self._item_tag_token = None
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
ListGroup, DbcListGroup = make_surface_classes("ListGroup", globals())
|
|
909
|
+
list_group = ListGroup
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
class _ListGroupItemImpl(BootstrapElement):
|
|
913
|
+
"""Private ListGroupItem implementation. Click listener is registered in ``__init__`` only."""
|
|
914
|
+
|
|
915
|
+
component_name = "ListGroupItem"
|
|
916
|
+
children_kind = "auto"
|
|
917
|
+
reserved_classes = ("list-group-item",)
|
|
918
|
+
|
|
919
|
+
def __init__(
|
|
920
|
+
self,
|
|
921
|
+
children: object = None,
|
|
922
|
+
*,
|
|
923
|
+
_surface: str,
|
|
924
|
+
active: bool = False,
|
|
925
|
+
disabled: bool = False,
|
|
926
|
+
color: str | None = None,
|
|
927
|
+
action: bool = False,
|
|
928
|
+
href: str | None = None,
|
|
929
|
+
external_link: bool = False,
|
|
930
|
+
n_clicks: int = 0,
|
|
931
|
+
on_click: Callable[..., Any] | None = None,
|
|
932
|
+
**kwargs: Any,
|
|
933
|
+
) -> None:
|
|
934
|
+
self._surface = _surface
|
|
935
|
+
self._structural_classes = tuple(
|
|
936
|
+
list_group_item_structural_classes(
|
|
937
|
+
active=active,
|
|
938
|
+
disabled=disabled,
|
|
939
|
+
color=color,
|
|
940
|
+
action=action,
|
|
941
|
+
href=href,
|
|
942
|
+
)
|
|
943
|
+
)
|
|
944
|
+
explicit_tag = kwargs.pop("tag", None)
|
|
945
|
+
self._resolved_tag = resolve_list_group_item_tag(
|
|
946
|
+
tag=explicit_tag,
|
|
947
|
+
href=href,
|
|
948
|
+
action=action,
|
|
949
|
+
context_default=_LIST_GROUP_ITEM_DEFAULT_TAG.get(),
|
|
950
|
+
)
|
|
951
|
+
self._n_clicks = int(n_clicks)
|
|
952
|
+
self._on_click = on_click
|
|
953
|
+
super().__init__(children, tag=self._resolved_tag, **kwargs)
|
|
954
|
+
_apply_interactive_props(
|
|
955
|
+
self,
|
|
956
|
+
tag=self._resolved_tag,
|
|
957
|
+
href=href,
|
|
958
|
+
disabled=disabled,
|
|
959
|
+
external_link=external_link,
|
|
960
|
+
)
|
|
961
|
+
self.on("click", self._handle_click)
|
|
962
|
+
|
|
963
|
+
def _handle_click(self, *_args: Any, **_kwargs: Any) -> None:
|
|
964
|
+
self._n_clicks += 1
|
|
965
|
+
if self._on_click is not None:
|
|
966
|
+
self._on_click()
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
ListGroupItem, DbcListGroupItem = make_surface_classes("ListGroupItem", globals())
|
|
970
|
+
list_group_item = ListGroupItem
|
|
971
|
+
|
|
972
|
+
|
|
973
|
+
class _TableImpl(BootstrapElement):
|
|
974
|
+
"""Private Table implementation."""
|
|
975
|
+
|
|
976
|
+
component_name = "Table"
|
|
977
|
+
children_kind = "grouping"
|
|
978
|
+
reserved_classes = ()
|
|
979
|
+
|
|
980
|
+
def __init__(
|
|
981
|
+
self,
|
|
982
|
+
children: object = None,
|
|
983
|
+
*,
|
|
984
|
+
_surface: str,
|
|
985
|
+
bordered: bool = False,
|
|
986
|
+
borderless: bool = False,
|
|
987
|
+
striped: bool = False,
|
|
988
|
+
hover: bool = False,
|
|
989
|
+
size: str | None = None,
|
|
990
|
+
responsive: bool | str = False,
|
|
991
|
+
color: str | None = None,
|
|
992
|
+
striped_columns: bool = False,
|
|
993
|
+
**kwargs: Any,
|
|
994
|
+
) -> None:
|
|
995
|
+
self._surface = _surface
|
|
996
|
+
self._inner: ui.element | None = None
|
|
997
|
+
if "dark" in kwargs:
|
|
998
|
+
kwargs.pop("dark")
|
|
999
|
+
if _surface == "compat":
|
|
1000
|
+
raise UnsupportedPropError("dark")
|
|
1001
|
+
raise ValueError("Table dark was removed; color mode comes from theme")
|
|
1002
|
+
small = False
|
|
1003
|
+
if "small" in kwargs:
|
|
1004
|
+
if _surface == "compat":
|
|
1005
|
+
raise UnsupportedPropError("small")
|
|
1006
|
+
small = bool(kwargs.pop("small"))
|
|
1007
|
+
tbl_classes = table_structural_classes(
|
|
1008
|
+
bordered=bordered,
|
|
1009
|
+
borderless=borderless,
|
|
1010
|
+
striped=striped,
|
|
1011
|
+
hover=hover,
|
|
1012
|
+
small=small,
|
|
1013
|
+
size=size,
|
|
1014
|
+
color=color,
|
|
1015
|
+
striped_columns=striped_columns,
|
|
1016
|
+
)
|
|
1017
|
+
resp_class = table_responsive_class(responsive)
|
|
1018
|
+
if resp_class is not None:
|
|
1019
|
+
user_class: str | None = None
|
|
1020
|
+
class_name = kwargs.pop("class_name", None)
|
|
1021
|
+
class_name_camel = kwargs.pop("className", None)
|
|
1022
|
+
if class_name is not None or class_name_camel is not None:
|
|
1023
|
+
user_class = resolve_class_name(class_name, class_name_camel, self._surface)
|
|
1024
|
+
self._structural_classes: tuple[str, ...] = (resp_class,)
|
|
1025
|
+
super().__init__(None, tag="div", **kwargs)
|
|
1026
|
+
inner = ui.element("table")
|
|
1027
|
+
inner.classes(" ".join(tbl_classes))
|
|
1028
|
+
if user_class:
|
|
1029
|
+
inner.classes(user_class)
|
|
1030
|
+
inner.move(self)
|
|
1031
|
+
self._inner = inner
|
|
1032
|
+
_place_children(inner, children)
|
|
1033
|
+
return
|
|
1034
|
+
self._structural_classes = tuple(tbl_classes)
|
|
1035
|
+
super().__init__(children, tag="table", **kwargs)
|
|
1036
|
+
|
|
1037
|
+
def __enter__(self) -> Any:
|
|
1038
|
+
if self._inner is not None:
|
|
1039
|
+
return self._inner.__enter__()
|
|
1040
|
+
return super().__enter__()
|
|
1041
|
+
|
|
1042
|
+
def __exit__(self, *args: Any) -> Any:
|
|
1043
|
+
if self._inner is not None:
|
|
1044
|
+
return self._inner.__exit__(*args)
|
|
1045
|
+
return super().__exit__(*args)
|
|
1046
|
+
|
|
1047
|
+
@classmethod
|
|
1048
|
+
def from_dataframe(
|
|
1049
|
+
cls,
|
|
1050
|
+
df: object,
|
|
1051
|
+
*,
|
|
1052
|
+
columns: str | Sequence[str] | None = None,
|
|
1053
|
+
header: bool = True,
|
|
1054
|
+
index: bool = True,
|
|
1055
|
+
index_label: str | None = None,
|
|
1056
|
+
date_format: str | None = None,
|
|
1057
|
+
float_format: str | Callable[..., Any] | None = None,
|
|
1058
|
+
caption: str | None = None,
|
|
1059
|
+
**table_props: Any,
|
|
1060
|
+
) -> _TableImpl:
|
|
1061
|
+
"""Build a Table from a pandas DataFrame.
|
|
1062
|
+
|
|
1063
|
+
pandas is an optional extra. Missing pandas raises ImportError.
|
|
1064
|
+
"""
|
|
1065
|
+
pd_mod = _require_pandas()
|
|
1066
|
+
frame = df if isinstance(df, pd_mod.DataFrame) else pd_mod.DataFrame(df)
|
|
1067
|
+
if columns is not None:
|
|
1068
|
+
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
1069
|
+
frame = frame.loc[:, col_list]
|
|
1070
|
+
table = cls(**table_props)
|
|
1071
|
+
with table:
|
|
1072
|
+
if caption is not None:
|
|
1073
|
+
cap_el = ui.element("caption")
|
|
1074
|
+
set_element_text(cap_el, str(caption))
|
|
1075
|
+
if header:
|
|
1076
|
+
with ui.element("thead"):
|
|
1077
|
+
_write_dataframe_header(frame, index=index, index_label=index_label)
|
|
1078
|
+
with ui.element("tbody"):
|
|
1079
|
+
_write_dataframe_body(
|
|
1080
|
+
frame,
|
|
1081
|
+
index=index,
|
|
1082
|
+
date_format=date_format,
|
|
1083
|
+
float_format=float_format,
|
|
1084
|
+
)
|
|
1085
|
+
return table
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
Table, DbcTable = make_surface_classes("Table", globals())
|
|
1089
|
+
table = Table
|
|
1090
|
+
|
|
1091
|
+
|
|
1092
|
+
class _BadgeImpl(BootstrapElement):
|
|
1093
|
+
"""Private Badge implementation. Click listener is registered in ``__init__`` only."""
|
|
1094
|
+
|
|
1095
|
+
component_name = "Badge"
|
|
1096
|
+
children_kind = "phrasing"
|
|
1097
|
+
reserved_classes = ("badge",)
|
|
1098
|
+
|
|
1099
|
+
def __init__(
|
|
1100
|
+
self,
|
|
1101
|
+
children: object = None,
|
|
1102
|
+
*,
|
|
1103
|
+
_surface: str,
|
|
1104
|
+
color: str = "secondary",
|
|
1105
|
+
pill: bool = False,
|
|
1106
|
+
text_color: str | None = None,
|
|
1107
|
+
href: str | None = None,
|
|
1108
|
+
external_link: bool = False,
|
|
1109
|
+
n_clicks: int = 0,
|
|
1110
|
+
on_click: Callable[..., Any] | None = None,
|
|
1111
|
+
**kwargs: Any,
|
|
1112
|
+
) -> None:
|
|
1113
|
+
self._surface = _surface
|
|
1114
|
+
self._structural_classes = tuple(
|
|
1115
|
+
badge_structural_classes(color=color, pill=pill, text_color=text_color)
|
|
1116
|
+
)
|
|
1117
|
+
explicit_tag = kwargs.pop("tag", None)
|
|
1118
|
+
resolved_tag = "a" if href is not None else (explicit_tag or "span")
|
|
1119
|
+
self._n_clicks = int(n_clicks)
|
|
1120
|
+
self._on_click = on_click
|
|
1121
|
+
super().__init__(children, tag=resolved_tag, **kwargs)
|
|
1122
|
+
_apply_interactive_props(self, tag=resolved_tag, href=href, external_link=external_link)
|
|
1123
|
+
self.on("click", self._handle_click)
|
|
1124
|
+
|
|
1125
|
+
def _handle_click(self, *_args: Any, **_kwargs: Any) -> None:
|
|
1126
|
+
self._n_clicks += 1
|
|
1127
|
+
if self._on_click is not None:
|
|
1128
|
+
self._on_click()
|
|
1129
|
+
|
|
1130
|
+
|
|
1131
|
+
Badge, DbcBadge = make_surface_classes("Badge", globals())
|
|
1132
|
+
badge = Badge
|
|
1133
|
+
|
|
1134
|
+
|
|
1135
|
+
class _ProgressImpl(BootstrapElement):
|
|
1136
|
+
"""Private Progress implementation."""
|
|
1137
|
+
|
|
1138
|
+
component_name = "Progress"
|
|
1139
|
+
children_kind = "auto"
|
|
1140
|
+
reserved_classes = ()
|
|
1141
|
+
|
|
1142
|
+
def __init__(
|
|
1143
|
+
self,
|
|
1144
|
+
children: object = None,
|
|
1145
|
+
*,
|
|
1146
|
+
_surface: str,
|
|
1147
|
+
value: float = 0,
|
|
1148
|
+
min: float = 0,
|
|
1149
|
+
max: float = 100,
|
|
1150
|
+
label: str | None = None,
|
|
1151
|
+
animated: bool = False,
|
|
1152
|
+
striped: bool = False,
|
|
1153
|
+
color: str | None = None,
|
|
1154
|
+
height: int | float | str | None = None,
|
|
1155
|
+
bar: bool = False,
|
|
1156
|
+
bar_class_name: str | None = None,
|
|
1157
|
+
barClassName: str | None = None,
|
|
1158
|
+
bar_style: dict[str, Any] | str | None = None,
|
|
1159
|
+
**kwargs: Any,
|
|
1160
|
+
) -> None:
|
|
1161
|
+
self._surface = _surface
|
|
1162
|
+
min_value = float(min)
|
|
1163
|
+
max_value = float(max)
|
|
1164
|
+
value_n = float(value)
|
|
1165
|
+
self._value = value_n
|
|
1166
|
+
self._min = min_value
|
|
1167
|
+
self._max = max_value
|
|
1168
|
+
self._label = label
|
|
1169
|
+
self._bar_style = bar_style
|
|
1170
|
+
extra_bar = None
|
|
1171
|
+
if bar_class_name is not None or barClassName is not None:
|
|
1172
|
+
extra_bar = resolve_class_name(bar_class_name, barClassName, self._surface)
|
|
1173
|
+
self._bar_classes = progress_bar_structural_classes(
|
|
1174
|
+
color=color, striped=striped, animated=animated
|
|
1175
|
+
)
|
|
1176
|
+
if extra_bar:
|
|
1177
|
+
self._bar_classes = [*self._bar_classes, extra_bar]
|
|
1178
|
+
self._nested = bool(bar) or _PROGRESS_NESTED.get()
|
|
1179
|
+
kwargs.pop("tag", None)
|
|
1180
|
+
if height is not None and not self._nested:
|
|
1181
|
+
if isinstance(height, bool):
|
|
1182
|
+
raise ValueError("height must be a CSS length or number of pixels")
|
|
1183
|
+
height_css = f"{height}px" if isinstance(height, (int, float)) else str(height)
|
|
1184
|
+
_merge_style_kwarg(kwargs, {"height": height_css})
|
|
1185
|
+
if self._nested:
|
|
1186
|
+
self._structural_classes = tuple(self._bar_classes)
|
|
1187
|
+
super().__init__(children, tag="div", **kwargs)
|
|
1188
|
+
_configure_progress_bar(
|
|
1189
|
+
self,
|
|
1190
|
+
classes=[],
|
|
1191
|
+
value=value_n,
|
|
1192
|
+
min_value=min_value,
|
|
1193
|
+
max_value=max_value,
|
|
1194
|
+
label=label,
|
|
1195
|
+
bar_style=bar_style,
|
|
1196
|
+
)
|
|
1197
|
+
self._bar = None
|
|
1198
|
+
return
|
|
1199
|
+
self._structural_classes = tuple(progress_structural_classes())
|
|
1200
|
+
super().__init__(children, tag="div", **kwargs)
|
|
1201
|
+
bar_el = ui.element("div")
|
|
1202
|
+
_configure_progress_bar(
|
|
1203
|
+
bar_el,
|
|
1204
|
+
classes=self._bar_classes,
|
|
1205
|
+
value=value_n,
|
|
1206
|
+
min_value=min_value,
|
|
1207
|
+
max_value=max_value,
|
|
1208
|
+
label=label,
|
|
1209
|
+
bar_style=bar_style,
|
|
1210
|
+
)
|
|
1211
|
+
bar_el.move(self, target_index=0)
|
|
1212
|
+
self._bar = bar_el
|
|
1213
|
+
self._convert_nested_progress_children(children)
|
|
1214
|
+
|
|
1215
|
+
def _convert_nested_progress_children(self, children: object) -> None:
|
|
1216
|
+
if children is None or isinstance(children, str):
|
|
1217
|
+
return
|
|
1218
|
+
for child in _declared_children(children):
|
|
1219
|
+
convert = getattr(child, "_become_nested_bar", None)
|
|
1220
|
+
if callable(convert):
|
|
1221
|
+
convert()
|
|
1222
|
+
|
|
1223
|
+
def _become_nested_bar(self) -> None:
|
|
1224
|
+
if self._nested:
|
|
1225
|
+
return
|
|
1226
|
+
self._nested = True
|
|
1227
|
+
self.classes(remove="progress")
|
|
1228
|
+
extra = " ".join(self._bar_classes)
|
|
1229
|
+
if extra:
|
|
1230
|
+
self.classes(add=extra)
|
|
1231
|
+
label = self._label
|
|
1232
|
+
bar = getattr(self, "_bar", None)
|
|
1233
|
+
if bar is not None:
|
|
1234
|
+
bar_text = getattr(bar, "text", None)
|
|
1235
|
+
if bar_text:
|
|
1236
|
+
label = str(bar_text)
|
|
1237
|
+
delete = getattr(bar, "delete", None)
|
|
1238
|
+
if callable(delete):
|
|
1239
|
+
delete()
|
|
1240
|
+
self._bar = None
|
|
1241
|
+
_configure_progress_bar(
|
|
1242
|
+
self,
|
|
1243
|
+
classes=[],
|
|
1244
|
+
value=self._value,
|
|
1245
|
+
min_value=self._min,
|
|
1246
|
+
max_value=self._max,
|
|
1247
|
+
label=label,
|
|
1248
|
+
bar_style=self._bar_style,
|
|
1249
|
+
)
|
|
1250
|
+
|
|
1251
|
+
def __enter__(self) -> Any:
|
|
1252
|
+
self._nested_token: Token[bool] | None = _PROGRESS_NESTED.set(True)
|
|
1253
|
+
try:
|
|
1254
|
+
return super().__enter__()
|
|
1255
|
+
except BaseException:
|
|
1256
|
+
_PROGRESS_NESTED.reset(self._nested_token)
|
|
1257
|
+
raise
|
|
1258
|
+
|
|
1259
|
+
def __exit__(self, *args: Any) -> Any:
|
|
1260
|
+
try:
|
|
1261
|
+
return super().__exit__(*args)
|
|
1262
|
+
finally:
|
|
1263
|
+
token = getattr(self, "_nested_token", None)
|
|
1264
|
+
if token is not None:
|
|
1265
|
+
_PROGRESS_NESTED.reset(token)
|
|
1266
|
+
self._nested_token = None
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
Progress, DbcProgress = make_surface_classes("Progress", globals())
|
|
1270
|
+
progress = Progress
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
class _PlaceholderImpl(BootstrapElement):
|
|
1274
|
+
"""Private Placeholder implementation."""
|
|
1275
|
+
|
|
1276
|
+
component_name = "Placeholder"
|
|
1277
|
+
children_kind = "auto"
|
|
1278
|
+
reserved_classes = ("placeholder",)
|
|
1279
|
+
|
|
1280
|
+
def __init__(
|
|
1281
|
+
self,
|
|
1282
|
+
children: object = None,
|
|
1283
|
+
*,
|
|
1284
|
+
_surface: str,
|
|
1285
|
+
animation: str | None = None,
|
|
1286
|
+
size: str | None = None,
|
|
1287
|
+
color: str | None = None,
|
|
1288
|
+
button: bool = False,
|
|
1289
|
+
loading: bool = False,
|
|
1290
|
+
display: bool | str | None = None,
|
|
1291
|
+
width: int | str | None = None,
|
|
1292
|
+
xs: int | bool | None = None,
|
|
1293
|
+
sm: int | bool | None = None,
|
|
1294
|
+
md: int | bool | None = None,
|
|
1295
|
+
lg: int | bool | None = None,
|
|
1296
|
+
xl: int | bool | None = None,
|
|
1297
|
+
xxl: int | bool | None = None,
|
|
1298
|
+
**kwargs: Any,
|
|
1299
|
+
) -> None:
|
|
1300
|
+
self._surface = _surface
|
|
1301
|
+
width_for_classes: int | None
|
|
1302
|
+
if isinstance(width, bool):
|
|
1303
|
+
raise ValueError(f"Unknown placeholder width {width!r}")
|
|
1304
|
+
width_for_classes = width if isinstance(width, int) else None
|
|
1305
|
+
self._structural_classes = tuple(
|
|
1306
|
+
placeholder_structural_classes(
|
|
1307
|
+
animation=animation,
|
|
1308
|
+
size=size,
|
|
1309
|
+
color=color,
|
|
1310
|
+
button=button,
|
|
1311
|
+
display=display,
|
|
1312
|
+
width=width_for_classes,
|
|
1313
|
+
xs=xs,
|
|
1314
|
+
sm=sm,
|
|
1315
|
+
md=md,
|
|
1316
|
+
lg=lg,
|
|
1317
|
+
xl=xl,
|
|
1318
|
+
xxl=xxl,
|
|
1319
|
+
)
|
|
1320
|
+
)
|
|
1321
|
+
explicit_tag = kwargs.pop("tag", None)
|
|
1322
|
+
if explicit_tag:
|
|
1323
|
+
resolved_tag = explicit_tag
|
|
1324
|
+
elif button:
|
|
1325
|
+
resolved_tag = "button"
|
|
1326
|
+
else:
|
|
1327
|
+
resolved_tag = "span"
|
|
1328
|
+
if isinstance(width, str):
|
|
1329
|
+
_merge_style_kwarg(kwargs, {"width": width})
|
|
1330
|
+
super().__init__(children, tag=resolved_tag, **kwargs)
|
|
1331
|
+
if resolved_tag == "button":
|
|
1332
|
+
self._props.setdefault("type", "button")
|
|
1333
|
+
self._props["disabled"] = True
|
|
1334
|
+
elif button:
|
|
1335
|
+
self._props["disabled"] = True
|
|
1336
|
+
if loading:
|
|
1337
|
+
self._props["aria-busy"] = "true"
|
|
1338
|
+
if not _children_present(children):
|
|
1339
|
+
self._props["aria-hidden"] = "true"
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
Placeholder, DbcPlaceholder = make_surface_classes("Placeholder", globals())
|
|
1343
|
+
placeholder = Placeholder
|