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.
Files changed (103) hide show
  1. nicegui_bootstrap_components/__init__.py +7 -0
  2. nicegui_bootstrap_components/_base.py +590 -0
  3. nicegui_bootstrap_components/_host.py +259 -0
  4. nicegui_bootstrap_components/assets.py +371 -0
  5. nicegui_bootstrap_components/bs/__init__.py +267 -0
  6. nicegui_bootstrap_components/bs/_actions.py +232 -0
  7. nicegui_bootstrap_components/bs/_alert_spinner.py +423 -0
  8. nicegui_bootstrap_components/bs/_carousel.py +312 -0
  9. nicegui_bootstrap_components/bs/_choices.py +779 -0
  10. nicegui_bootstrap_components/bs/_content.py +1343 -0
  11. nicegui_bootstrap_components/bs/_forms.py +378 -0
  12. nicegui_bootstrap_components/bs/_inputs.py +560 -0
  13. nicegui_bootstrap_components/bs/_layout.py +341 -0
  14. nicegui_bootstrap_components/bs/_nav.py +650 -0
  15. nicegui_bootstrap_components/bs/_navbar.py +445 -0
  16. nicegui_bootstrap_components/bs/_overlay_root.py +73 -0
  17. nicegui_bootstrap_components/bs/_overlays.py +2633 -0
  18. nicegui_bootstrap_components/bs/_scope.py +28 -0
  19. nicegui_bootstrap_components/bs/_tabs.py +605 -0
  20. nicegui_bootstrap_components/bs/_toast.py +137 -0
  21. nicegui_bootstrap_components/bs/_tooltip_popover.py +537 -0
  22. nicegui_bootstrap_components/contracts/dbc-2.0.4.json +16811 -0
  23. nicegui_bootstrap_components/dbc/__init__.py +108 -0
  24. nicegui_bootstrap_components/icons.py +29 -0
  25. nicegui_bootstrap_components/persist.py +201 -0
  26. nicegui_bootstrap_components/py.typed +0 -0
  27. nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff +0 -0
  28. nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff2 +0 -0
  29. nicegui_bootstrap_components/static/dist/ngbs-bootstrap-unscoped.css +12047 -0
  30. nicegui_bootstrap_components/static/dist/ngbs-bootstrap.css +12057 -0
  31. nicegui_bootstrap_components/static/dist/ngbs-cerulean-unscoped.css +12338 -0
  32. nicegui_bootstrap_components/static/dist/ngbs-cerulean.css +12348 -0
  33. nicegui_bootstrap_components/static/dist/ngbs-compat-unscoped.css +12 -0
  34. nicegui_bootstrap_components/static/dist/ngbs-compat.css +12 -0
  35. nicegui_bootstrap_components/static/dist/ngbs-cosmo-unscoped.css +11825 -0
  36. nicegui_bootstrap_components/static/dist/ngbs-cosmo.css +11835 -0
  37. nicegui_bootstrap_components/static/dist/ngbs-cyborg-unscoped.css +12213 -0
  38. nicegui_bootstrap_components/static/dist/ngbs-cyborg.css +12223 -0
  39. nicegui_bootstrap_components/static/dist/ngbs-darkly-unscoped.css +12143 -0
  40. nicegui_bootstrap_components/static/dist/ngbs-darkly.css +12153 -0
  41. nicegui_bootstrap_components/static/dist/ngbs-flatly-unscoped.css +12129 -0
  42. nicegui_bootstrap_components/static/dist/ngbs-flatly.css +12139 -0
  43. nicegui_bootstrap_components/static/dist/ngbs-grid-unscoped.css +4084 -0
  44. nicegui_bootstrap_components/static/dist/ngbs-grid.css +4094 -0
  45. nicegui_bootstrap_components/static/dist/ngbs-host-unscoped.css +9 -0
  46. nicegui_bootstrap_components/static/dist/ngbs-host.css +9 -0
  47. nicegui_bootstrap_components/static/dist/ngbs-icons-bootstrap.css +5 -0
  48. nicegui_bootstrap_components/static/dist/ngbs-icons-fontawesome.css +9 -0
  49. nicegui_bootstrap_components/static/dist/ngbs-journal-unscoped.css +12112 -0
  50. nicegui_bootstrap_components/static/dist/ngbs-journal.css +12122 -0
  51. nicegui_bootstrap_components/static/dist/ngbs-litera-unscoped.css +12214 -0
  52. nicegui_bootstrap_components/static/dist/ngbs-litera.css +12224 -0
  53. nicegui_bootstrap_components/static/dist/ngbs-lumen-unscoped.css +12377 -0
  54. nicegui_bootstrap_components/static/dist/ngbs-lumen.css +12387 -0
  55. nicegui_bootstrap_components/static/dist/ngbs-lux-unscoped.css +12048 -0
  56. nicegui_bootstrap_components/static/dist/ngbs-lux.css +12058 -0
  57. nicegui_bootstrap_components/static/dist/ngbs-materia-unscoped.css +13197 -0
  58. nicegui_bootstrap_components/static/dist/ngbs-materia.css +13207 -0
  59. nicegui_bootstrap_components/static/dist/ngbs-minty-unscoped.css +12184 -0
  60. nicegui_bootstrap_components/static/dist/ngbs-minty.css +12194 -0
  61. nicegui_bootstrap_components/static/dist/ngbs-morph-unscoped.css +12829 -0
  62. nicegui_bootstrap_components/static/dist/ngbs-morph.css +12839 -0
  63. nicegui_bootstrap_components/static/dist/ngbs-pulse-unscoped.css +11901 -0
  64. nicegui_bootstrap_components/static/dist/ngbs-pulse.css +11911 -0
  65. nicegui_bootstrap_components/static/dist/ngbs-quartz-unscoped.css +12625 -0
  66. nicegui_bootstrap_components/static/dist/ngbs-quartz.css +12635 -0
  67. nicegui_bootstrap_components/static/dist/ngbs-sandstone-unscoped.css +12214 -0
  68. nicegui_bootstrap_components/static/dist/ngbs-sandstone.css +12224 -0
  69. nicegui_bootstrap_components/static/dist/ngbs-simplex-unscoped.css +12199 -0
  70. nicegui_bootstrap_components/static/dist/ngbs-simplex.css +12209 -0
  71. nicegui_bootstrap_components/static/dist/ngbs-sketchy-unscoped.css +12518 -0
  72. nicegui_bootstrap_components/static/dist/ngbs-sketchy.css +12528 -0
  73. nicegui_bootstrap_components/static/dist/ngbs-slate-unscoped.css +12527 -0
  74. nicegui_bootstrap_components/static/dist/ngbs-slate.css +12537 -0
  75. nicegui_bootstrap_components/static/dist/ngbs-solar-unscoped.css +12152 -0
  76. nicegui_bootstrap_components/static/dist/ngbs-solar.css +12162 -0
  77. nicegui_bootstrap_components/static/dist/ngbs-spacelab-unscoped.css +12279 -0
  78. nicegui_bootstrap_components/static/dist/ngbs-spacelab.css +12289 -0
  79. nicegui_bootstrap_components/static/dist/ngbs-superhero-unscoped.css +12223 -0
  80. nicegui_bootstrap_components/static/dist/ngbs-superhero.css +12233 -0
  81. nicegui_bootstrap_components/static/dist/ngbs-united-unscoped.css +12090 -0
  82. nicegui_bootstrap_components/static/dist/ngbs-united.css +12100 -0
  83. nicegui_bootstrap_components/static/dist/ngbs-vapor-unscoped.css +12556 -0
  84. nicegui_bootstrap_components/static/dist/ngbs-vapor.css +12566 -0
  85. nicegui_bootstrap_components/static/dist/ngbs-yeti-unscoped.css +12333 -0
  86. nicegui_bootstrap_components/static/dist/ngbs-yeti.css +12343 -0
  87. nicegui_bootstrap_components/static/dist/ngbs-zephyr-unscoped.css +12296 -0
  88. nicegui_bootstrap_components/static/dist/ngbs-zephyr.css +12306 -0
  89. nicegui_bootstrap_components/static/prototype-ngbs.css +541 -0
  90. nicegui_bootstrap_components/static/webfonts/fa-brands-400.ttf +0 -0
  91. nicegui_bootstrap_components/static/webfonts/fa-brands-400.woff2 +0 -0
  92. nicegui_bootstrap_components/static/webfonts/fa-regular-400.ttf +0 -0
  93. nicegui_bootstrap_components/static/webfonts/fa-regular-400.woff2 +0 -0
  94. nicegui_bootstrap_components/static/webfonts/fa-solid-900.ttf +0 -0
  95. nicegui_bootstrap_components/static/webfonts/fa-solid-900.woff2 +0 -0
  96. nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.ttf +0 -0
  97. nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.woff2 +0 -0
  98. nicegui_bootstrap_components/theme.py +238 -0
  99. nicegui_bootstrap_components/themes.py +131 -0
  100. nicegui_bootstrap_components-0.1.0.dist-info/METADATA +234 -0
  101. nicegui_bootstrap_components-0.1.0.dist-info/RECORD +103 -0
  102. nicegui_bootstrap_components-0.1.0.dist-info/WHEEL +4 -0
  103. nicegui_bootstrap_components-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,423 @@
1
+ """Alert and Spinner family (native + compat sibling types)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ from collections.abc import Callable
7
+ from typing import Any
8
+
9
+ from nicegui import ui
10
+
11
+ from .._base import (
12
+ BootstrapElement,
13
+ make_surface_classes,
14
+ normalize_style,
15
+ resolve_class_name,
16
+ )
17
+ from .._host import set_element_text
18
+ from ._overlays import _adopt_into, _ensure_overlay_runtime, _portal_to_overlay_root
19
+
20
+ __all__ = [
21
+ "Alert",
22
+ "DbcAlert",
23
+ "alert",
24
+ "Spinner",
25
+ "DbcSpinner",
26
+ "spinner",
27
+ "alert_structural_classes",
28
+ "spinner_structural_classes",
29
+ "spinner_color_css",
30
+ "spinner_is_visible",
31
+ "spinner_fullscreen_classes",
32
+ ]
33
+
34
+ _SEMANTIC_COLORS: tuple[str, ...] = (
35
+ "primary",
36
+ "secondary",
37
+ "success",
38
+ "danger",
39
+ "warning",
40
+ "info",
41
+ "light",
42
+ "dark",
43
+ )
44
+ _SPINNER_TYPES: tuple[str, ...] = ("border", "grow")
45
+ _SPINNER_SIZES: tuple[str, ...] = ("sm",)
46
+ _SPINNER_DISPLAYS: tuple[str, ...] = ("auto", "show", "hide")
47
+
48
+
49
+ def alert_structural_classes(
50
+ *,
51
+ color: str,
52
+ dismissable: bool = False,
53
+ fade: bool = True,
54
+ is_open: bool = True,
55
+ ) -> list[str]:
56
+ """Return Bootstrap ``alert`` classes. Unknown colors raise ``ValueError``."""
57
+ if color not in _SEMANTIC_COLORS:
58
+ raise ValueError(f"Unknown Alert color {color!r}")
59
+ classes = ["alert", f"alert-{color}"]
60
+ if dismissable:
61
+ classes.append("alert-dismissible")
62
+ if fade:
63
+ classes.append("fade")
64
+ if is_open:
65
+ classes.append("show")
66
+ elif not is_open:
67
+ classes.append("d-none")
68
+ return classes
69
+
70
+
71
+ def spinner_structural_classes(
72
+ *,
73
+ type: str = "border",
74
+ size: str | None = None,
75
+ color: str | None = None,
76
+ ) -> list[str]:
77
+ """Return Bootstrap spinner classes. Unknown type or size raise ``ValueError``."""
78
+ if type not in _SPINNER_TYPES:
79
+ raise ValueError(f"Unknown Spinner type {type!r}; expected 'border' or 'grow'")
80
+ if size is not None and size not in _SPINNER_SIZES:
81
+ raise ValueError(f"Unknown Spinner size {size!r}; expected 'sm' or None")
82
+ classes = [f"spinner-{type}"]
83
+ if size is not None:
84
+ classes.append(f"spinner-{type}-{size}")
85
+ if color is not None and color in _SEMANTIC_COLORS:
86
+ classes.append(f"text-{color}")
87
+ return classes
88
+
89
+
90
+ def spinner_color_css(color: str | None) -> str | None:
91
+ """Return an inline CSS color for arbitrary (non-semantic) spinner colors."""
92
+ if color is None or color in _SEMANTIC_COLORS:
93
+ return None
94
+ return color
95
+
96
+
97
+ def spinner_is_visible(
98
+ *,
99
+ display: str = "auto",
100
+ loading: bool | None = None,
101
+ has_children: bool = False,
102
+ fullscreen: bool = False,
103
+ ) -> bool:
104
+ """Return whether the spinner graphic should be shown."""
105
+ if display not in _SPINNER_DISPLAYS:
106
+ raise ValueError(f"Unknown Spinner display {display!r}; expected 'auto', 'show', or 'hide'")
107
+ if display == "hide":
108
+ return False
109
+ if display == "show":
110
+ return True
111
+ if loading is not None:
112
+ return bool(loading)
113
+ if fullscreen:
114
+ return True
115
+ return not has_children
116
+
117
+
118
+ def spinner_fullscreen_classes() -> list[str]:
119
+ """Return classes for a fullscreen spinner overlay root."""
120
+ return [
121
+ "position-fixed",
122
+ "top-0",
123
+ "start-0",
124
+ "w-100",
125
+ "h-100",
126
+ "d-flex",
127
+ "align-items-center",
128
+ "justify-content-center",
129
+ ]
130
+
131
+
132
+ def _spinner_node_css(
133
+ *,
134
+ color: str | None,
135
+ spinner_style: dict[str, Any] | None,
136
+ ) -> str | None:
137
+ parts: list[str] = []
138
+ extra = spinner_color_css(color)
139
+ if extra is not None:
140
+ parts.append(f"color: {extra}")
141
+ if spinner_style:
142
+ css_val: Any = normalize_style(spinner_style)
143
+ if isinstance(css_val, dict):
144
+ css_val = "; ".join(f"{key}: {value}" for key, value in css_val.items())
145
+ text = str(css_val).strip().rstrip(";")
146
+ if text:
147
+ parts.append(text)
148
+ if not parts:
149
+ return None
150
+ return "; ".join(parts)
151
+
152
+
153
+ def _mount_spinner_label() -> None:
154
+ label = ui.element("span").classes("visually-hidden")
155
+ setter = getattr(label, "set_text", None)
156
+ if callable(setter):
157
+ setter("Loading...")
158
+ else:
159
+ set_element_text(label, "Loading...")
160
+
161
+
162
+ def _deactivate_timer(timer: Any) -> None:
163
+ with contextlib.suppress(Exception):
164
+ timer.deactivate()
165
+
166
+
167
+ def _attach_passed_children(host: Any, children: object) -> None:
168
+ """Place constructor children onto ``host`` without using ``children=`` on init."""
169
+ if children is None:
170
+ return
171
+ if isinstance(children, str):
172
+ setter = getattr(host, "set_text", None)
173
+ if callable(setter):
174
+ setter(children)
175
+ else:
176
+ set_element_text(host, children)
177
+ return
178
+ _adopt_into(host, children)
179
+
180
+
181
+ class _AlertImpl(BootstrapElement):
182
+ """Private Alert implementation."""
183
+
184
+ component_name = "Alert"
185
+ children_kind = "auto"
186
+ reserved_classes = ("alert",)
187
+
188
+ def __init__(
189
+ self,
190
+ children: object = None,
191
+ *,
192
+ _surface: str,
193
+ color: str = "primary",
194
+ dismissable: bool = False,
195
+ fade: bool = True,
196
+ is_open: bool = True,
197
+ duration: int | None = None,
198
+ on_dismiss: Callable[..., Any] | None = None,
199
+ **kwargs: Any,
200
+ ) -> None:
201
+ self._surface = _surface
202
+ if duration is not None:
203
+ duration_ms = int(duration)
204
+ if duration_ms < 0:
205
+ raise ValueError("Alert duration must be None or a non-negative integer")
206
+ self._duration: int | None = duration_ms
207
+ else:
208
+ self._duration = None
209
+ self._structural_classes = tuple(
210
+ alert_structural_classes(
211
+ color=color,
212
+ dismissable=bool(dismissable),
213
+ fade=bool(fade),
214
+ is_open=bool(is_open),
215
+ )
216
+ )
217
+ self._is_open = bool(is_open)
218
+ self._fade = bool(fade)
219
+ self._on_dismiss = on_dismiss
220
+ self._timer: Any | None = None
221
+ super().__init__(None, tag="div", **kwargs)
222
+ self._props["role"] = "alert"
223
+ _attach_passed_children(self, children)
224
+ if dismissable:
225
+ with self:
226
+ close = ui.element("button").classes("btn-close")
227
+ close._props["type"] = "button"
228
+ close._props["aria-label"] = "Close"
229
+ close.on("click", self._handle_dismiss)
230
+ self._arm_duration_timer()
231
+
232
+ @property
233
+ def is_open(self) -> bool:
234
+ return self._is_open
235
+
236
+ @is_open.setter
237
+ def is_open(self, value: bool) -> None:
238
+ wanted = bool(value)
239
+ if wanted == self._is_open:
240
+ return
241
+ if wanted:
242
+ self._is_open = True
243
+ self._apply_open_classes()
244
+ self._arm_duration_timer()
245
+ else:
246
+ self._handle_dismiss()
247
+
248
+ def _apply_open_classes(self) -> None:
249
+ if self._is_open:
250
+ if self._fade:
251
+ self.classes(add="show", remove="d-none")
252
+ else:
253
+ self.classes(remove="d-none")
254
+ elif self._fade:
255
+ self.classes(remove="show")
256
+ else:
257
+ self.classes(add="d-none")
258
+
259
+ def _cancel_timer(self) -> None:
260
+ timer = self._timer
261
+ self._timer = None
262
+ if timer is not None:
263
+ _deactivate_timer(timer)
264
+
265
+ def _arm_duration_timer(self) -> None:
266
+ self._cancel_timer()
267
+ if self._duration is not None and self._duration > 0 and self._is_open:
268
+ self._timer = ui.timer(
269
+ self._duration / 1000.0,
270
+ self._handle_dismiss,
271
+ once=True,
272
+ )
273
+
274
+ def _handle_dismiss(self, *_args: Any, **_kwargs: Any) -> None:
275
+ if not self._is_open:
276
+ return
277
+ self._is_open = False
278
+ self._cancel_timer()
279
+ self._apply_open_classes()
280
+ if self._on_dismiss is not None:
281
+ self._on_dismiss()
282
+
283
+ def _handle_delete(self) -> None:
284
+ self._cancel_timer()
285
+ super()._handle_delete()
286
+
287
+
288
+ Alert, DbcAlert = make_surface_classes("Alert", globals())
289
+ alert = Alert
290
+
291
+
292
+ class _SpinnerImpl(BootstrapElement):
293
+ """Private Spinner implementation."""
294
+
295
+ component_name = "Spinner"
296
+ children_kind = "auto"
297
+ reserved_classes: tuple[str, ...] = ()
298
+
299
+ def __init__(
300
+ self,
301
+ children: object = None,
302
+ *,
303
+ _surface: str,
304
+ color: str | None = None,
305
+ type: str = "border",
306
+ size: str | None = None,
307
+ loading: bool | None = None,
308
+ display: str = "auto",
309
+ delay_show: int | None = None,
310
+ delay_hide: int | None = None,
311
+ fullscreen: bool = False,
312
+ spinner_style: dict[str, Any] | None = None,
313
+ spinner_class_name: str | None = None,
314
+ spinnerClassName: str | None = None,
315
+ **kwargs: Any,
316
+ ) -> None:
317
+ self._surface = _surface
318
+ spinner_classes = spinner_structural_classes(type=type, size=size, color=color)
319
+ has_children = children is not None
320
+ visible = spinner_is_visible(
321
+ display=display,
322
+ loading=loading,
323
+ has_children=has_children,
324
+ fullscreen=bool(fullscreen),
325
+ )
326
+ if delay_show is not None:
327
+ delay_show_ms = int(delay_show)
328
+ if delay_show_ms < 0:
329
+ raise ValueError("Spinner delay_show must be None or a non-negative integer")
330
+ self._delay_show: int | None = delay_show_ms
331
+ else:
332
+ self._delay_show = None
333
+ if delay_hide is not None:
334
+ delay_hide_ms = int(delay_hide)
335
+ if delay_hide_ms < 0:
336
+ raise ValueError("Spinner delay_hide must be None or a non-negative integer")
337
+ self._delay_hide: int | None = delay_hide_ms
338
+ else:
339
+ self._delay_hide = None
340
+ use_wrapper = has_children or bool(fullscreen)
341
+ if use_wrapper:
342
+ wrapper = spinner_fullscreen_classes() if fullscreen else []
343
+ self._structural_classes = tuple(wrapper)
344
+ else:
345
+ classes = list(spinner_classes)
346
+ if not visible:
347
+ classes.append("d-none")
348
+ self._structural_classes = tuple(classes)
349
+ self._fullscreen = bool(fullscreen)
350
+ self._delay_timers: list[Any] = []
351
+ if fullscreen:
352
+ _ensure_overlay_runtime()
353
+ super().__init__(None, tag="div", **kwargs)
354
+ resolved_cn: str | None = None
355
+ if spinner_class_name is not None or spinnerClassName is not None:
356
+ resolved_cn = resolve_class_name(spinner_class_name, spinnerClassName, self._surface)
357
+ inline = _spinner_node_css(color=color, spinner_style=spinner_style)
358
+ if use_wrapper:
359
+ _attach_passed_children(self, children)
360
+ with self:
361
+ spinner_el = ui.element("div")
362
+ spinner_el.classes(" ".join(spinner_classes))
363
+ if resolved_cn:
364
+ spinner_el.classes(resolved_cn)
365
+ if not visible:
366
+ spinner_el.classes(add="d-none")
367
+ spinner_el._props["role"] = "status"
368
+ if inline:
369
+ spinner_el._props["style"] = inline
370
+ with spinner_el:
371
+ _mount_spinner_label()
372
+ self._spinner_el: Any = spinner_el
373
+ else:
374
+ self._spinner_el = self
375
+ self._props["role"] = "status"
376
+ if resolved_cn:
377
+ self.classes(resolved_cn)
378
+ if inline:
379
+ existing = self._props.get("style")
380
+ if existing:
381
+ self._props["style"] = f"{existing}; {inline}"
382
+ else:
383
+ self._props["style"] = inline
384
+ with self:
385
+ _mount_spinner_label()
386
+ if visible and self._delay_show is not None and self._delay_show > 0:
387
+ self._set_spinner_visible(False)
388
+ self._delay_timers.append(
389
+ ui.timer(self._delay_show / 1000.0, self._show_spinner, once=True)
390
+ )
391
+ elif (not visible) and self._delay_hide is not None and self._delay_hide > 0:
392
+ self._set_spinner_visible(True)
393
+ self._delay_timers.append(
394
+ ui.timer(self._delay_hide / 1000.0, self._hide_spinner, once=True)
395
+ )
396
+ if fullscreen:
397
+ _portal_to_overlay_root(self)
398
+
399
+ def _set_spinner_visible(self, visible: bool) -> None:
400
+ if visible:
401
+ self._spinner_el.classes(remove="d-none")
402
+ else:
403
+ self._spinner_el.classes(add="d-none")
404
+
405
+ def _show_spinner(self) -> None:
406
+ self._set_spinner_visible(True)
407
+
408
+ def _hide_spinner(self) -> None:
409
+ self._set_spinner_visible(False)
410
+
411
+ def _clear_delay_timers(self) -> None:
412
+ timers = self._delay_timers
413
+ self._delay_timers = []
414
+ for timer in timers:
415
+ _deactivate_timer(timer)
416
+
417
+ def _handle_delete(self) -> None:
418
+ self._clear_delay_timers()
419
+ super()._handle_delete()
420
+
421
+
422
+ Spinner, DbcSpinner = make_surface_classes("Spinner", globals())
423
+ spinner = Spinner