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,445 @@
1
+ """Navbar family: Navbar, NavbarBrand, NavbarToggler, NavbarSimple."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable
6
+ from typing import Any
7
+
8
+ from nicegui import ui
9
+
10
+ from .._base import (
11
+ BootstrapElement,
12
+ ChildrenError,
13
+ PropConflictError,
14
+ UnsupportedPropError,
15
+ lookup_public_id,
16
+ make_surface_classes,
17
+ )
18
+ from .._host import client_store, get_client
19
+
20
+ __all__ = [
21
+ "Navbar",
22
+ "DbcNavbar",
23
+ "navbar",
24
+ "NavbarBrand",
25
+ "DbcNavbarBrand",
26
+ "navbar_brand",
27
+ "NavbarToggler",
28
+ "DbcNavbarToggler",
29
+ "navbar_toggler",
30
+ "NavbarSimple",
31
+ "DbcNavbarSimple",
32
+ "navbar_simple",
33
+ "navbar_structural_classes",
34
+ "navbar_expand_class",
35
+ "navbar_color_class",
36
+ "navbar_position_classes",
37
+ "navbar_theme_value",
38
+ ]
39
+
40
+ _EXPAND_BREAKPOINTS: frozenset[str] = frozenset({"sm", "md", "lg", "xl", "xxl"})
41
+ _FIXED_VALUES: frozenset[str] = frozenset({"top", "bottom"})
42
+ _STICKY_VALUES: frozenset[str] = frozenset({"top"})
43
+
44
+
45
+ def _is_color_token(value: str) -> bool:
46
+ if not value or not value[0].isalpha():
47
+ return False
48
+ return all(ch.isalnum() or ch in "-_" for ch in value)
49
+
50
+
51
+ def navbar_expand_class(expand: bool | str | None) -> str | None:
52
+ """Return the ``navbar-expand-*`` class, or ``None``."""
53
+ if expand is None or expand is False:
54
+ return None
55
+ if expand is True:
56
+ return "navbar-expand"
57
+ if isinstance(expand, str) and expand in _EXPAND_BREAKPOINTS:
58
+ return f"navbar-expand-{expand}"
59
+ raise ValueError(
60
+ f"invalid expand value {expand!r}; expected True, False, None, "
61
+ "or one of 'sm', 'md', 'lg', 'xl', 'xxl'"
62
+ )
63
+
64
+
65
+ def navbar_color_class(color: str | None) -> str | None:
66
+ """Return the ``bg-*`` class for a navbar color, or ``None``."""
67
+ if color is None:
68
+ return None
69
+ if not _is_color_token(color):
70
+ raise ValueError(f"invalid color value {color!r}")
71
+ return f"bg-{color}"
72
+
73
+
74
+ def navbar_position_classes(
75
+ *,
76
+ fixed: str | None = None,
77
+ sticky: str | None = None,
78
+ ) -> list[str]:
79
+ """Return ``fixed-*`` / ``sticky-*`` classes for a navbar."""
80
+ if fixed is not None and sticky is not None:
81
+ raise PropConflictError("fixed and sticky cannot both be set")
82
+ classes: list[str] = []
83
+ if fixed is not None:
84
+ if fixed not in _FIXED_VALUES:
85
+ raise ValueError(f"invalid fixed value {fixed!r}; expected 'top' or 'bottom'")
86
+ classes.append(f"fixed-{fixed}")
87
+ if sticky is not None:
88
+ if sticky not in _STICKY_VALUES:
89
+ raise ValueError(f"invalid sticky value {sticky!r}; expected 'top'")
90
+ classes.append(f"sticky-{sticky}")
91
+ return classes
92
+
93
+
94
+ def navbar_theme_value(dark: bool | None) -> str | None:
95
+ """Return Bootstrap 5.3 ``data-bs-theme`` value for the navbar."""
96
+ if dark is True:
97
+ return "dark"
98
+ return None
99
+
100
+
101
+ def navbar_structural_classes(
102
+ *,
103
+ color: str | None = None,
104
+ expand: bool | str | None = None,
105
+ fixed: str | None = None,
106
+ sticky: str | None = None,
107
+ ) -> list[str]:
108
+ """Return Bootstrap ``navbar`` classes."""
109
+ classes = ["navbar"]
110
+ expand_class = navbar_expand_class(expand)
111
+ if expand_class is not None:
112
+ classes.append(expand_class)
113
+ color_class = navbar_color_class(color)
114
+ if color_class is not None:
115
+ classes.append(color_class)
116
+ classes.extend(navbar_position_classes(fixed=fixed, sticky=sticky))
117
+ return classes
118
+
119
+
120
+ def _reject_removed_compat_props(surface: str, kwargs: dict[str, Any]) -> None:
121
+ if surface == "compat" and "light" in kwargs:
122
+ raise UnsupportedPropError("light")
123
+
124
+
125
+ def _as_children_list(children: object) -> list[Any]:
126
+ if children is None:
127
+ return []
128
+ if isinstance(children, (list, tuple)):
129
+ return list(children)
130
+ return [children]
131
+
132
+
133
+ def _element_children(component_name: str, children: object) -> list[Any]:
134
+ items = _as_children_list(children)
135
+ for item in items:
136
+ if isinstance(item, str) or not hasattr(item, "move"):
137
+ raise ChildrenError(
138
+ f"{component_name} does not accept text children; wrap text in a component."
139
+ )
140
+ return items
141
+
142
+
143
+ def _safe_get_client() -> Any:
144
+ """Return the current NiceGUI client, or ``None`` when no UI context exists."""
145
+ try:
146
+ return get_client()
147
+ except RuntimeError:
148
+ return None
149
+
150
+
151
+ def _store_for_client(client: Any) -> Any:
152
+ """Return the public-id store for ``client``, or ``None`` if it cannot be obtained."""
153
+ if client is None:
154
+ return None
155
+ try:
156
+ return client_store(client)
157
+ except (RuntimeError, TypeError):
158
+ return None
159
+
160
+
161
+ def _lookup_in_store(store: Any, public_id: str) -> Any:
162
+ """Resolve ``public_id`` from a captured client store. Missing ids return ``None``."""
163
+ if store is None:
164
+ return None
165
+ try:
166
+ getter = store.get
167
+ except AttributeError:
168
+ return None
169
+ try:
170
+ return getter(public_id)
171
+ except (KeyError, RuntimeError, TypeError):
172
+ return None
173
+
174
+
175
+ class _CollapseToggleTarget:
176
+ """Adapter exposing ``is_open`` to toggle a collapse element's ``show`` class."""
177
+
178
+ def __init__(self, element: Any) -> None:
179
+ self._element = element
180
+ self._open = False
181
+
182
+ @property
183
+ def is_open(self) -> bool:
184
+ return self._open
185
+
186
+ @is_open.setter
187
+ def is_open(self, value: bool) -> None:
188
+ opened = bool(value)
189
+ self._open = opened
190
+ if opened:
191
+ self._element.classes(add="show")
192
+ else:
193
+ self._element.classes(remove="show")
194
+
195
+
196
+ class _NavbarImpl(BootstrapElement):
197
+ """Private Navbar implementation."""
198
+
199
+ component_name = "Navbar"
200
+ children_kind = "grouping"
201
+ reserved_classes = ("navbar",)
202
+
203
+ def __init__(
204
+ self,
205
+ children: object = None,
206
+ *,
207
+ _surface: str,
208
+ color: str | None = None,
209
+ dark: bool | None = None,
210
+ expand: bool | str | None = None,
211
+ fixed: str | None = None,
212
+ sticky: str | None = None,
213
+ tag: str = "nav",
214
+ role: str | None = None,
215
+ **kwargs: Any,
216
+ ) -> None:
217
+ self._surface = _surface
218
+ _reject_removed_compat_props(_surface, kwargs)
219
+ self._structural_classes = tuple(
220
+ navbar_structural_classes(color=color, expand=expand, fixed=fixed, sticky=sticky)
221
+ )
222
+ super().__init__(children, tag=tag, **kwargs)
223
+ theme = navbar_theme_value(dark)
224
+ if theme is not None:
225
+ self._props["data-bs-theme"] = theme
226
+ if role is not None:
227
+ self._props["role"] = role
228
+
229
+
230
+ Navbar, DbcNavbar = make_surface_classes("Navbar", globals())
231
+ navbar = Navbar
232
+
233
+
234
+ class _NavbarBrandImpl(BootstrapElement):
235
+ """Private NavbarBrand implementation."""
236
+
237
+ component_name = "NavbarBrand"
238
+ children_kind = "phrasing"
239
+ reserved_classes = ("navbar-brand",)
240
+
241
+ def __init__(
242
+ self,
243
+ children: object = None,
244
+ *,
245
+ _surface: str,
246
+ href: str | None = None,
247
+ external_link: bool = False,
248
+ **kwargs: Any,
249
+ ) -> None:
250
+ self._surface = _surface
251
+ self._structural_classes = ("navbar-brand",)
252
+ tag = "a" if href else "span"
253
+ super().__init__(children, tag=tag, **kwargs)
254
+ if href:
255
+ self._props["href"] = href
256
+ if external_link:
257
+ self._props["target"] = "_blank"
258
+ self._props["rel"] = "noopener noreferrer"
259
+
260
+
261
+ NavbarBrand, DbcNavbarBrand = make_surface_classes("NavbarBrand", globals())
262
+ navbar_brand = NavbarBrand
263
+
264
+
265
+ class _NavbarTogglerImpl(BootstrapElement):
266
+ """Private NavbarToggler implementation.
267
+
268
+ ``target`` is a collapse component reference or a public id string.
269
+ Clicking toggles ``target.is_open`` and does not use ``data-bs-toggle``.
270
+ """
271
+
272
+ component_name = "NavbarToggler"
273
+ children_kind = "phrasing"
274
+ reserved_classes = ("navbar-toggler",)
275
+
276
+ def __init__(
277
+ self,
278
+ children: object = None,
279
+ *,
280
+ _surface: str,
281
+ target: object = None,
282
+ n_clicks: int = 0,
283
+ on_click: Callable[..., Any] | None = None,
284
+ type: str = "button",
285
+ **kwargs: Any,
286
+ ) -> None:
287
+ self._surface = _surface
288
+ self._structural_classes = ("navbar-toggler",)
289
+ self._target = target
290
+ self._n_clicks = int(n_clicks)
291
+ self._on_click = on_click
292
+ lookup_client = _safe_get_client()
293
+ self._lookup_client = lookup_client
294
+ self._id_store = _store_for_client(lookup_client)
295
+ super().__init__(children, tag="button", **kwargs)
296
+ self._props["type"] = type
297
+ if children is None:
298
+ with self:
299
+ ui.element("span").classes("navbar-toggler-icon")
300
+ self.on("click", self._handle_click)
301
+
302
+ @property
303
+ def n_clicks(self) -> int:
304
+ return self._n_clicks
305
+
306
+ @n_clicks.setter
307
+ def n_clicks(self, value: int) -> None:
308
+ self._n_clicks = int(value)
309
+
310
+ @property
311
+ def target(self) -> object:
312
+ return self._target
313
+
314
+ def _lookup_target_id(self, public_id: str) -> Any:
315
+ client = self._lookup_client
316
+ if client is not None:
317
+ try:
318
+ lookup_fn: Any = lookup_public_id
319
+ return lookup_fn(public_id, client)
320
+ except (RuntimeError, TypeError, KeyError, LookupError):
321
+ pass
322
+ return _lookup_in_store(self._id_store, public_id)
323
+
324
+ def _resolve_target(self) -> Any:
325
+ target = self._target
326
+ if target is None or not isinstance(target, str):
327
+ return target
328
+ return self._lookup_target_id(target)
329
+
330
+ def _toggle_target(self) -> None:
331
+ target = self._resolve_target()
332
+ if target is None or not hasattr(target, "is_open"):
333
+ return
334
+ target.is_open = not bool(target.is_open)
335
+
336
+ def _handle_click(self, *_args: Any, **_kwargs: Any) -> None:
337
+ self._n_clicks += 1
338
+ self._toggle_target()
339
+ if self._on_click is not None:
340
+ self._on_click()
341
+
342
+
343
+ NavbarToggler, DbcNavbarToggler = make_surface_classes("NavbarToggler", globals())
344
+ navbar_toggler = NavbarToggler
345
+
346
+
347
+ class _NavbarSimpleImpl(BootstrapElement):
348
+ """Navbar composed with brand, toggler, and collapsible content."""
349
+
350
+ component_name = "NavbarSimple"
351
+ children_kind = "grouping"
352
+ reserved_classes = ("navbar",)
353
+
354
+ def __init__(
355
+ self,
356
+ children: object = None,
357
+ *,
358
+ _surface: str,
359
+ brand: object = None,
360
+ brand_href: str | None = None,
361
+ brand_external_link: bool = False,
362
+ brand_style: Any = None,
363
+ color: str | None = None,
364
+ dark: bool | None = None,
365
+ fluid: bool = False,
366
+ links_left: bool = False,
367
+ expand: bool | str | None = True,
368
+ fixed: str | None = None,
369
+ sticky: str | None = None,
370
+ is_open: bool = False,
371
+ **kwargs: Any,
372
+ ) -> None:
373
+ self._surface = _surface
374
+ self._content: Any = None
375
+ self._collapse_target: _CollapseToggleTarget | None = None
376
+ _reject_removed_compat_props(_surface, kwargs)
377
+ child_list = _element_children("NavbarSimple", children)
378
+ self._structural_classes = tuple(
379
+ navbar_structural_classes(color=color, expand=expand, fixed=fixed, sticky=sticky)
380
+ )
381
+ super().__init__(None, tag="nav", **kwargs)
382
+ theme = navbar_theme_value(dark)
383
+ if theme is not None:
384
+ self._props["data-bs-theme"] = theme
385
+
386
+ brand_type = DbcNavbarBrand if _surface == "compat" else NavbarBrand
387
+ toggler_type = DbcNavbarToggler if _surface == "compat" else NavbarToggler
388
+ align = "me-auto" if links_left else "ms-auto"
389
+ container_class = "container-fluid" if fluid else "container"
390
+
391
+ collapse_target: _CollapseToggleTarget | None = None
392
+ content: Any = None
393
+ with self:
394
+ container = ui.element("div")
395
+ container.classes(container_class)
396
+ with container:
397
+ if brand is not None:
398
+ brand_type(
399
+ brand,
400
+ href=brand_href,
401
+ external_link=brand_external_link,
402
+ style=brand_style,
403
+ )
404
+ collapse = ui.element("div")
405
+ collapse.classes("collapse navbar-collapse")
406
+ with collapse:
407
+ nav_wrap = ui.element("div")
408
+ nav_wrap.classes(f"navbar-nav {align}")
409
+ for child in child_list:
410
+ child.move(nav_wrap)
411
+ content = nav_wrap
412
+ collapse_target = _CollapseToggleTarget(collapse)
413
+ toggler_type(target=collapse_target)
414
+ collapse.move(container)
415
+ self._content = content
416
+ self._collapse_target = collapse_target
417
+ if is_open:
418
+ self.is_open = True
419
+
420
+ @property
421
+ def is_open(self) -> bool:
422
+ if self._collapse_target is None:
423
+ return False
424
+ return bool(self._collapse_target.is_open)
425
+
426
+ @is_open.setter
427
+ def is_open(self, value: bool) -> None:
428
+ if self._collapse_target is None:
429
+ return
430
+ self._collapse_target.is_open = bool(value)
431
+
432
+ def __enter__(self) -> _NavbarSimpleImpl:
433
+ super().__enter__()
434
+ if self._content is not None:
435
+ self._content.__enter__()
436
+ return self
437
+
438
+ def __exit__(self, *args: Any) -> None:
439
+ if self._content is not None:
440
+ self._content.__exit__(*args)
441
+ super().__exit__(*args)
442
+
443
+
444
+ NavbarSimple, DbcNavbarSimple = make_surface_classes("NavbarSimple", globals())
445
+ navbar_simple = NavbarSimple
@@ -0,0 +1,73 @@
1
+ """Per-client ``#ngbs-overlay-root`` (body child, scope-anchor for overlays)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ from typing import Any
8
+
9
+ from .._host import Element, client_store, html_id, is_deleted, run_javascript
10
+ from ..assets import StyleMode, get_mode
11
+
12
+ __all__ = [
13
+ "OVERLAY_ROOT_ID",
14
+ "get_or_create_overlay_root",
15
+ "set_overlay_theme",
16
+ ]
17
+
18
+ OVERLAY_ROOT_ID = "ngbs-overlay-root"
19
+
20
+
21
+ def get_or_create_overlay_root() -> Any:
22
+ """Return the per-client overlay portal root, creating it on first use.
23
+
24
+ Classes: ``ngbs ngbs-overlay``. DOM id: ``#ngbs-overlay-root``.
25
+ Mode B applies ``z-index: 5000`` so Bootstrap's internal scale stacks inside.
26
+ """
27
+ store = client_store()
28
+ existing = store.get("overlay_root")
29
+ if existing is not None and not is_deleted(existing):
30
+ return existing
31
+ root = Element("div")
32
+ root.classes("ngbs ngbs-overlay")
33
+ root.props(f"id={OVERLAY_ROOT_ID}")
34
+ theme = store.get("overlay_theme")
35
+ if theme:
36
+ root.props(f"data-bs-theme={theme}")
37
+ mode = get_mode()
38
+ if mode is not StyleMode.UNSCOPED:
39
+ root.style("z-index: 5000")
40
+ store["overlay_root"] = root
41
+ hid = html_id(root)
42
+ js = (
43
+ "(() => {"
44
+ f"const el = document.getElementById({json.dumps(OVERLAY_ROOT_ID)}) "
45
+ f"|| document.getElementById({json.dumps(hid)});"
46
+ "if (!el) return;"
47
+ f"el.id = {json.dumps(OVERLAY_ROOT_ID)};"
48
+ "document.body.appendChild(el);"
49
+ "})();"
50
+ )
51
+ response = run_javascript(js)
52
+ fire = getattr(response, "_fire", None)
53
+ if fire is not None:
54
+ task = asyncio.ensure_future(fire())
55
+ task.add_done_callback(lambda _t: None)
56
+ return root
57
+
58
+
59
+ def ensure_overlay_root() -> Any:
60
+ """Alias of :func:`get_or_create_overlay_root` (overlays contract name)."""
61
+ return get_or_create_overlay_root()
62
+
63
+
64
+ def set_overlay_theme(theme: str | None) -> None:
65
+ """Set or remove ``data-bs-theme`` on the overlay root."""
66
+ store = client_store()
67
+ cleaned = theme if theme else None
68
+ store["overlay_theme"] = cleaned
69
+ root = get_or_create_overlay_root()
70
+ if cleaned is None:
71
+ root.props(remove="data-bs-theme")
72
+ else:
73
+ root.props(f"data-bs-theme={cleaned}")