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,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}")
|