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,2633 @@
1
+ """Bootstrap overlay components and their shared browser runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ import json
7
+ from collections.abc import Callable
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from nicegui import ui
11
+
12
+ from .._base import (
13
+ BootstrapElement,
14
+ BootstrapElementMixin,
15
+ ChildrenError,
16
+ PropConflictError,
17
+ UnsupportedPropError,
18
+ make_surface_classes,
19
+ )
20
+ from .._host import set_element_text
21
+
22
+ if TYPE_CHECKING:
23
+
24
+ def ensure_overlay_root() -> Any: ...
25
+ else:
26
+ try:
27
+ from ._overlay_root import ensure_overlay_root
28
+ except ImportError: # pragma: no cover
29
+
30
+ def ensure_overlay_root() -> Any:
31
+ return None
32
+
33
+
34
+ _UNSET = object()
35
+
36
+ _DIRECTION_CLASS = {
37
+ "down": "dropdown",
38
+ "up": "dropup",
39
+ "start": "dropstart",
40
+ "end": "dropend",
41
+ "left": "dropstart",
42
+ "right": "dropend",
43
+ }
44
+
45
+ _DROPDOWN_COLORS = frozenset(
46
+ {"primary", "secondary", "success", "danger", "warning", "info", "light", "dark", "link"}
47
+ )
48
+ _DROPDOWN_SIZES = frozenset({"sm", "lg"})
49
+ _COLLAPSE_DIMENSIONS = frozenset({"height", "width"})
50
+ _OFFCANVAS_PLACEMENTS = frozenset({"start", "end", "top", "bottom"})
51
+ _MODAL_SIZES = frozenset({"sm", "lg", "xl"})
52
+ _MENU_VARIANTS = frozenset({"light", "dark"})
53
+
54
+
55
+ def dropdown_direction_class(direction: str) -> str:
56
+ """Return the Bootstrap direction class for a dropdown."""
57
+ if direction not in _DIRECTION_CLASS:
58
+ raise ValueError(f"Unsupported DropdownMenu direction={direction!r}")
59
+ return _DIRECTION_CLASS[direction]
60
+
61
+
62
+ def dropdown_root_classes(
63
+ *,
64
+ direction: str = "down",
65
+ nav: bool = False,
66
+ group: bool = False,
67
+ in_navbar: bool = False,
68
+ ) -> list[str]:
69
+ """Return structural classes for the dropdown root."""
70
+ direction_cls = dropdown_direction_class(direction)
71
+ classes: list[str] = []
72
+ if group:
73
+ classes.append("btn-group")
74
+ if direction != "down":
75
+ classes.append(direction_cls)
76
+ else:
77
+ classes.append(direction_cls)
78
+ if nav or in_navbar:
79
+ classes.append("nav-item")
80
+ return classes
81
+
82
+
83
+ def dropdown_toggle_classes(
84
+ *,
85
+ color: str = "secondary",
86
+ size: str | None = None,
87
+ caret: bool = True,
88
+ nav: bool = False,
89
+ ) -> list[str]:
90
+ """Return Bootstrap classes for a dropdown toggle."""
91
+ if color not in _DROPDOWN_COLORS:
92
+ raise ValueError(f"Unsupported DropdownMenu color={color!r}")
93
+ if size is not None and size not in _DROPDOWN_SIZES:
94
+ raise ValueError(f"Unsupported DropdownMenu size={size!r}")
95
+ if nav:
96
+ classes = ["nav-link"]
97
+ if caret:
98
+ classes.append("dropdown-toggle")
99
+ return classes
100
+ classes = ["btn"]
101
+ classes.append("btn-link" if color == "link" else f"btn-{color}")
102
+ if size is not None:
103
+ classes.append(f"btn-{size}")
104
+ if caret:
105
+ classes.append("dropdown-toggle")
106
+ return classes
107
+
108
+
109
+ def dropdown_menu_classes(
110
+ *,
111
+ menu_variant: str | None = None,
112
+ align_end: bool | None = None,
113
+ ) -> list[str]:
114
+ """Return Bootstrap classes for the dropdown menu list."""
115
+ classes = ["dropdown-menu"]
116
+ if menu_variant is not None:
117
+ if menu_variant not in _MENU_VARIANTS:
118
+ raise ValueError(f"Unsupported DropdownMenu menu_variant={menu_variant!r}")
119
+ if menu_variant == "dark":
120
+ classes.append("dropdown-menu-dark")
121
+ if align_end:
122
+ classes.append("dropdown-menu-end")
123
+ return classes
124
+
125
+
126
+ def dropdown_item_inner_classes(
127
+ *,
128
+ header: bool = False,
129
+ divider: bool = False,
130
+ disabled: bool = False,
131
+ active: bool = False,
132
+ ) -> list[str]:
133
+ """Return Bootstrap classes for the inner dropdown item node."""
134
+ if divider:
135
+ return ["dropdown-divider"]
136
+ if header:
137
+ return ["dropdown-header"]
138
+ classes = ["dropdown-item"]
139
+ if disabled:
140
+ classes.append("disabled")
141
+ if active:
142
+ classes.append("active")
143
+ return classes
144
+
145
+
146
+ def collapse_structural_classes(*, navbar: bool = False, dimension: str = "height") -> list[str]:
147
+ """Return Bootstrap ``collapse`` classes."""
148
+ if dimension not in _COLLAPSE_DIMENSIONS:
149
+ raise ValueError(f"Unsupported Collapse dimension={dimension!r}")
150
+ classes = ["collapse"]
151
+ if dimension == "width":
152
+ classes.append("collapse-horizontal")
153
+ if navbar:
154
+ classes.append("navbar-collapse")
155
+ return classes
156
+
157
+
158
+ def fade_structural_classes(*, is_in: bool = False) -> list[str]:
159
+ """Return Bootstrap ``fade`` classes."""
160
+ classes = ["fade"]
161
+ if is_in:
162
+ classes.append("show")
163
+ return classes
164
+
165
+
166
+ def accordion_structural_classes(*, flush: bool = False) -> list[str]:
167
+ """Return Bootstrap ``accordion`` classes."""
168
+ classes = ["accordion"]
169
+ if flush:
170
+ classes.append("accordion-flush")
171
+ return classes
172
+
173
+
174
+ def modal_dialog_classes(
175
+ *,
176
+ size: str | None = None,
177
+ centered: bool = False,
178
+ scrollable: bool = False,
179
+ fullscreen: bool | str = False,
180
+ dialog_class_name: str | None = None,
181
+ ) -> list[str]:
182
+ """Return Bootstrap classes for the modal dialog wrapper."""
183
+ classes = ["modal-dialog"]
184
+ if size in _MODAL_SIZES:
185
+ classes.append(f"modal-{size}")
186
+ if centered:
187
+ classes.append("modal-dialog-centered")
188
+ if scrollable:
189
+ classes.append("modal-dialog-scrollable")
190
+ if fullscreen is True:
191
+ classes.append("modal-fullscreen")
192
+ elif isinstance(fullscreen, str) and fullscreen:
193
+ token = (
194
+ fullscreen
195
+ if fullscreen.startswith("modal-fullscreen")
196
+ else f"modal-fullscreen-{fullscreen}"
197
+ )
198
+ classes.append(token)
199
+ if dialog_class_name:
200
+ classes.append(dialog_class_name)
201
+ return classes
202
+
203
+
204
+ def offcanvas_structural_classes(*, placement: str) -> list[str]:
205
+ """Return Bootstrap ``offcanvas`` classes for a placement."""
206
+ if placement not in _OFFCANVAS_PLACEMENTS:
207
+ raise ValueError(f"Offcanvas placement must be start|end|top|bottom, got {placement!r}")
208
+ return ["offcanvas", f"offcanvas-{placement}"]
209
+
210
+
211
+ def generate_accordion_item_ids(given: list[str | None]) -> list[str]:
212
+ """Assign stable ``item-N`` ids without colliding with public ids."""
213
+ used: set[str] = {ident for ident in given if ident}
214
+ result: list[str] = []
215
+ n = 0
216
+ for ident in given:
217
+ if ident:
218
+ result.append(ident)
219
+ continue
220
+ while f"item-{n}" in used:
221
+ n += 1
222
+ fresh = f"item-{n}"
223
+ used.add(fresh)
224
+ result.append(fresh)
225
+ n += 1
226
+ return result
227
+
228
+
229
+ def resolve_accordion_active(
230
+ active_item: str | list[str] | None,
231
+ *,
232
+ always_open: bool,
233
+ item_ids: list[str],
234
+ default_first: bool = False,
235
+ ) -> str | list[str] | None:
236
+ """Normalize accordion selection to a scalar or list matching ``always_open``."""
237
+ known = set(item_ids)
238
+ if always_open:
239
+ if active_item is None:
240
+ return [item_ids[0]] if default_first and item_ids else []
241
+ values = active_item if isinstance(active_item, list) else [active_item]
242
+ return [ident for ident in values if ident in known]
243
+ if isinstance(active_item, list):
244
+ for ident in active_item:
245
+ if ident in known:
246
+ return ident
247
+ active_item = None
248
+ if active_item is None:
249
+ return item_ids[0] if default_first and item_ids else None
250
+ return active_item if active_item in known else None
251
+
252
+
253
+ _RUNTIME_READY = False
254
+
255
+ _OVERLAY_CSS = """
256
+ #ngbs-overlay-root.ngbs-overlay {
257
+ position: absolute;
258
+ inset: 0;
259
+ pointer-events: none;
260
+ background-color: transparent;
261
+ z-index: 5000;
262
+ }
263
+ #ngbs-overlay-root.ngbs-overlay > * { pointer-events: auto; }
264
+ #ngbs-overlay-root .dropdown-menu {
265
+ display: none;
266
+ position: absolute;
267
+ z-index: var(--bs-dropdown-zindex);
268
+ }
269
+ #ngbs-overlay-root .dropdown-menu.show { display: block; }
270
+ .ngbs .dropdown, .ngbs .dropup, .ngbs .dropstart, .ngbs .dropend { position: relative; }
271
+ .ngbs .dropdown-menu {
272
+ display: none;
273
+ position: absolute;
274
+ z-index: var(--bs-dropdown-zindex);
275
+ }
276
+ .ngbs .dropdown-menu.show { display: block; }
277
+ #ngbs-overlay-root .modal { position: fixed; inset: 0; z-index: var(--bs-modal-zindex); display: none; overflow-x: hidden;
278
+ overflow-y: auto; outline: 0; }
279
+ #ngbs-overlay-root .modal.show { display: block; }
280
+ #ngbs-overlay-root .modal-dialog { position: relative; width: auto; margin: 1.75rem auto; max-width: 500px;
281
+ pointer-events: none; }
282
+ #ngbs-overlay-root .modal-dialog-centered { display: flex; align-items: center; min-height: calc(100% - 3.5rem); }
283
+ #ngbs-overlay-root .modal-dialog-scrollable { max-height: calc(100% - 3.5rem); overflow: hidden; }
284
+ #ngbs-overlay-root .modal-dialog-scrollable .modal-content { max-height: 100%; overflow: hidden; display: flex;
285
+ flex-direction: column; }
286
+ #ngbs-overlay-root .modal-sm { max-width: 300px; }
287
+ #ngbs-overlay-root .modal-lg { max-width: 800px; }
288
+ #ngbs-overlay-root .modal-xl { max-width: 1140px; }
289
+ #ngbs-overlay-root .modal-fullscreen { width: 100vw; max-width: none; height: 100%; margin: 0; }
290
+ #ngbs-overlay-root .modal-content { pointer-events: auto; position: relative; outline: 0; }
291
+ #ngbs-overlay-root .modal-backdrop { position: fixed; inset: 0; z-index: var(--bs-backdrop-zindex); }
292
+ #ngbs-overlay-root .offcanvas { position: fixed; z-index: var(--bs-offcanvas-zindex); display: flex; flex-direction: column;
293
+ outline: 0; }
294
+ #ngbs-overlay-root .offcanvas-start { top: 0; left: 0; bottom: 0; width: 400px; max-width: 100%;
295
+ transform: translateX(-100%); }
296
+ #ngbs-overlay-root .offcanvas-end { top: 0; right: 0; bottom: 0; width: 400px; max-width: 100%;
297
+ transform: translateX(100%); }
298
+ #ngbs-overlay-root .offcanvas-top { top: 0; left: 0; right: 0; height: 30vh; transform: translateY(-100%); }
299
+ #ngbs-overlay-root .offcanvas-bottom { bottom: 0; left: 0; right: 0; height: 30vh; transform: translateY(100%); }
300
+ #ngbs-overlay-root .offcanvas.show { transform: none; }
301
+ #ngbs-overlay-root .offcanvas-backdrop { position: fixed; inset: 0; z-index: var(--bs-backdrop-zindex); }
302
+ #ngbs-overlay-root .toast { z-index: var(--bs-toast-zindex); display: none; }
303
+ #ngbs-overlay-root .toast.show { display: block; }
304
+ #ngbs-overlay-root .tooltip { z-index: var(--bs-tooltip-zindex); position: fixed; display: none; padding: 0.25rem 0.5rem; }
305
+ #ngbs-overlay-root .tooltip.show { display: block; }
306
+ """
307
+
308
+ _OVERLAY_JS = r"""
309
+ (function() {
310
+ if (window.__ngbsOverlayProto) return;
311
+ window.__ngbsOverlayProto = 1;
312
+
313
+ function overlayRoot() {
314
+ var r = document.getElementById('ngbs-overlay-root');
315
+ if (!r) {
316
+ r = document.createElement('div');
317
+ r.id = 'ngbs-overlay-root';
318
+ r.className = 'ngbs ngbs-overlay';
319
+ document.body.appendChild(r);
320
+ }
321
+ return r;
322
+ }
323
+ window.ngbsEnsureOverlayRoot = overlayRoot;
324
+
325
+ if (!window.bootstrap) window.bootstrap = {};
326
+
327
+ function focusables(root) {
328
+ var nodes = root.querySelectorAll(
329
+ 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]):not([type="hidden"]), select:not([disabled]), [tabindex]:not([tabindex="-1"])'
330
+ );
331
+ return Array.prototype.filter.call(nodes, function(el) {
332
+ return !el.hasAttribute('disabled') && el.getAttribute('aria-hidden') !== 'true';
333
+ });
334
+ }
335
+
336
+ function dispatch(el, type) {
337
+ try { el.dispatchEvent(new CustomEvent(type, { bubbles: true })); } catch (err) {}
338
+ }
339
+
340
+ var backdropSequence = 0;
341
+
342
+ function backdropKey(el) {
343
+ if (!el._ngbsBackdropKey) {
344
+ backdropSequence += 1;
345
+ el._ngbsBackdropKey = 'ngbs-backdrop-' + backdropSequence;
346
+ }
347
+ return el._ngbsBackdropKey;
348
+ }
349
+
350
+ function backdropFor(el, kind) {
351
+ var key = backdropKey(el);
352
+ var cls = kind === 'offcanvas' ? 'offcanvas-backdrop' : 'modal-backdrop';
353
+ var b = document.querySelector('[data-ngbs-backdrop="' + key + '"]');
354
+ if (!b) {
355
+ b = document.createElement('div');
356
+ b.className = cls + ' fade show';
357
+ b.setAttribute('data-ngbs-backdrop', key);
358
+ var extraClass = el.getAttribute('data-ngbs-backdrop-class');
359
+ if (extraClass) b.classList.add.apply(b.classList, extraClass.split(/\s+/).filter(Boolean));
360
+ var extraStyle = el.getAttribute('data-ngbs-backdrop-style');
361
+ if (extraStyle) b.style.cssText += ';' + extraStyle;
362
+ overlayRoot().appendChild(b);
363
+ }
364
+ return b;
365
+ }
366
+
367
+ function removeBackdrop(el) {
368
+ var key = el._ngbsBackdropKey;
369
+ if (!key) return;
370
+ var b = document.querySelector('[data-ngbs-backdrop="' + key + '"]');
371
+ if (b) b.remove();
372
+ el._ngbsBackdropKey = null;
373
+ }
374
+
375
+ function lockBodyScroll(el) {
376
+ if (el._ngbsScrollLocked) return;
377
+ var body = document.body;
378
+ var state = body._ngbsScrollLock;
379
+ if (!state) {
380
+ state = {
381
+ count: 0,
382
+ previousOverflow: body.style.overflow,
383
+ previousModalOpen: body.classList.contains('modal-open'),
384
+ lockedOverflow: 'hidden'
385
+ };
386
+ body._ngbsScrollLock = state;
387
+ }
388
+ state.count += 1;
389
+ el._ngbsScrollLocked = true;
390
+ body.classList.add('modal-open');
391
+ body.style.overflow = state.lockedOverflow;
392
+ }
393
+
394
+ function unlockBodyScroll(el) {
395
+ if (!el._ngbsScrollLocked) return;
396
+ el._ngbsScrollLocked = false;
397
+ var body = document.body;
398
+ var state = body._ngbsScrollLock;
399
+ if (!state) return;
400
+ state.count = Math.max(0, state.count - 1);
401
+ if (state.count > 0) return;
402
+ if (body.style.overflow === state.lockedOverflow) {
403
+ if (state.previousOverflow) body.style.overflow = state.previousOverflow;
404
+ else body.style.removeProperty('overflow');
405
+ }
406
+ if (!state.previousModalOpen) body.classList.remove('modal-open');
407
+ delete body._ngbsScrollLock;
408
+ }
409
+
410
+ function restoreFocus(el) {
411
+ if (el._ngbsPrevFocus && el._ngbsPrevFocus.focus) {
412
+ try { el._ngbsPrevFocus.focus(); } catch (err) {}
413
+ }
414
+ el._ngbsPrevFocus = null;
415
+ }
416
+
417
+ function portal(el) {
418
+ if (el.getAttribute('data-ngbs-portal') === 'true' && el.parentElement !== overlayRoot()) {
419
+ overlayRoot().appendChild(el);
420
+ }
421
+ }
422
+
423
+ function NgbsModal(el, opts) {
424
+ this._el = el;
425
+ this._opts = Object.assign({ backdrop: true, keyboard: true, focus: true }, opts || {});
426
+ }
427
+ NgbsModal.prototype.show = function() { showModal(this._el); };
428
+ NgbsModal.prototype.hide = function() { hideModal(this._el); };
429
+ NgbsModal.prototype.toggle = function() {
430
+ if (this._el.classList.contains('show')) this.hide(); else this.show();
431
+ };
432
+ NgbsModal.prototype.dispose = function() { disposeEl(this._el); };
433
+ NgbsModal.getOrCreateInstance = function(el, opts) {
434
+ el._ngbsModal = el._ngbsModal || new NgbsModal(el, opts);
435
+ return el._ngbsModal;
436
+ };
437
+ NgbsModal.getInstance = function(el) { return el._ngbsModal || null; };
438
+ if (!window.bootstrap.Modal) window.bootstrap.Modal = NgbsModal;
439
+
440
+ function NgbsCollapse(el) { this._el = el; }
441
+ NgbsCollapse.prototype.show = function() { showCollapse(this._el); };
442
+ NgbsCollapse.prototype.hide = function() { hideCollapse(this._el); };
443
+ NgbsCollapse.prototype.toggle = function() {
444
+ if (this._el.classList.contains('show')) this.hide(); else this.show();
445
+ };
446
+ NgbsCollapse.prototype.dispose = function() {};
447
+ NgbsCollapse.getOrCreateInstance = function(el) {
448
+ el._ngbsCollapse = el._ngbsCollapse || new NgbsCollapse(el);
449
+ return el._ngbsCollapse;
450
+ };
451
+ if (!window.bootstrap.Collapse) window.bootstrap.Collapse = NgbsCollapse;
452
+
453
+ function NgbsOffcanvas(el) { this._el = el; }
454
+ NgbsOffcanvas.prototype.show = function() { showOffcanvas(this._el); };
455
+ NgbsOffcanvas.prototype.hide = function() { hideOffcanvas(this._el); };
456
+ NgbsOffcanvas.prototype.toggle = function() {
457
+ if (this._el.classList.contains('show')) this.hide(); else this.show();
458
+ };
459
+ NgbsOffcanvas.prototype.dispose = function() { disposeEl(this._el); };
460
+ NgbsOffcanvas.getOrCreateInstance = function(el) {
461
+ el._ngbsOffcanvas = el._ngbsOffcanvas || new NgbsOffcanvas(el);
462
+ return el._ngbsOffcanvas;
463
+ };
464
+ if (!window.bootstrap.Offcanvas) window.bootstrap.Offcanvas = NgbsOffcanvas;
465
+
466
+ function NgbsTooltip(target, cfg) {
467
+ this._target = target;
468
+ this._cfg = cfg || {};
469
+ }
470
+ NgbsTooltip.prototype.show = function() {};
471
+ NgbsTooltip.prototype.hide = function() {
472
+ if (this._tip && this._tip.classList.contains('show')) this._tip.classList.remove('show');
473
+ };
474
+ NgbsTooltip.prototype.dispose = function() {
475
+ if (this._tip && this._tip.remove) this._tip.remove();
476
+ this._tip = null;
477
+ };
478
+ NgbsTooltip.getOrCreateInstance = function(target, cfg) {
479
+ target._ngbsTooltip = target._ngbsTooltip || new NgbsTooltip(target, cfg);
480
+ return target._ngbsTooltip;
481
+ };
482
+ if (!window.bootstrap.Tooltip) window.bootstrap.Tooltip = NgbsTooltip;
483
+
484
+ function showModal(el) {
485
+ var alreadyShown = el.classList.contains('show') && el._ngbsPhase === 'open';
486
+ if (alreadyShown) {
487
+ el._ngbsDesired = true;
488
+ var bd0 = el.getAttribute('data-ngbs-backdrop') || 'true';
489
+ if (bd0 !== 'false') backdropFor(el, 'modal');
490
+ return;
491
+ }
492
+ if (el._ngbsPhase === 'opening') {
493
+ el._ngbsDesired = true;
494
+ if (!el.classList.contains('show')) {
495
+ el._ngbsPhase = 'closed';
496
+ showModal(el);
497
+ }
498
+ return;
499
+ }
500
+ document.querySelectorAll('[data-ngbs-kind="modal"]').forEach(function(other) {
501
+ if (other === el) return;
502
+ var otherOpen = other.classList.contains('show')
503
+ || other._ngbsPhase === 'open'
504
+ || other._ngbsPhase === 'opening'
505
+ || other.getAttribute('data-ngbs-open') === 'true';
506
+ if (!otherOpen) return;
507
+ setOpenAttr(other, 'false');
508
+ hideModal(other);
509
+ });
510
+ el._ngbsDesired = true;
511
+ el._ngbsPhase = 'opening';
512
+ portal(el);
513
+ el._ngbsPrevFocus = document.activeElement;
514
+ var bd = el.getAttribute('data-ngbs-backdrop') || 'true';
515
+ if (bd !== 'false') backdropFor(el, 'modal');
516
+ el.style.display = 'block';
517
+ if (!el.classList.contains('show')) el.classList.add('show');
518
+ el.setAttribute('aria-modal', 'true');
519
+ el.removeAttribute('aria-hidden');
520
+ lockBodyScroll(el);
521
+ if (!el._ngbsKey) {
522
+ el._ngbsKey = function(e) {
523
+ if (e.key === 'Escape') {
524
+ var kb = el.getAttribute('data-ngbs-keyboard');
525
+ if (kb === 'false') return;
526
+ e.preventDefault();
527
+ setOpenAttr(el, 'false');
528
+ dispatch(el, 'dismiss');
529
+ hideModal(el);
530
+ } else if (e.key === 'Tab') {
531
+ var nodes = focusables(el);
532
+ if (!nodes.length) { e.preventDefault(); el.focus(); return; }
533
+ var first = nodes[0], last = nodes[nodes.length - 1];
534
+ if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
535
+ else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
536
+ }
537
+ };
538
+ document.addEventListener('keydown', el._ngbsKey, true);
539
+ }
540
+ if (el.getAttribute('data-ngbs-focus') !== 'false' && !el._ngbsFocusIn) {
541
+ el._ngbsFocusIn = function(e) {
542
+ if (el.getAttribute('data-ngbs-focus') === 'false') return;
543
+ if (!el.classList.contains('show')) return;
544
+ if (el.contains(e.target)) return;
545
+ var pull = focusables(el)[0] || el.querySelector('.modal-dialog') || el;
546
+ try { pull.focus(); } catch (err) {}
547
+ };
548
+ document.addEventListener('focusin', el._ngbsFocusIn, true);
549
+ }
550
+ if (!el._ngbsBackdropClick) {
551
+ el._ngbsBackdropClick = function(e) {
552
+ if (e.target !== el) return;
553
+ var mode = el.getAttribute('data-ngbs-backdrop') || 'true';
554
+ if (mode !== 'true') return;
555
+ setOpenAttr(el, 'false');
556
+ dispatch(el, 'dismiss');
557
+ hideModal(el);
558
+ };
559
+ el.addEventListener('mousedown', el._ngbsBackdropClick);
560
+ }
561
+ if (el.getAttribute('data-ngbs-focus') !== 'false') {
562
+ var focusFirst = focusables(el)[0] || el.querySelector('.modal-dialog') || el;
563
+ try { focusFirst.focus(); } catch (err) {}
564
+ }
565
+ el._ngbsPhase = 'open';
566
+ dispatch(el, 'shown');
567
+ if (el.getAttribute('data-ngbs-open') !== 'true') hideModal(el);
568
+ }
569
+
570
+ function hideModal(el) {
571
+ if (el._ngbsPhase === 'closed' || el._ngbsPhase === 'closing') {
572
+ el._ngbsDesired = false;
573
+ removeBackdrop(el);
574
+ unlockBodyScroll(el);
575
+ return;
576
+ }
577
+ el._ngbsDesired = false;
578
+ el._ngbsPhase = 'closing';
579
+ if (el.classList.contains('show')) el.classList.remove('show');
580
+ el.style.display = 'none';
581
+ el.setAttribute('aria-hidden', 'true');
582
+ el.removeAttribute('aria-modal');
583
+ removeBackdrop(el);
584
+ if (el._ngbsKey) {
585
+ document.removeEventListener('keydown', el._ngbsKey, true);
586
+ el._ngbsKey = null;
587
+ }
588
+ if (el._ngbsFocusIn) {
589
+ document.removeEventListener('focusin', el._ngbsFocusIn, true);
590
+ el._ngbsFocusIn = null;
591
+ }
592
+ unlockBodyScroll(el);
593
+ restoreFocus(el);
594
+ el._ngbsPhase = 'closed';
595
+ dispatch(el, 'hidden');
596
+ if (el.getAttribute('data-ngbs-open') === 'true') showModal(el);
597
+ }
598
+
599
+ function showCollapse(el) {
600
+ if (el.classList.contains('show')) return;
601
+ if (!el.classList.contains('show')) el.classList.add('show');
602
+ dispatch(el, 'shown');
603
+ if (el.getAttribute('data-ngbs-open') !== 'true') hideCollapse(el);
604
+ }
605
+ function hideCollapse(el) {
606
+ if (!el.classList.contains('show')) return;
607
+ if (el.classList.contains('show')) el.classList.remove('show');
608
+ dispatch(el, 'hidden');
609
+ if (el.getAttribute('data-ngbs-open') === 'true') showCollapse(el);
610
+ }
611
+
612
+ function showOffcanvas(el) {
613
+ if (el.classList.contains('show') && el._ngbsPhase === 'open') {
614
+ el._ngbsDesired = true;
615
+ return;
616
+ }
617
+ el._ngbsDesired = true;
618
+ el._ngbsPhase = 'opening';
619
+ el._ngbsPrevFocus = document.activeElement;
620
+ portal(el);
621
+ var bd = el.getAttribute('data-ngbs-backdrop') || 'true';
622
+ if (bd !== 'false') {
623
+ var b = backdropFor(el, 'offcanvas');
624
+ if (!b._ngbsClick) {
625
+ b._ngbsClick = function() {
626
+ if ((el.getAttribute('data-ngbs-backdrop') || 'true') !== 'true') return;
627
+ setOpenAttr(el, 'false');
628
+ dispatch(el, 'dismiss');
629
+ hideOffcanvas(el);
630
+ };
631
+ b.addEventListener('click', b._ngbsClick);
632
+ }
633
+ }
634
+ if (!el.classList.contains('show')) el.classList.add('show');
635
+ if (el.getAttribute('data-ngbs-scroll') !== 'true') lockBodyScroll(el);
636
+ if (!el._ngbsKey) {
637
+ el._ngbsKey = function(e) {
638
+ if (e.key !== 'Escape') return;
639
+ if (el.getAttribute('data-ngbs-keyboard') === 'false') return;
640
+ setOpenAttr(el, 'false');
641
+ dispatch(el, 'dismiss');
642
+ hideOffcanvas(el);
643
+ };
644
+ document.addEventListener('keydown', el._ngbsKey, true);
645
+ }
646
+ el._ngbsPhase = 'open';
647
+ dispatch(el, 'shown');
648
+ if (el.getAttribute('data-ngbs-open') !== 'true') hideOffcanvas(el);
649
+ }
650
+
651
+ function hideOffcanvas(el) {
652
+ if (el._ngbsPhase === 'closed') {
653
+ el._ngbsDesired = false;
654
+ removeBackdrop(el);
655
+ el.style.removeProperty('visibility');
656
+ unlockBodyScroll(el);
657
+ restoreFocus(el);
658
+ return;
659
+ }
660
+ el._ngbsDesired = false;
661
+ if (el.classList.contains('show')) el.classList.remove('show');
662
+ el.style.removeProperty('visibility');
663
+ removeBackdrop(el);
664
+ if (el._ngbsKey) {
665
+ document.removeEventListener('keydown', el._ngbsKey, true);
666
+ el._ngbsKey = null;
667
+ }
668
+ unlockBodyScroll(el);
669
+ restoreFocus(el);
670
+ el._ngbsPhase = 'closed';
671
+ dispatch(el, 'hidden');
672
+ if (el.getAttribute('data-ngbs-open') === 'true') showOffcanvas(el);
673
+ }
674
+
675
+ function menuOf(el) {
676
+ return el.querySelector('.dropdown-menu') || el;
677
+ }
678
+ function toggleOf(el) {
679
+ return el.querySelector('.dropdown-toggle, [data-ngbs-role="toggle"]') || el;
680
+ }
681
+ function dropdownItems(el) {
682
+ var menu = menuOf(el);
683
+ if (!menu || !menu.querySelectorAll) return [];
684
+ return Array.prototype.filter.call(menu.querySelectorAll('.dropdown-item'), function(n) {
685
+ if (n.classList.contains('disabled')) return false;
686
+ if (n.getAttribute('disabled') != null) return false;
687
+ if (n.getAttribute('aria-disabled') === 'true') return false;
688
+ return true;
689
+ });
690
+ }
691
+
692
+ function setOpenAttr(el, value) {
693
+ if (el.getAttribute('data-ngbs-open') !== value) {
694
+ el.setAttribute('data-ngbs-open', value);
695
+ }
696
+ }
697
+ function showDropdown(el) {
698
+ var menu = menuOf(el);
699
+ var btn = toggleOf(el);
700
+ if (menu.classList.contains('show')) {
701
+ setOpenAttr(el, 'true');
702
+ if (btn && btn.setAttribute) btn.setAttribute('aria-expanded', 'true');
703
+ return;
704
+ }
705
+ menu.classList.add('show');
706
+ setOpenAttr(el, 'true');
707
+ if (btn && btn.setAttribute) btn.setAttribute('aria-expanded', 'true');
708
+ dispatch(el, 'shown');
709
+ dispatch(el, 'toggle');
710
+ }
711
+ function hideDropdown(el) {
712
+ var menu = menuOf(el);
713
+ var btn = toggleOf(el);
714
+ if (!menu.classList.contains('show')) {
715
+ setOpenAttr(el, 'false');
716
+ if (btn && btn.setAttribute) btn.setAttribute('aria-expanded', 'false');
717
+ return;
718
+ }
719
+ menu.classList.remove('show');
720
+ setOpenAttr(el, 'false');
721
+ if (btn && btn.setAttribute) btn.setAttribute('aria-expanded', 'false');
722
+ dispatch(el, 'hidden');
723
+ dispatch(el, 'toggle');
724
+ }
725
+
726
+ function initDropdown(el) {
727
+ if (el._ngbsDropInited) return;
728
+ el._ngbsDropInited = true;
729
+ var btn = toggleOf(el);
730
+ btn.addEventListener('click', function(e) {
731
+ e.preventDefault();
732
+ e.stopPropagation();
733
+ if (btn.disabled || btn.getAttribute('disabled') != null || btn.classList.contains('disabled')) return;
734
+ if (menuOf(el).classList.contains('show')) hideDropdown(el);
735
+ else showDropdown(el);
736
+ });
737
+ el.addEventListener('click', function(e) {
738
+ var item = e.target && e.target.closest ? e.target.closest('.dropdown-item') : null;
739
+ if (!item || !el.contains(item)) return;
740
+ if (item.classList.contains('disabled') || item.getAttribute('aria-disabled') === 'true') {
741
+ e.preventDefault();
742
+ return;
743
+ }
744
+ if (item.getAttribute('data-ngbs-item-toggle') === 'false') return;
745
+ hideDropdown(el);
746
+ });
747
+ el.addEventListener('keydown', function(e) {
748
+ var items = dropdownItems(el);
749
+ var menu = menuOf(el);
750
+ var shown = menu.classList.contains('show');
751
+ var idx = items.indexOf(document.activeElement);
752
+ if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
753
+ e.preventDefault();
754
+ if (!shown) showDropdown(el);
755
+ items = dropdownItems(el);
756
+ if (!items.length) return;
757
+ if (idx < 0) {
758
+ if (e.key === 'ArrowDown') items[0].focus();
759
+ else items[items.length - 1].focus();
760
+ return;
761
+ }
762
+ if (e.key === 'ArrowDown') items[(idx + 1) % items.length].focus();
763
+ else items[(idx - 1 + items.length) % items.length].focus();
764
+ return;
765
+ }
766
+ if (e.key === 'Home') {
767
+ if (!shown || !items.length) return;
768
+ e.preventDefault();
769
+ items[0].focus();
770
+ return;
771
+ }
772
+ if (e.key === 'End') {
773
+ if (!shown || !items.length) return;
774
+ e.preventDefault();
775
+ items[items.length - 1].focus();
776
+ return;
777
+ }
778
+ if (e.key === 'Escape') {
779
+ if (!shown) return;
780
+ e.preventDefault();
781
+ hideDropdown(el);
782
+ if (btn && btn.focus) btn.focus();
783
+ return;
784
+ }
785
+ if (e.key === 'Tab' && shown) {
786
+ hideDropdown(el);
787
+ }
788
+ });
789
+ if (!window.__ngbsDropDoc) {
790
+ window.__ngbsDropDoc = true;
791
+ document.addEventListener('click', function(e) {
792
+ document.querySelectorAll('[data-ngbs-kind="dropdown"]').forEach(function(d) {
793
+ if (!d.contains(e.target)) hideDropdown(d);
794
+ });
795
+ });
796
+ document.addEventListener('keydown', function(e) {
797
+ if (e.key !== 'Escape') return;
798
+ document.querySelectorAll('[data-ngbs-kind="dropdown"]').forEach(hideDropdown);
799
+ });
800
+ }
801
+ }
802
+
803
+ function resolveTarget(el) {
804
+ var spec = el.getAttribute('data-ngbs-target') || '';
805
+ var kind = el.getAttribute('data-ngbs-target-kind') || 'public';
806
+ if (!spec) return null;
807
+ if (kind === 'eid') {
808
+ var n = document.getElementById('c' + spec) || document.querySelector('[data-ngbs-eid="' + spec + '"]');
809
+ return n;
810
+ }
811
+ return document.querySelector('[data-ngbs-public-id="' + spec + '"]')
812
+ || document.getElementById(spec)
813
+ || document.querySelector('[id="' + spec + '"]');
814
+ }
815
+
816
+ function placeTooltip(tip, target, placement) {
817
+ var r = target.getBoundingClientRect();
818
+ var t = tip.getBoundingClientRect();
819
+ var p = !placement || placement === 'auto' ? 'top' : placement;
820
+ var top, left;
821
+ if (p.indexOf('bottom') === 0) { top = r.bottom + 8; left = r.left + (r.width - t.width) / 2; }
822
+ else if (p.indexOf('left') === 0) { top = r.top + (r.height - t.height) / 2; left = r.left - t.width - 8; }
823
+ else if (p.indexOf('right') === 0) { top = r.top + (r.height - t.height) / 2; left = r.right + 8; }
824
+ else { top = r.top - t.height - 8; left = r.left + (r.width - t.width) / 2; }
825
+ tip.style.position = 'fixed';
826
+ tip.style.top = Math.max(0, top) + 'px';
827
+ tip.style.left = Math.max(0, left) + 'px';
828
+ }
829
+
830
+ function tooltipDelay(el, phase) {
831
+ var raw = el.getAttribute('data-ngbs-delay');
832
+ if (!raw) return 0;
833
+ var parsed;
834
+ try { parsed = JSON.parse(raw); } catch (err) { parsed = Number(raw); }
835
+ if (typeof parsed === 'number') return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
836
+ if (parsed && typeof parsed === 'object') {
837
+ var value = Number(parsed[phase]);
838
+ return Number.isFinite(value) ? Math.max(0, value) : 0;
839
+ }
840
+ return 0;
841
+ }
842
+
843
+ function initTooltip(el) {
844
+ if (el._ngbsTipInited) return;
845
+ el._ngbsTipInited = true;
846
+ el.style.display = 'none';
847
+ var target = resolveTarget(el);
848
+ if (!target) {
849
+ el._ngbsTargetWatch = new MutationObserver(function() {
850
+ if (!document.contains(el)) {
851
+ el._ngbsTargetWatch.disconnect();
852
+ el._ngbsTargetWatch = null;
853
+ return;
854
+ }
855
+ var resolved = resolveTarget(el);
856
+ if (!resolved) return;
857
+ el._ngbsTargetWatch.disconnect();
858
+ el._ngbsTargetWatch = null;
859
+ el._ngbsTipInited = false;
860
+ initTooltip(el);
861
+ if (el._ngbsInited) sync(el);
862
+ });
863
+ el._ngbsTargetWatch.observe(document.body, { childList: true, subtree: true, attributes: true });
864
+ return;
865
+ }
866
+ var tip = document.createElement('div');
867
+ tip.className = 'tooltip bs-tooltip-auto';
868
+ tip.setAttribute('role', 'tooltip');
869
+ var arrow = document.createElement('div');
870
+ arrow.className = 'tooltip-arrow';
871
+ var inner = document.createElement('div');
872
+ inner.className = 'tooltip-inner';
873
+ while (el.firstChild) inner.appendChild(el.firstChild);
874
+ tip.appendChild(arrow);
875
+ tip.appendChild(inner);
876
+ tip.style.display = 'none';
877
+ overlayRoot().appendChild(tip);
878
+ el._ngbsTip = tip;
879
+ el._ngbsTipTarget = target;
880
+ target._ngbsTipEl = tip;
881
+ el._ngbsTipHandlers = [];
882
+ el._ngbsTipShowTimer = null;
883
+ el._ngbsTipHideTimer = null;
884
+ var trigger = (el.getAttribute('data-ngbs-trigger') || 'hover focus').split(/\s+/);
885
+
886
+ function bind(node, event, handler) {
887
+ node.addEventListener(event, handler);
888
+ el._ngbsTipHandlers.push({ node: node, event: event, handler: handler });
889
+ }
890
+
891
+ function showNow() {
892
+ if (!document.contains(target)) { disposeEl(el); return; }
893
+ if (tip.classList.contains('show')) return;
894
+ if (el._ngbsTipHideTimer) {
895
+ clearTimeout(el._ngbsTipHideTimer);
896
+ el._ngbsTipHideTimer = null;
897
+ }
898
+ tip.style.display = 'block';
899
+ tip.classList.add('show');
900
+ placeTooltip(tip, target, el.getAttribute('data-ngbs-placement') || 'auto');
901
+ }
902
+
903
+ function show() {
904
+ if (tip.classList.contains('show')) return;
905
+ if (el._ngbsTipShowTimer) clearTimeout(el._ngbsTipShowTimer);
906
+ var delay = tooltipDelay(el, 'show');
907
+ if (delay > 0) {
908
+ el._ngbsTipShowTimer = setTimeout(function() {
909
+ el._ngbsTipShowTimer = null;
910
+ showNow();
911
+ }, delay);
912
+ }
913
+ else showNow();
914
+ }
915
+
916
+ function hideNow() {
917
+ tip.classList.remove('show');
918
+ tip.style.display = 'none';
919
+ }
920
+
921
+ function hide() {
922
+ if (el._ngbsTipShowTimer) {
923
+ clearTimeout(el._ngbsTipShowTimer);
924
+ el._ngbsTipShowTimer = null;
925
+ }
926
+ if (el._ngbsTipHideTimer) clearTimeout(el._ngbsTipHideTimer);
927
+ var delay = tooltipDelay(el, 'hide');
928
+ if (delay > 0) {
929
+ el._ngbsTipHideTimer = setTimeout(function() {
930
+ el._ngbsTipHideTimer = null;
931
+ hideNow();
932
+ }, delay);
933
+ }
934
+ else hideNow();
935
+ }
936
+
937
+ function toggle() {
938
+ if (tip.classList.contains('show')) hide(); else show();
939
+ }
940
+ if (trigger.indexOf('hover') >= 0 || trigger.indexOf('mouseover') >= 0) {
941
+ bind(target, 'mouseenter', show);
942
+ bind(target, 'mouseleave', hide);
943
+ }
944
+ if (trigger.indexOf('focus') >= 0) {
945
+ bind(target, 'focus', show);
946
+ bind(target, 'blur', hide);
947
+ }
948
+ if (trigger.indexOf('click') >= 0) {
949
+ bind(target, 'click', toggle);
950
+ }
951
+ if (el.getAttribute('data-ngbs-autohide') === 'false') {
952
+ bind(tip, 'mouseenter', function() {
953
+ if (el._ngbsTipHideTimer) {
954
+ clearTimeout(el._ngbsTipHideTimer);
955
+ el._ngbsTipHideTimer = null;
956
+ }
957
+ });
958
+ bind(tip, 'mouseleave', hide);
959
+ }
960
+ el._ngbsTargetWatch = new MutationObserver(function() {
961
+ if (!document.contains(target)) disposeEl(el);
962
+ });
963
+ el._ngbsTargetWatch.observe(document.body, { childList: true, subtree: true });
964
+ if (window.bootstrap && window.bootstrap.Tooltip && window.bootstrap.Tooltip !== NgbsTooltip) {
965
+ try {
966
+ el._bsTooltip = window.bootstrap.Tooltip.getOrCreateInstance(target, {
967
+ title: function() { return inner; },
968
+ html: true,
969
+ container: overlayRoot(),
970
+ placement: el.getAttribute('data-ngbs-placement') || 'auto',
971
+ trigger: el.getAttribute('data-ngbs-trigger') || 'hover focus',
972
+ delay: (function() {
973
+ var raw = el.getAttribute('data-ngbs-delay');
974
+ if (!raw) return 0;
975
+ try { return JSON.parse(raw); } catch (err) { return Number(raw) || 0; };
976
+ })(),
977
+ sanitize: false
978
+ });
979
+ } catch (err) {}
980
+ }
981
+ }
982
+
983
+ function showTooltip(el) {
984
+ if (!el._ngbsTip) return;
985
+ if (el._ngbsTip.classList.contains('show')) return;
986
+ var target = el._ngbsTipTarget;
987
+ if (!target || !document.contains(target)) { disposeEl(el); return; }
988
+ if (el._ngbsTipShowTimer) clearTimeout(el._ngbsTipShowTimer);
989
+ var delay = tooltipDelay(el, 'show');
990
+ if (delay > 0) {
991
+ el._ngbsTipShowTimer = setTimeout(function() {
992
+ el._ngbsTipShowTimer = null;
993
+ el._ngbsTip.style.display = 'block';
994
+ el._ngbsTip.classList.add('show');
995
+ placeTooltip(el._ngbsTip, target, el.getAttribute('data-ngbs-placement') || 'auto');
996
+ }, delay);
997
+ } else {
998
+ el._ngbsTip.style.display = 'block';
999
+ el._ngbsTip.classList.add('show');
1000
+ placeTooltip(el._ngbsTip, target, el.getAttribute('data-ngbs-placement') || 'auto');
1001
+ }
1002
+ }
1003
+
1004
+ function hideTooltip(el) {
1005
+ if (el._ngbsTipShowTimer) {
1006
+ clearTimeout(el._ngbsTipShowTimer);
1007
+ el._ngbsTipShowTimer = null;
1008
+ }
1009
+ if (el._ngbsTipHideTimer) {
1010
+ clearTimeout(el._ngbsTipHideTimer);
1011
+ el._ngbsTipHideTimer = null;
1012
+ }
1013
+ if (el._ngbsTip) {
1014
+ el._ngbsTip.classList.remove('show');
1015
+ el._ngbsTip.style.display = 'none';
1016
+ }
1017
+ }
1018
+
1019
+ function armToastTimer(el) {
1020
+ var dur = el.getAttribute('data-ngbs-duration');
1021
+ if (!dur || Number(dur) <= 0 || el._ngbsTimer) return;
1022
+ el._ngbsTimer = setTimeout(function() {
1023
+ el._ngbsTimer = null;
1024
+ setOpenAttr(el, 'false');
1025
+ hideToast(el);
1026
+ }, Number(dur));
1027
+ }
1028
+
1029
+ function showToast(el) {
1030
+ if (el.classList.contains('show')) {
1031
+ armToastTimer(el);
1032
+ return;
1033
+ }
1034
+ portal(el);
1035
+ el.classList.add('show');
1036
+ el.style.display = 'block';
1037
+ armToastTimer(el);
1038
+ dispatch(el, 'shown');
1039
+ }
1040
+ function hideToast(el) {
1041
+ if (!el.classList.contains('show')) return;
1042
+ if (el._ngbsTimer) { clearTimeout(el._ngbsTimer); el._ngbsTimer = null; }
1043
+ el.classList.remove('show');
1044
+ el.style.display = 'none';
1045
+ dispatch(el, 'hidden');
1046
+ }
1047
+
1048
+ function initToast(el) {
1049
+ if (el._ngbsToastInited) return;
1050
+ el._ngbsToastInited = true;
1051
+ el.addEventListener('click', function(e) {
1052
+ var t = e.target;
1053
+ if (t && t.getAttribute && t.getAttribute('data-ngbs-dismiss') === 'toast') {
1054
+ el.setAttribute('data-ngbs-open', 'false');
1055
+ dispatch(el, 'dismiss');
1056
+ hideToast(el);
1057
+ }
1058
+ });
1059
+ }
1060
+
1061
+ function initOne(el) {
1062
+ if (!el || !el.getAttribute) return;
1063
+ var kind = el.getAttribute('data-ngbs-kind');
1064
+ if (!kind) return;
1065
+ if (kind === 'dropdown') initDropdown(el);
1066
+ if (kind === 'tooltip') initTooltip(el);
1067
+ if (kind === 'toast') initToast(el);
1068
+ if (kind === 'modal') {
1069
+ el.addEventListener('click', function(e) {
1070
+ var t = e.target;
1071
+ if (t && t.getAttribute && t.getAttribute('data-ngbs-dismiss') === 'modal') {
1072
+ setOpenAttr(el, 'false');
1073
+ dispatch(el, 'dismiss');
1074
+ hideModal(el);
1075
+ }
1076
+ });
1077
+ }
1078
+ if (kind === 'offcanvas') {
1079
+ el.addEventListener('click', function(e) {
1080
+ var t = e.target;
1081
+ if (t && t.getAttribute && t.getAttribute('data-ngbs-dismiss') === 'offcanvas') {
1082
+ setOpenAttr(el, 'false');
1083
+ dispatch(el, 'dismiss');
1084
+ hideOffcanvas(el);
1085
+ }
1086
+ });
1087
+ }
1088
+ el._ngbsInited = true;
1089
+ sync(el);
1090
+ }
1091
+
1092
+ function sync(el) {
1093
+ if (!el || !el.getAttribute) return;
1094
+ var kind = el.getAttribute('data-ngbs-kind');
1095
+ if (!kind) return;
1096
+ if (!el._ngbsInited) { initOne(el); return; }
1097
+ var want = el.getAttribute('data-ngbs-open') === 'true';
1098
+ if (kind === 'modal') {
1099
+ if (window.bootstrap && window.bootstrap.Modal && window.bootstrap.Modal.getOrCreateInstance) {
1100
+ try {
1101
+ var inst = window.bootstrap.Modal.getOrCreateInstance(el, {
1102
+ backdrop: (function() {
1103
+ var v = el.getAttribute('data-ngbs-backdrop') || 'true';
1104
+ if (v === 'static') return 'static';
1105
+ return v !== 'false';
1106
+ })(),
1107
+ keyboard: el.getAttribute('data-ngbs-keyboard') !== 'false',
1108
+ focus: el.getAttribute('data-ngbs-focus') !== 'false'
1109
+ });
1110
+ if (inst && inst !== el._ngbsModal && inst.show && inst.hide) {
1111
+ portal(el);
1112
+ if (want) inst.show(); else inst.hide();
1113
+ return;
1114
+ }
1115
+ } catch (err) {}
1116
+ }
1117
+ if (want) showModal(el); else hideModal(el);
1118
+ // Definitive convergence: DOM truth wins over the phase machine.
1119
+ var shownNow = el.classList.contains('show');
1120
+ if (want && shownNow) {
1121
+ var bdWanted = (el.getAttribute('data-ngbs-backdrop') || 'true') !== 'false';
1122
+ if (bdWanted) backdropFor(el, 'modal');
1123
+ }
1124
+ if (!want && !shownNow) {
1125
+ removeBackdrop(el);
1126
+ }
1127
+ } else if (kind === 'collapse') {
1128
+ if (want) showCollapse(el); else hideCollapse(el);
1129
+ } else if (kind === 'offcanvas') {
1130
+ if (want) showOffcanvas(el); else hideOffcanvas(el);
1131
+ } else if (kind === 'dropdown') {
1132
+ if (want) showDropdown(el); else hideDropdown(el);
1133
+ } else if (kind === 'toast') {
1134
+ if (want) showToast(el); else hideToast(el);
1135
+ } else if (kind === 'tooltip') {
1136
+ if (!el._ngbsTipInited) initTooltip(el);
1137
+ if (want) showTooltip(el); else hideTooltip(el);
1138
+ }
1139
+ }
1140
+
1141
+ function disposeEl(el) {
1142
+ if (!el || !el.getAttribute) return;
1143
+ var kind = el.getAttribute('data-ngbs-kind');
1144
+ if (!kind) return;
1145
+ if (el._ngbsTimer) { clearTimeout(el._ngbsTimer); el._ngbsTimer = null; }
1146
+ if (el._ngbsKey) {
1147
+ document.removeEventListener('keydown', el._ngbsKey, true);
1148
+ el._ngbsKey = null;
1149
+ }
1150
+ if (el._ngbsFocusIn) {
1151
+ document.removeEventListener('focusin', el._ngbsFocusIn, true);
1152
+ el._ngbsFocusIn = null;
1153
+ }
1154
+ if (el._ngbsTipShowTimer) {
1155
+ clearTimeout(el._ngbsTipShowTimer);
1156
+ el._ngbsTipShowTimer = null;
1157
+ }
1158
+ if (el._ngbsTipHideTimer) {
1159
+ clearTimeout(el._ngbsTipHideTimer);
1160
+ el._ngbsTipHideTimer = null;
1161
+ }
1162
+ if (el._ngbsTipHandlers) {
1163
+ el._ngbsTipHandlers.forEach(function(binding) {
1164
+ binding.node.removeEventListener(binding.event, binding.handler);
1165
+ });
1166
+ el._ngbsTipHandlers = [];
1167
+ }
1168
+ if (el._ngbsTip && el._ngbsTip.remove) el._ngbsTip.remove();
1169
+ if (el._ngbsTargetWatch) {
1170
+ el._ngbsTargetWatch.disconnect();
1171
+ el._ngbsTargetWatch = null;
1172
+ }
1173
+ if (el._ngbsTipTarget && el._ngbsTipTarget._ngbsTipEl === el._ngbsTip) {
1174
+ el._ngbsTipTarget._ngbsTipEl = null;
1175
+ }
1176
+ if (el._bsTooltip && el._bsTooltip.dispose) {
1177
+ try { el._bsTooltip.dispose(); } catch (err) {}
1178
+ }
1179
+ removeBackdrop(el);
1180
+ unlockBodyScroll(el);
1181
+ restoreFocus(el);
1182
+ el._ngbsPhase = 'closed';
1183
+ }
1184
+ window.ngbsDispose = disposeEl;
1185
+ window.ngbsSync = sync;
1186
+
1187
+ function scan(root) {
1188
+ if (!root) return;
1189
+ var list = [];
1190
+ if (root.getAttribute && root.getAttribute('data-ngbs-kind')) list.push(root);
1191
+ if (root.querySelectorAll) {
1192
+ root.querySelectorAll('[data-ngbs-kind]').forEach(function(n) { list.push(n); });
1193
+ }
1194
+ list.forEach(function(n) {
1195
+ if (!n._ngbsInited) initOne(n);
1196
+ else sync(n);
1197
+ });
1198
+ }
1199
+
1200
+ var mo = new MutationObserver(function(mutations) {
1201
+ for (var i = 0; i < mutations.length; i++) {
1202
+ var m = mutations[i];
1203
+ if (m.type === 'attributes' && m.target && m.target.getAttribute) {
1204
+ if (m.target.getAttribute('data-ngbs-kind')) sync(m.target);
1205
+ }
1206
+ if (m.type === 'childList') {
1207
+ m.addedNodes.forEach(function(n) {
1208
+ if (n.nodeType === 1) scan(n);
1209
+ });
1210
+ m.removedNodes.forEach(function(n) {
1211
+ if (n.nodeType !== 1) return;
1212
+ if (document.contains(n)) return;
1213
+ disposeEl(n);
1214
+ if (n.querySelectorAll) {
1215
+ n.querySelectorAll('[data-ngbs-kind]').forEach(disposeEl);
1216
+ }
1217
+ });
1218
+ }
1219
+ }
1220
+ });
1221
+
1222
+ function start() {
1223
+ overlayRoot();
1224
+ mo.observe(document.documentElement, {
1225
+ childList: true,
1226
+ subtree: true,
1227
+ attributes: true,
1228
+ attributeFilter: ['data-ngbs-open', 'data-ngbs-kind', 'data-ngbs-epoch']
1229
+ });
1230
+ scan(document);
1231
+ }
1232
+ if (document.body) start();
1233
+ else document.addEventListener('DOMContentLoaded', start);
1234
+ })();
1235
+ """
1236
+
1237
+
1238
+ def _make_surfaces(impl_cls: type) -> tuple[type, type]:
1239
+ name = getattr(impl_cls, "component_name", None) or impl_cls.__name__
1240
+ if name.startswith("_"):
1241
+ name = name[1:]
1242
+ if name.endswith("Impl"):
1243
+ name = name[:-4]
1244
+ return make_surface_classes(impl_cls, component_name=name)
1245
+
1246
+
1247
+ def _surface_of(el: Any, explicit: str | None) -> str:
1248
+ if explicit in {"native", "compat"}:
1249
+ return explicit
1250
+ value = getattr(el, "_surface", None) or getattr(type(el), "_surface", None)
1251
+ return value if value in {"native", "compat"} else "native"
1252
+
1253
+
1254
+ def _flatten_children(children: Any) -> list[Any]:
1255
+ if children is None:
1256
+ return []
1257
+ if isinstance(children, (str, int, float)):
1258
+ return [children]
1259
+ element_type: Any
1260
+ try:
1261
+ from nicegui.element import Element as element_type
1262
+ except Exception: # pragma: no cover
1263
+ element_type = ui.element
1264
+ if isinstance(children, element_type):
1265
+ return [children]
1266
+ if isinstance(children, (list, tuple)):
1267
+ out: list[Any] = []
1268
+ for child in children:
1269
+ out.extend(_flatten_children(child))
1270
+ return out
1271
+ raise TypeError(f"Unsupported child type: {type(children)!r}")
1272
+
1273
+
1274
+ def _adopt_into(host: Any, children: Any, *, owner: Any = None) -> None:
1275
+ seen: set[int] = set()
1276
+ for child in _flatten_children(children):
1277
+ if child is None:
1278
+ continue
1279
+ if isinstance(child, (str, int, float)):
1280
+ with host:
1281
+ span = ui.element("span").classes("ngbs-text")
1282
+ if hasattr(span, "set_text"):
1283
+ span.set_text(str(child))
1284
+ else:
1285
+ set_element_text(span, str(child))
1286
+ elif isinstance(child, ui.element):
1287
+ ident = id(child)
1288
+ if ident in seen:
1289
+ raise ChildrenError("Duplicate ownership")
1290
+ seen.add(ident)
1291
+ BootstrapElementMixin._validate_adoptee(owner if owner is not None else host, child)
1292
+ child.move(host)
1293
+ else:
1294
+ raise TypeError(f"Unsupported child type: {type(child)!r}")
1295
+
1296
+
1297
+ def _fill_text_or_children(host: Any, children: Any, *, owner: Any = None) -> None:
1298
+ if children is None:
1299
+ return
1300
+ if isinstance(children, (str, int, float)) and not isinstance(children, bool):
1301
+ text = str(children)
1302
+ if hasattr(host, "set_text"):
1303
+ host.set_text(text)
1304
+ else:
1305
+ set_element_text(host, text)
1306
+ return
1307
+ _adopt_into(host, children, owner=owner)
1308
+
1309
+
1310
+ def _slot_children(host: Any) -> list[Any]:
1311
+ slot = getattr(host, "default_slot", None)
1312
+ if slot is not None:
1313
+ return list(getattr(slot, "children", []) or [])
1314
+ slots = getattr(host, "slots", None)
1315
+ if isinstance(slots, dict):
1316
+ default = slots.get("default")
1317
+ if default is not None:
1318
+ return list(getattr(default, "children", []) or [])
1319
+ return []
1320
+
1321
+
1322
+ def _collect_accordion_items(host: Any) -> list[Any]:
1323
+ items: list[Any] = []
1324
+ for child in _slot_children(host):
1325
+ if getattr(child, "component_name", None) == "AccordionItem":
1326
+ items.append(child)
1327
+ return items
1328
+
1329
+
1330
+ def _find_ancestor(el: Any, component_name: str) -> Any | None:
1331
+ seen: set[int] = set()
1332
+ current = el
1333
+ while current is not None:
1334
+ ident = id(current)
1335
+ if ident in seen:
1336
+ break
1337
+ seen.add(ident)
1338
+ slot = getattr(current, "parent_slot", None)
1339
+ parent = (
1340
+ getattr(slot, "parent", None) if slot is not None else getattr(current, "parent", None)
1341
+ )
1342
+ if parent is None:
1343
+ break
1344
+ if getattr(parent, "component_name", None) == component_name:
1345
+ return parent
1346
+ current = parent
1347
+ return None
1348
+
1349
+
1350
+ def _style_obj(target: Any, style: dict[str, Any] | None) -> str:
1351
+ if not style:
1352
+ return ""
1353
+ unitless = {
1354
+ "opacity",
1355
+ "z-index",
1356
+ "zIndex",
1357
+ "font-weight",
1358
+ "fontWeight",
1359
+ "line-height",
1360
+ "lineHeight",
1361
+ "flex",
1362
+ "flex-grow",
1363
+ "flexGrow",
1364
+ "flex-shrink",
1365
+ "flexShrink",
1366
+ "order",
1367
+ "zoom",
1368
+ "animation-iteration-count",
1369
+ "animationIterationCount",
1370
+ }
1371
+ parts: list[str] = []
1372
+ for key, value in style.items():
1373
+ css_key = key
1374
+ if isinstance(value, (int, float)) and key not in unitless:
1375
+ value = f"{value}px"
1376
+ parts.append(f"{css_key}: {value}")
1377
+ if parts:
1378
+ css = "; ".join(parts)
1379
+ if target is not None:
1380
+ target.style(css)
1381
+ return css
1382
+ return ""
1383
+
1384
+
1385
+ def _ensure_overlay_runtime() -> None:
1386
+ global _RUNTIME_READY
1387
+ if _RUNTIME_READY:
1388
+ return
1389
+ ui.add_head_html(f"<style>@layer overrides {{\n{_OVERLAY_CSS}\n}}</style>", shared=True)
1390
+ ui.add_head_html(f"<script>{_OVERLAY_JS}</script>", shared=True)
1391
+ _RUNTIME_READY = True
1392
+
1393
+
1394
+ def _bind_kind(el: Any, kind: str, **attrs: Any) -> None:
1395
+ el._props["data-ngbs-kind"] = kind
1396
+ with contextlib.suppress(Exception):
1397
+ el._props["data-ngbs-eid"] = str(el.id)
1398
+ for key, value in attrs.items():
1399
+ if value is None:
1400
+ continue
1401
+ attr = key if key.startswith("data-") else f"data-ngbs-{key.replace('_', '-')}"
1402
+ el._props[attr] = ("true" if value else "false") if isinstance(value, bool) else str(value)
1403
+
1404
+
1405
+ def _portal_to_overlay_root(el: Any) -> None:
1406
+ root = None
1407
+ with contextlib.suppress(Exception):
1408
+ root = ensure_overlay_root()
1409
+ if root is not None and root is not el and hasattr(el, "move"):
1410
+ with contextlib.suppress(Exception):
1411
+ el.move(root)
1412
+ return
1413
+ el._props["data-ngbs-portal"] = "true"
1414
+
1415
+
1416
+ def _set_open_state_props(el: Any, value: bool) -> None:
1417
+ el._is_open = bool(value)
1418
+ props: dict[str, Any] = getattr(el, "_props", {})
1419
+ props["data-ngbs-open"] = "true" if el._is_open else "false"
1420
+ epoch = int(getattr(el, "_ngbs_epoch", 0)) + 1
1421
+ el._ngbs_epoch = epoch
1422
+ props["data-ngbs-epoch"] = str(epoch)
1423
+
1424
+
1425
+ def _sync_open_class(el: Any) -> None:
1426
+ classes = getattr(el, "classes", None)
1427
+ if not getattr(el, "_css_show", False) or not callable(classes):
1428
+ return
1429
+ if getattr(el, "_is_open", False):
1430
+ classes(add="show")
1431
+ else:
1432
+ classes(remove="show")
1433
+
1434
+
1435
+ def _echo_open_state(el: Any, value: bool) -> None:
1436
+ desired = "true" if value else "false"
1437
+ props = getattr(el, "_props", {})
1438
+ if props.get("data-ngbs-open") != desired or bool(getattr(el, "_is_open", False)) != value:
1439
+ _set_open_state_props(el, value)
1440
+ _sync_open_class(el)
1441
+
1442
+
1443
+ class _SlotRedirect:
1444
+ def __enter__(self) -> Any:
1445
+ if getattr(self, "_children_passed", False):
1446
+ raise ChildrenError("Cannot pass children= and use as a context manager")
1447
+ host = getattr(self, "_slot_host", None)
1448
+ if host is not None:
1449
+ host.__enter__()
1450
+ return self
1451
+ parent_enter = getattr(super(), "__enter__", None)
1452
+ if parent_enter is not None:
1453
+ return parent_enter()
1454
+ return self
1455
+
1456
+ def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> Any:
1457
+ host = getattr(self, "_slot_host", None)
1458
+ if host is not None:
1459
+ return host.__exit__(exc_type, exc, tb)
1460
+ parent_exit = getattr(super(), "__exit__", None)
1461
+ if parent_exit is not None:
1462
+ return parent_exit(exc_type, exc, tb)
1463
+ return None
1464
+
1465
+
1466
+ class _OpenMixin:
1467
+ _css_show: bool = True
1468
+
1469
+ @property
1470
+ def is_open(self) -> bool:
1471
+ return bool(getattr(self, "_is_open", False))
1472
+
1473
+ @is_open.setter
1474
+ def is_open(self, value: bool) -> None:
1475
+ self._apply_open(bool(value))
1476
+
1477
+ def _apply_open(self, value: bool) -> None:
1478
+ _set_open_state_props(self, value)
1479
+ _sync_open_class(self)
1480
+ update = getattr(self, "update", None)
1481
+ if callable(update):
1482
+ update()
1483
+
1484
+ def open(self) -> None:
1485
+ self.is_open = True
1486
+
1487
+ def close(self) -> None:
1488
+ self.is_open = False
1489
+
1490
+ def toggle(self) -> None:
1491
+ self.is_open = not self.is_open
1492
+
1493
+ def show(self) -> None:
1494
+ self.open()
1495
+
1496
+ def hide(self) -> None:
1497
+ self.close()
1498
+
1499
+
1500
+ def _wire_overlay_events(
1501
+ el: Any,
1502
+ *,
1503
+ on_show: Callable[..., Any] | None = None,
1504
+ on_shown: Callable[..., Any] | None = None,
1505
+ on_hide: Callable[..., Any] | None = None,
1506
+ on_hidden: Callable[..., Any] | None = None,
1507
+ on_dismiss: Callable[..., Any] | None = None,
1508
+ ) -> None:
1509
+ el._on_show_cb = on_show
1510
+ el._on_shown_cb = on_shown
1511
+ el._on_hide_cb = on_hide
1512
+ el._on_hidden_cb = on_hidden
1513
+ el._on_dismiss_cb = on_dismiss
1514
+
1515
+ def _shown(_e: Any = None) -> None:
1516
+ _echo_open_state(el, True)
1517
+ if el._on_shown_cb is not None:
1518
+ el._on_shown_cb()
1519
+
1520
+ def _hidden(_e: Any = None) -> None:
1521
+ _echo_open_state(el, False)
1522
+ if el._on_hidden_cb is not None:
1523
+ el._on_hidden_cb()
1524
+
1525
+ def _dismiss(_e: Any = None) -> None:
1526
+ _echo_open_state(el, False)
1527
+ if el._on_dismiss_cb is not None:
1528
+ el._on_dismiss_cb()
1529
+
1530
+ el.on("shown", _shown)
1531
+ el.on("hidden", _hidden)
1532
+ el.on("dismiss", _dismiss)
1533
+ if on_show is not None:
1534
+ el.on("show", lambda _e=None: on_show())
1535
+ if on_hide is not None:
1536
+ el.on("hide", lambda _e=None: on_hide())
1537
+
1538
+
1539
+ class _ModalImpl(_SlotRedirect, _OpenMixin, BootstrapElement):
1540
+ component_name = "Modal"
1541
+ styling_target = "root"
1542
+ reserved_classes = ("modal", "fade", "show")
1543
+ _css_show = True
1544
+
1545
+ def __init__(
1546
+ self,
1547
+ children: Any = None,
1548
+ *,
1549
+ _surface: str | None = None,
1550
+ is_open: bool = False,
1551
+ size: str | None = None,
1552
+ fullscreen: bool | str = False,
1553
+ backdrop: bool | str = True,
1554
+ centered: bool = False,
1555
+ scrollable: bool = False,
1556
+ fade: bool = True,
1557
+ keyboard: bool = True,
1558
+ enforceFocus: bool | None = None,
1559
+ enforce_focus: bool | None = None,
1560
+ labelledby: str | None = None,
1561
+ dialog_class_name: str | None = None,
1562
+ dialog_style: dict[str, Any] | None = None,
1563
+ content_class_name: str | None = None,
1564
+ content_style: dict[str, Any] | None = None,
1565
+ backdrop_class_name: str | None = None,
1566
+ backdrop_style: dict[str, Any] | None = None,
1567
+ class_name: str | None = None,
1568
+ dialogStyle: dict[str, Any] | None = None,
1569
+ contentStyle: dict[str, Any] | None = None,
1570
+ on_show: Callable[..., Any] | None = None,
1571
+ on_shown: Callable[..., Any] | None = None,
1572
+ on_hide: Callable[..., Any] | None = None,
1573
+ on_hidden: Callable[..., Any] | None = None,
1574
+ on_dismiss: Callable[..., Any] | None = None,
1575
+ style: dict[str, Any] | None = None,
1576
+ id: str | None = None,
1577
+ className: str | None = None,
1578
+ key: Any = None,
1579
+ ) -> None:
1580
+ surface = _surface_of(self, _surface)
1581
+ with contextlib.suppress(Exception):
1582
+ self._surface = surface
1583
+ if dialog_style is None:
1584
+ dialog_style = dialogStyle
1585
+ if content_style is None:
1586
+ content_style = contentStyle
1587
+ if enforce_focus is None and enforceFocus is not None:
1588
+ enforce_focus = enforceFocus
1589
+ elif (
1590
+ enforce_focus is not None
1591
+ and enforceFocus is not None
1592
+ and bool(enforce_focus) != bool(enforceFocus)
1593
+ ):
1594
+ raise PropConflictError("enforce_focus/enforceFocus conflict")
1595
+ if enforce_focus is None:
1596
+ enforce_focus = True
1597
+ _ensure_overlay_runtime()
1598
+ super().__init__(
1599
+ children=None,
1600
+ id=id,
1601
+ class_name=class_name,
1602
+ className=className,
1603
+ style=style,
1604
+ key=key,
1605
+ )
1606
+ self._surface = surface
1607
+ self.classes("modal")
1608
+ if fade:
1609
+ self.classes("fade")
1610
+ self._props["tabindex"] = "-1"
1611
+ self._props["role"] = "dialog"
1612
+ if labelledby:
1613
+ self._props["aria-labelledby"] = labelledby
1614
+ dialog_classes = modal_dialog_classes(
1615
+ size=size,
1616
+ centered=centered,
1617
+ scrollable=scrollable,
1618
+ fullscreen=fullscreen,
1619
+ dialog_class_name=dialog_class_name,
1620
+ )
1621
+ with self:
1622
+ self._dialog = ui.element("div").classes(" ".join(dialog_classes))
1623
+ _style_obj(self._dialog, dialog_style)
1624
+ with self._dialog:
1625
+ content_classes = ["modal-content"]
1626
+ if content_class_name:
1627
+ content_classes.append(content_class_name)
1628
+ self._content = ui.element("div").classes(" ".join(content_classes))
1629
+ _style_obj(self._content, content_style)
1630
+ self._slot_host = self._content
1631
+ self._children_passed = children is not None
1632
+ if children is not None:
1633
+ _adopt_into(self._content, children, owner=self)
1634
+ self._apply_open(bool(is_open))
1635
+ backdrop_attr = backdrop if isinstance(backdrop, str) else bool(backdrop)
1636
+ _bind_kind(
1637
+ self,
1638
+ "modal",
1639
+ open=bool(is_open),
1640
+ backdrop=backdrop_attr,
1641
+ keyboard=bool(keyboard),
1642
+ focus=bool(enforce_focus),
1643
+ )
1644
+ if backdrop_class_name:
1645
+ self._props["data-ngbs-backdrop-class"] = backdrop_class_name
1646
+ if backdrop_style:
1647
+ self._props["data-ngbs-backdrop-style"] = _style_obj(None, backdrop_style)
1648
+ _portal_to_overlay_root(self)
1649
+ _wire_overlay_events(
1650
+ self,
1651
+ on_show=on_show,
1652
+ on_shown=on_shown,
1653
+ on_hide=on_hide,
1654
+ on_hidden=on_hidden,
1655
+ on_dismiss=on_dismiss,
1656
+ )
1657
+
1658
+ def _handle_delete(self) -> None: # noqa: D401
1659
+ self._props["data-ngbs-open"] = "false"
1660
+ super()._handle_delete()
1661
+
1662
+
1663
+ class _ModalHeaderImpl(_SlotRedirect, BootstrapElement):
1664
+ component_name = "ModalHeader"
1665
+ styling_target = "root"
1666
+ reserved_classes = ("modal-header",)
1667
+
1668
+ def __init__(
1669
+ self,
1670
+ children: Any = None,
1671
+ *,
1672
+ _surface: str | None = None,
1673
+ close_button: bool = True,
1674
+ class_name: str | None = None,
1675
+ className: str | None = None,
1676
+ style: dict[str, Any] | None = None,
1677
+ id: str | None = None,
1678
+ key: Any = None,
1679
+ ) -> None:
1680
+ surface = _surface_of(self, _surface)
1681
+ with contextlib.suppress(Exception):
1682
+ self._surface = surface
1683
+ super().__init__(
1684
+ children=None,
1685
+ id=id,
1686
+ class_name=class_name,
1687
+ className=className,
1688
+ style=style,
1689
+ key=key,
1690
+ )
1691
+ self._surface = surface
1692
+ self.classes("modal-header")
1693
+ with self:
1694
+ self._slot_host = ui.element("div")
1695
+ if close_button:
1696
+ close = ui.element("button").classes("btn-close")
1697
+ close._props["type"] = "button"
1698
+ close._props["data-ngbs-dismiss"] = "modal"
1699
+ close._props["aria-label"] = "Close"
1700
+ self._children_passed = children is not None
1701
+ if children is not None:
1702
+ _fill_text_or_children(self._slot_host, children, owner=self)
1703
+
1704
+
1705
+ class _ModalTitleImpl(BootstrapElement):
1706
+ component_name = "ModalTitle"
1707
+ styling_target = "root"
1708
+ reserved_classes = ("modal-title",)
1709
+
1710
+ def __init__(
1711
+ self,
1712
+ children: Any = None,
1713
+ *,
1714
+ _surface: str | None = None,
1715
+ tag: str = "h5",
1716
+ class_name: str | None = None,
1717
+ className: str | None = None,
1718
+ style: dict[str, Any] | None = None,
1719
+ id: str | None = None,
1720
+ key: Any = None,
1721
+ ) -> None:
1722
+ surface = _surface_of(self, _surface)
1723
+ with contextlib.suppress(Exception):
1724
+ self._surface = surface
1725
+ super().__init__(
1726
+ children=children,
1727
+ tag=tag,
1728
+ id=id,
1729
+ class_name=class_name,
1730
+ className=className,
1731
+ style=style,
1732
+ key=key,
1733
+ )
1734
+ self._surface = surface
1735
+ self.classes("modal-title")
1736
+ if not id:
1737
+ self._props["id"] = f"ngbs-modal-title-{self.id}"
1738
+
1739
+
1740
+ class _ModalBodyImpl(BootstrapElement):
1741
+ component_name = "ModalBody"
1742
+ styling_target = "root"
1743
+ reserved_classes = ("modal-body",)
1744
+
1745
+ def __init__(
1746
+ self,
1747
+ children: Any = None,
1748
+ *,
1749
+ _surface: str | None = None,
1750
+ class_name: str | None = None,
1751
+ className: str | None = None,
1752
+ style: dict[str, Any] | None = None,
1753
+ id: str | None = None,
1754
+ key: Any = None,
1755
+ ) -> None:
1756
+ surface = _surface_of(self, _surface)
1757
+ with contextlib.suppress(Exception):
1758
+ self._surface = surface
1759
+ super().__init__(
1760
+ children=children,
1761
+ id=id,
1762
+ class_name=class_name,
1763
+ className=className,
1764
+ style=style,
1765
+ key=key,
1766
+ )
1767
+ self._surface = surface
1768
+ self.classes("modal-body")
1769
+
1770
+
1771
+ class _ModalFooterImpl(BootstrapElement):
1772
+ component_name = "ModalFooter"
1773
+ styling_target = "root"
1774
+ reserved_classes = ("modal-footer",)
1775
+
1776
+ def __init__(
1777
+ self,
1778
+ children: Any = None,
1779
+ *,
1780
+ _surface: str | None = None,
1781
+ class_name: str | None = None,
1782
+ className: str | None = None,
1783
+ style: dict[str, Any] | None = None,
1784
+ id: str | None = None,
1785
+ key: Any = None,
1786
+ ) -> None:
1787
+ surface = _surface_of(self, _surface)
1788
+ with contextlib.suppress(Exception):
1789
+ self._surface = surface
1790
+ super().__init__(
1791
+ children=children,
1792
+ id=id,
1793
+ class_name=class_name,
1794
+ className=className,
1795
+ style=style,
1796
+ key=key,
1797
+ )
1798
+ self._surface = surface
1799
+ self.classes("modal-footer")
1800
+
1801
+
1802
+ class _CollapseImpl(_OpenMixin, BootstrapElement):
1803
+ """Collapse panel with height or width disclosure."""
1804
+
1805
+ component_name = "Collapse"
1806
+ styling_target = "root"
1807
+ reserved_classes = ("collapse", "show", "navbar-collapse", "collapse-horizontal")
1808
+ _css_show = True
1809
+
1810
+ def __init__(
1811
+ self,
1812
+ children: Any = None,
1813
+ *,
1814
+ _surface: str | None = None,
1815
+ is_open: bool = False,
1816
+ navbar: bool = False,
1817
+ dimension: str = "height",
1818
+ on_show: Callable[..., Any] | None = None,
1819
+ on_shown: Callable[..., Any] | None = None,
1820
+ on_hide: Callable[..., Any] | None = None,
1821
+ on_hidden: Callable[..., Any] | None = None,
1822
+ class_name: str | None = None,
1823
+ className: str | None = None,
1824
+ style: dict[str, Any] | None = None,
1825
+ id: str | None = None,
1826
+ key: Any = None,
1827
+ ) -> None:
1828
+ surface = _surface_of(self, _surface)
1829
+ with contextlib.suppress(Exception):
1830
+ self._surface = surface
1831
+ struct = collapse_structural_classes(navbar=navbar, dimension=dimension)
1832
+ if surface == "compat" and dimension != "height":
1833
+ raise UnsupportedPropError("dimension")
1834
+ _ensure_overlay_runtime()
1835
+ super().__init__(
1836
+ children=children,
1837
+ id=id,
1838
+ class_name=class_name,
1839
+ className=className,
1840
+ style=style,
1841
+ key=key,
1842
+ )
1843
+ self._surface = surface
1844
+ self.classes(" ".join(struct))
1845
+ self._apply_open(bool(is_open))
1846
+ _bind_kind(self, "collapse", open=bool(is_open), dimension=dimension)
1847
+ _wire_overlay_events(
1848
+ self, on_show=on_show, on_shown=on_shown, on_hide=on_hide, on_hidden=on_hidden
1849
+ )
1850
+
1851
+
1852
+ class _FadeImpl(BootstrapElement):
1853
+ """Fade transition wrapper using ``is_in`` (not ``is_open``)."""
1854
+
1855
+ component_name = "Fade"
1856
+ styling_target = "root"
1857
+ reserved_classes = ("fade", "show")
1858
+
1859
+ def __init__(
1860
+ self,
1861
+ children: Any = None,
1862
+ *,
1863
+ _surface: str | None = None,
1864
+ is_in: bool = False,
1865
+ timeout: int | dict[str, Any] | None = None,
1866
+ appear: bool = False,
1867
+ enter: bool = True,
1868
+ exit: bool = True,
1869
+ unmount_on_exit: bool = False,
1870
+ is_open: Any = _UNSET,
1871
+ tag: str = "div",
1872
+ class_name: str | None = None,
1873
+ className: str | None = None,
1874
+ style: dict[str, Any] | None = None,
1875
+ id: str | None = None,
1876
+ key: Any = None,
1877
+ ) -> None:
1878
+ surface = _surface_of(self, _surface)
1879
+ with contextlib.suppress(Exception):
1880
+ self._surface = surface
1881
+ if is_open is not _UNSET:
1882
+ raise UnsupportedPropError("is_open")
1883
+ super().__init__(
1884
+ children=children,
1885
+ tag=tag,
1886
+ id=id,
1887
+ class_name=class_name,
1888
+ className=className,
1889
+ style=style,
1890
+ key=key,
1891
+ )
1892
+ self._surface = surface
1893
+ self.classes("fade")
1894
+ self._timeout = timeout
1895
+ self._appear = bool(appear)
1896
+ self._enter = bool(enter)
1897
+ self._exit = bool(exit)
1898
+ self._unmount_on_exit = bool(unmount_on_exit)
1899
+ self._is_in = False
1900
+ self._apply_in(bool(is_in), initial=True)
1901
+
1902
+ def _apply_in(self, value: bool, *, initial: bool = False) -> None:
1903
+ self._is_in = bool(value)
1904
+ if self._is_in:
1905
+ self.classes(add="show")
1906
+ if self._unmount_on_exit:
1907
+ self._props.pop("hidden", None)
1908
+ else:
1909
+ self.classes(remove="show")
1910
+ if self._unmount_on_exit:
1911
+ self._props["hidden"] = True
1912
+ if not initial:
1913
+ update = getattr(self, "update", None)
1914
+ if callable(update):
1915
+ update()
1916
+
1917
+ @property
1918
+ def is_in(self) -> bool:
1919
+ return bool(self._is_in)
1920
+
1921
+ @is_in.setter
1922
+ def is_in(self, value: bool) -> None:
1923
+ self._apply_in(bool(value))
1924
+
1925
+ def show(self) -> None:
1926
+ self.is_in = True
1927
+
1928
+ def hide(self) -> None:
1929
+ self.is_in = False
1930
+
1931
+
1932
+ class _OffcanvasImpl(_SlotRedirect, _OpenMixin, BootstrapElement):
1933
+ component_name = "Offcanvas"
1934
+ styling_target = "root"
1935
+ reserved_classes = (
1936
+ "offcanvas",
1937
+ "offcanvas-start",
1938
+ "offcanvas-end",
1939
+ "offcanvas-top",
1940
+ "offcanvas-bottom",
1941
+ "show",
1942
+ )
1943
+ _css_show = True
1944
+
1945
+ def __init__(
1946
+ self,
1947
+ children: Any = None,
1948
+ *,
1949
+ _surface: str | None = None,
1950
+ is_open: bool = False,
1951
+ placement: str = "start",
1952
+ backdrop: bool | str = True,
1953
+ scroll: bool = False,
1954
+ keyboard: bool = True,
1955
+ title: str | None = None,
1956
+ labelledby: str | None = None,
1957
+ header_class_name: str | None = None,
1958
+ body_class_name: str | None = None,
1959
+ on_show: Callable[..., Any] | None = None,
1960
+ on_shown: Callable[..., Any] | None = None,
1961
+ on_hide: Callable[..., Any] | None = None,
1962
+ on_hidden: Callable[..., Any] | None = None,
1963
+ on_dismiss: Callable[..., Any] | None = None,
1964
+ class_name: str | None = None,
1965
+ className: str | None = None,
1966
+ style: dict[str, Any] | None = None,
1967
+ id: str | None = None,
1968
+ key: Any = None,
1969
+ ) -> None:
1970
+ surface = _surface_of(self, _surface)
1971
+ with contextlib.suppress(Exception):
1972
+ self._surface = surface
1973
+ struct = offcanvas_structural_classes(placement=placement)
1974
+ _ensure_overlay_runtime()
1975
+ super().__init__(
1976
+ children=None,
1977
+ id=id,
1978
+ class_name=class_name,
1979
+ className=className,
1980
+ style=style,
1981
+ key=key,
1982
+ )
1983
+ self._surface = surface
1984
+ self.classes(" ".join(struct))
1985
+ self._props["tabindex"] = "-1"
1986
+ with self:
1987
+ if title is not None:
1988
+ header_classes = ["offcanvas-header"]
1989
+ if header_class_name:
1990
+ header_classes.append(header_class_name)
1991
+ header = ui.element("div").classes(" ".join(header_classes))
1992
+ with header:
1993
+ heading = ui.element("h5").classes("offcanvas-title")
1994
+ title_dom_id = labelledby or f"ngbs-offcanvas-title-{self.id}"
1995
+ heading._props["id"] = title_dom_id
1996
+ if hasattr(heading, "set_text"):
1997
+ heading.set_text(str(title))
1998
+ else:
1999
+ set_element_text(heading, str(title))
2000
+ close = ui.element("button").classes("btn-close")
2001
+ close._props["type"] = "button"
2002
+ close._props["data-ngbs-dismiss"] = "offcanvas"
2003
+ close._props["aria-label"] = "Close"
2004
+ self._props["aria-labelledby"] = title_dom_id
2005
+ elif labelledby:
2006
+ self._props["aria-labelledby"] = labelledby
2007
+ body_classes = ["offcanvas-body"]
2008
+ if body_class_name:
2009
+ body_classes.append(body_class_name)
2010
+ self._slot_host = ui.element("div").classes(" ".join(body_classes))
2011
+ self._children_passed = children is not None
2012
+ if children is not None:
2013
+ _adopt_into(self._slot_host, children, owner=self)
2014
+ self._apply_open(bool(is_open))
2015
+ backdrop_attr = backdrop if isinstance(backdrop, str) else bool(backdrop)
2016
+ _bind_kind(
2017
+ self,
2018
+ "offcanvas",
2019
+ open=bool(is_open),
2020
+ backdrop=backdrop_attr,
2021
+ keyboard=bool(keyboard),
2022
+ scroll=bool(scroll),
2023
+ placement=placement,
2024
+ )
2025
+ _portal_to_overlay_root(self)
2026
+ _wire_overlay_events(
2027
+ self,
2028
+ on_show=on_show,
2029
+ on_shown=on_shown,
2030
+ on_hide=on_hide,
2031
+ on_hidden=on_hidden,
2032
+ on_dismiss=on_dismiss,
2033
+ )
2034
+
2035
+ def _handle_delete(self) -> None:
2036
+ self._props["data-ngbs-open"] = "false"
2037
+ super()._handle_delete()
2038
+
2039
+
2040
+ class _DropdownMenuImpl(_SlotRedirect, _OpenMixin, BootstrapElement):
2041
+ component_name = "DropdownMenu"
2042
+ styling_target = "root"
2043
+ reserved_classes = ("dropdown", "dropup", "dropstart", "dropend", "btn-group", "nav-item")
2044
+ _css_show = False
2045
+
2046
+ def __init__(
2047
+ self,
2048
+ children: Any = None,
2049
+ *,
2050
+ _surface: str | None = None,
2051
+ label: str | None = None,
2052
+ color: str = "secondary",
2053
+ size: str | None = None,
2054
+ direction: str = "down",
2055
+ menu_variant: str | None = None,
2056
+ align_end: bool | None = None,
2057
+ disabled: bool = False,
2058
+ nav: bool = False,
2059
+ caret: bool = True,
2060
+ in_navbar: bool = False,
2061
+ group: bool = False,
2062
+ toggle_style: dict[str, Any] | None = None,
2063
+ toggle_class_name: str | None = None,
2064
+ on_item_click: Any = _UNSET,
2065
+ is_open: Any = _UNSET,
2066
+ on_toggle: Callable[..., Any] | None = None,
2067
+ right: Any = _UNSET,
2068
+ addon_type: Any = _UNSET,
2069
+ class_name: str | None = None,
2070
+ className: str | None = None,
2071
+ style: dict[str, Any] | None = None,
2072
+ id: str | None = None,
2073
+ key: Any = None,
2074
+ ) -> None:
2075
+ surface = _surface_of(self, _surface)
2076
+ with contextlib.suppress(Exception):
2077
+ self._surface = surface
2078
+ if right is not _UNSET:
2079
+ raise UnsupportedPropError("right")
2080
+ if addon_type is not _UNSET:
2081
+ raise UnsupportedPropError("addon_type")
2082
+ if surface == "compat" and is_open is not _UNSET:
2083
+ raise UnsupportedPropError("is_open is a native-only extension on DropdownMenu")
2084
+ if surface == "compat" and on_item_click is not _UNSET:
2085
+ raise UnsupportedPropError("on_item_click is a native-only extension on DropdownMenu")
2086
+ nav_like = bool(nav or in_navbar)
2087
+ root_classes = dropdown_root_classes(
2088
+ direction=direction, nav=nav, group=group, in_navbar=in_navbar
2089
+ )
2090
+ toggle_classes = dropdown_toggle_classes(color=color, size=size, caret=caret, nav=nav_like)
2091
+ menu_classes = dropdown_menu_classes(menu_variant=menu_variant, align_end=align_end)
2092
+ open_value = False if is_open is _UNSET or is_open is None else bool(is_open)
2093
+ _ensure_overlay_runtime()
2094
+ super().__init__(
2095
+ children=None,
2096
+ tag="li" if nav_like else "div",
2097
+ id=id,
2098
+ class_name=class_name,
2099
+ className=className,
2100
+ style=style,
2101
+ key=key,
2102
+ )
2103
+ self._surface = surface
2104
+ self.classes(" ".join(root_classes))
2105
+ with self:
2106
+ toggle_tag = "a" if nav_like else "button"
2107
+ self._toggle = ui.element(toggle_tag).classes(" ".join(toggle_classes))
2108
+ self._toggle._props["data-ngbs-role"] = "toggle"
2109
+ self._toggle._props["aria-expanded"] = "true" if open_value else "false"
2110
+ self._toggle._props["aria-haspopup"] = "true"
2111
+ if nav_like:
2112
+ self._toggle._props["href"] = "#"
2113
+ self._toggle._props["role"] = "button"
2114
+ else:
2115
+ self._toggle._props["type"] = "button"
2116
+ if disabled:
2117
+ if nav_like:
2118
+ self._toggle.classes(add="disabled")
2119
+ self._toggle._props["aria-disabled"] = "true"
2120
+ self._toggle._props["tabindex"] = "-1"
2121
+ else:
2122
+ self._toggle._props["disabled"] = True
2123
+ if toggle_class_name:
2124
+ self._toggle.classes(toggle_class_name)
2125
+ _style_obj(self._toggle, toggle_style)
2126
+ text = "" if label is None else str(label)
2127
+ if hasattr(self._toggle, "set_text"):
2128
+ self._toggle.set_text(text)
2129
+ else:
2130
+ set_element_text(self._toggle, text)
2131
+ self._slot_host = ui.element("ul").classes(" ".join(menu_classes))
2132
+ self._children_passed = children is not None
2133
+ if children is not None:
2134
+ _adopt_into(self._slot_host, children, owner=self)
2135
+ self._on_item_click = None if on_item_click is _UNSET else on_item_click
2136
+ self._apply_open(open_value)
2137
+ _bind_kind(self, "dropdown", open=open_value, direction=direction)
2138
+ _wire_overlay_events(self)
2139
+ if on_toggle is not None:
2140
+ self.on("toggle", lambda _e=None: on_toggle())
2141
+
2142
+ def _apply_open(self, value: bool) -> None:
2143
+ super()._apply_open(value)
2144
+ host = getattr(self, "_slot_host", None)
2145
+ if host is not None:
2146
+ if value:
2147
+ host.classes(add="show")
2148
+ else:
2149
+ host.classes(remove="show")
2150
+ toggle = getattr(self, "_toggle", None)
2151
+ if toggle is not None:
2152
+ toggle._props["aria-expanded"] = "true" if value else "false"
2153
+
2154
+
2155
+ class _DropdownMenuItemImpl(_SlotRedirect, BootstrapElement):
2156
+ component_name = "DropdownMenuItem"
2157
+ styling_target = "root"
2158
+ reserved_classes = ("dropdown-item", "dropdown-header", "dropdown-divider")
2159
+ children_kind = "phrasing"
2160
+
2161
+ def __init__(
2162
+ self,
2163
+ children: Any = None,
2164
+ *,
2165
+ _surface: str | None = None,
2166
+ header: bool = False,
2167
+ divider: bool = False,
2168
+ disabled: bool = False,
2169
+ active: bool = False,
2170
+ href: str | None = None,
2171
+ n_clicks: int = 0,
2172
+ toggle: bool = True,
2173
+ class_name: str | None = None,
2174
+ className: str | None = None,
2175
+ style: dict[str, Any] | None = None,
2176
+ id: str | None = None,
2177
+ key: Any = None,
2178
+ ) -> None:
2179
+ surface = _surface_of(self, _surface)
2180
+ with contextlib.suppress(Exception):
2181
+ self._surface = surface
2182
+ if header and divider:
2183
+ raise PropConflictError("header/divider conflict")
2184
+ inner_classes = dropdown_item_inner_classes(
2185
+ header=header, divider=divider, disabled=disabled, active=active
2186
+ )
2187
+ super().__init__(
2188
+ children=None,
2189
+ tag="li",
2190
+ id=id,
2191
+ class_name=class_name,
2192
+ className=className,
2193
+ style=style,
2194
+ key=key,
2195
+ )
2196
+ self._surface = surface
2197
+ self._n_clicks = int(n_clicks)
2198
+ self._toggle = bool(toggle)
2199
+ self._disabled = bool(disabled)
2200
+ self._is_header = bool(header)
2201
+ self._is_divider = bool(divider)
2202
+ self._slot_host = None
2203
+ with self:
2204
+ if divider:
2205
+ ui.element("hr").classes(" ".join(inner_classes))
2206
+ elif header:
2207
+ self._slot_host = ui.element("h6").classes(" ".join(inner_classes))
2208
+ else:
2209
+ tag = "a" if href else "button"
2210
+ self._slot_host = ui.element(tag).classes(" ".join(inner_classes))
2211
+ if href:
2212
+ self._slot_host._props["href"] = href
2213
+ else:
2214
+ self._slot_host._props["type"] = "button"
2215
+ self._slot_host._props["data-ngbs-item-toggle"] = "true" if toggle else "false"
2216
+ if disabled:
2217
+ self._slot_host._props["aria-disabled"] = "true"
2218
+ self._slot_host._props["tabindex"] = "-1"
2219
+ if href is None:
2220
+ self._slot_host._props["disabled"] = True
2221
+ if active:
2222
+ self._slot_host._props["aria-current"] = "true"
2223
+ self._slot_host.on("click", self._handle_click)
2224
+ self._children_passed = children is not None
2225
+ if children is not None and self._slot_host is not None:
2226
+ _fill_text_or_children(self._slot_host, children, owner=self)
2227
+
2228
+ @property
2229
+ def n_clicks(self) -> int:
2230
+ return self._n_clicks
2231
+
2232
+ @n_clicks.setter
2233
+ def n_clicks(self, value: int) -> None:
2234
+ self._n_clicks = int(value)
2235
+
2236
+ def _handle_click(self, *_args: Any, **_kwargs: Any) -> None:
2237
+ if self._disabled or self._is_header or self._is_divider:
2238
+ return
2239
+ self._n_clicks += 1
2240
+ parent = _find_ancestor(self, "DropdownMenu")
2241
+ if parent is None:
2242
+ return
2243
+ callback = getattr(parent, "_on_item_click", None)
2244
+ if callback is not None:
2245
+ callback(self)
2246
+ if self._toggle:
2247
+ closer = getattr(parent, "close", None)
2248
+ if callable(closer):
2249
+ closer()
2250
+
2251
+
2252
+ class _AccordionItemImpl(_SlotRedirect, BootstrapElement):
2253
+ component_name = "AccordionItem"
2254
+ styling_target = "root"
2255
+ reserved_classes = ("accordion-item",)
2256
+ children_kind = "auto"
2257
+
2258
+ def __init__(
2259
+ self,
2260
+ children: Any = None,
2261
+ *,
2262
+ _surface: str | None = None,
2263
+ title: str | None = None,
2264
+ item_id: str | None = None,
2265
+ class_name: str | None = None,
2266
+ title_class_name: str | None = None,
2267
+ className: str | None = None,
2268
+ style: dict[str, Any] | None = None,
2269
+ id: str | None = None,
2270
+ key: Any = None,
2271
+ ) -> None:
2272
+ surface = _surface_of(self, _surface)
2273
+ with contextlib.suppress(Exception):
2274
+ self._surface = surface
2275
+ super().__init__(
2276
+ children=None,
2277
+ id=id,
2278
+ class_name=class_name,
2279
+ className=className,
2280
+ style=style,
2281
+ key=key,
2282
+ )
2283
+ self._surface = surface
2284
+ self.classes("accordion-item")
2285
+ self._item_id = item_id
2286
+ self._expanded = False
2287
+ with self:
2288
+ self._header = ui.element("h2").classes("accordion-header")
2289
+ with self._header:
2290
+ self._button = ui.element("button").classes("accordion-button collapsed")
2291
+ self._button._props["type"] = "button"
2292
+ self._button._props["aria-expanded"] = "false"
2293
+ if title_class_name:
2294
+ self._button.classes(title_class_name)
2295
+ if title is not None:
2296
+ if hasattr(self._button, "set_text"):
2297
+ self._button.set_text(str(title))
2298
+ else:
2299
+ set_element_text(self._button, str(title))
2300
+ self._collapse = ui.element("div").classes("accordion-collapse collapse")
2301
+ self._collapse._props["role"] = "region"
2302
+ with self._collapse:
2303
+ self._slot_host = ui.element("div").classes("accordion-body")
2304
+ self._children_passed = children is not None
2305
+ if children is not None:
2306
+ _adopt_into(self._slot_host, children, owner=self)
2307
+ self._button.on("click", self._on_header_click)
2308
+ if self._item_id:
2309
+ self._bind_accordion_ids()
2310
+
2311
+ @property
2312
+ def item_id(self) -> str | None:
2313
+ return self._item_id
2314
+
2315
+ def _bind_accordion_ids(self) -> None:
2316
+ ident = self._item_id or "item"
2317
+ collapse_id = f"{ident}-collapse"
2318
+ header_id = f"{ident}-header"
2319
+ self._header._props["id"] = header_id
2320
+ self._collapse._props["id"] = collapse_id
2321
+ self._button._props["aria-controls"] = collapse_id
2322
+ self._collapse._props["aria-labelledby"] = header_id
2323
+
2324
+ def _set_expanded(self, expanded: bool) -> None:
2325
+ self._expanded = bool(expanded)
2326
+ if self._expanded:
2327
+ self._button.classes(remove="collapsed")
2328
+ self._button._props["aria-expanded"] = "true"
2329
+ self._collapse.classes(add="show")
2330
+ else:
2331
+ self._button.classes(add="collapsed")
2332
+ self._button._props["aria-expanded"] = "false"
2333
+ self._collapse.classes(remove="show")
2334
+ for node in (self._button, self._collapse):
2335
+ updater = getattr(node, "update", None)
2336
+ if callable(updater):
2337
+ updater()
2338
+
2339
+ def _on_header_click(self, *_args: Any, **_kwargs: Any) -> None:
2340
+ parent = _find_ancestor(self, "Accordion")
2341
+ ident = self._item_id
2342
+ if parent is not None and ident:
2343
+ parent._toggle_item(ident)
2344
+ return
2345
+ self._set_expanded(not self._expanded)
2346
+
2347
+
2348
+ class _AccordionImpl(BootstrapElement):
2349
+ component_name = "Accordion"
2350
+ styling_target = "root"
2351
+ reserved_classes = ("accordion", "accordion-flush")
2352
+ children_kind = "grouping"
2353
+
2354
+ def __init__(
2355
+ self,
2356
+ children: Any = None,
2357
+ *,
2358
+ _surface: str | None = None,
2359
+ active_item: str | list[str] | None = None,
2360
+ always_open: bool = False,
2361
+ start_collapsed: bool = False,
2362
+ flush: bool = False,
2363
+ persist: str = "off",
2364
+ on_change: Callable[..., Any] | None = None,
2365
+ class_name: str | None = None,
2366
+ className: str | None = None,
2367
+ style: dict[str, Any] | None = None,
2368
+ id: str | None = None,
2369
+ key: Any = None,
2370
+ ) -> None:
2371
+ surface = _surface_of(self, _surface)
2372
+ with contextlib.suppress(Exception):
2373
+ self._surface = surface
2374
+ struct = accordion_structural_classes(flush=flush)
2375
+ super().__init__(
2376
+ children=children,
2377
+ id=id,
2378
+ class_name=class_name,
2379
+ className=className,
2380
+ style=style,
2381
+ key=key,
2382
+ )
2383
+ self._surface = surface
2384
+ self.classes(" ".join(struct))
2385
+ self._active_item_arg = active_item
2386
+ self._start_collapsed = bool(start_collapsed)
2387
+ self._always_open = bool(always_open)
2388
+ self._on_change = on_change
2389
+ self._persist = persist
2390
+ self._items: list[Any] = []
2391
+ self._active_item: str | list[str] | None = [] if self._always_open else None
2392
+ self._finalize_items()
2393
+
2394
+ def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> Any:
2395
+ result = super().__exit__(exc_type, exc, tb)
2396
+ self._finalize_items()
2397
+ return result
2398
+
2399
+ def _item_ids(self) -> list[str]:
2400
+ return [
2401
+ ident for ident in (getattr(item, "_item_id", None) for item in self._items) if ident
2402
+ ]
2403
+
2404
+ def _finalize_items(self) -> None:
2405
+ items = _collect_accordion_items(self)
2406
+ given = [getattr(item, "_item_id", None) for item in items]
2407
+ ids = generate_accordion_item_ids(given)
2408
+ for item, ident in zip(items, ids, strict=True):
2409
+ item._item_id = ident
2410
+ bind = getattr(item, "_bind_accordion_ids", None)
2411
+ if callable(bind):
2412
+ bind()
2413
+ default_first = (not self._start_collapsed) and (self._active_item_arg is None)
2414
+ self._items = items
2415
+ self._active_item = resolve_accordion_active(
2416
+ self._active_item_arg,
2417
+ always_open=self._always_open,
2418
+ item_ids=ids,
2419
+ default_first=default_first,
2420
+ )
2421
+ self._sync_item_expanded()
2422
+
2423
+ def _sync_item_expanded(self) -> None:
2424
+ active = self._active_item
2425
+ selected: set[str]
2426
+ if self._always_open:
2427
+ values = active if isinstance(active, list) else ([] if active is None else [active])
2428
+ selected = set(values)
2429
+ else:
2430
+ selected = {active} if isinstance(active, str) and active else set()
2431
+ for item in self._items:
2432
+ ident = getattr(item, "_item_id", None)
2433
+ setter = getattr(item, "_set_expanded", None)
2434
+ if callable(setter):
2435
+ setter(ident in selected)
2436
+
2437
+ def _toggle_item(self, item_id: str) -> None:
2438
+ if self._always_open:
2439
+ current = list(self._active_item or [])
2440
+ if item_id in current:
2441
+ current.remove(item_id)
2442
+ else:
2443
+ current.append(item_id)
2444
+ self.active_item = current
2445
+ return
2446
+ self.active_item = None if self._active_item == item_id else item_id
2447
+
2448
+ @property
2449
+ def active_item(self) -> str | list[str] | None:
2450
+ return self._active_item
2451
+
2452
+ @active_item.setter
2453
+ def active_item(self, value: str | list[str] | None) -> None:
2454
+ resolved = resolve_accordion_active(
2455
+ value,
2456
+ always_open=self._always_open,
2457
+ item_ids=self._item_ids(),
2458
+ default_first=False,
2459
+ )
2460
+ if resolved == self._active_item:
2461
+ return
2462
+ self._active_item = resolved
2463
+ self._sync_item_expanded()
2464
+ if self._on_change is not None:
2465
+ self._on_change(resolved)
2466
+
2467
+ @property
2468
+ def always_open(self) -> bool:
2469
+ return self._always_open
2470
+
2471
+ @always_open.setter
2472
+ def always_open(self, value: bool) -> None:
2473
+ value = bool(value)
2474
+ if value == self._always_open:
2475
+ return
2476
+ current = self._active_item
2477
+ self._always_open = value
2478
+ if value:
2479
+ self._active_item = (
2480
+ [] if current is None else current if isinstance(current, list) else [current]
2481
+ )
2482
+ else:
2483
+ self._active_item = (
2484
+ (current[0] if current else None) if isinstance(current, list) else current
2485
+ )
2486
+ self._sync_item_expanded()
2487
+
2488
+
2489
+ class _TooltipImpl(_OpenMixin, BootstrapElement):
2490
+ component_name = "Tooltip"
2491
+ styling_target = "root"
2492
+ reserved_classes = ("tooltip",)
2493
+
2494
+ def __init__(
2495
+ self,
2496
+ children: Any = None,
2497
+ *,
2498
+ _surface: str | None = None,
2499
+ target: str | BootstrapElement | None = None,
2500
+ placement: str = "auto",
2501
+ trigger: str | None = None,
2502
+ delay: dict[str, Any] | int | None = None,
2503
+ is_open: bool | None = None,
2504
+ autohide: bool | None = None,
2505
+ class_name: str | None = None,
2506
+ className: str | None = None,
2507
+ style: dict[str, Any] | None = None,
2508
+ id: str | None = None,
2509
+ key: Any = None,
2510
+ ) -> None:
2511
+ surface = _surface_of(self, _surface)
2512
+ with contextlib.suppress(Exception):
2513
+ self._surface = surface
2514
+ if target is None:
2515
+ raise TypeError("Tooltip requires target")
2516
+ _ensure_overlay_runtime()
2517
+ super().__init__(
2518
+ children=children,
2519
+ id=id,
2520
+ class_name=class_name,
2521
+ className=className,
2522
+ style=style,
2523
+ key=key,
2524
+ )
2525
+ self._surface = surface
2526
+ if isinstance(target, str):
2527
+ target_kind = "public"
2528
+ target_spec = target
2529
+ else:
2530
+ target_kind = "eid"
2531
+ target_spec = str(getattr(target, "id", target))
2532
+ self._apply_open(bool(is_open) if is_open is not None else False)
2533
+ _bind_kind(
2534
+ self,
2535
+ "tooltip",
2536
+ target=target_spec,
2537
+ target_kind=target_kind,
2538
+ placement=placement,
2539
+ trigger=trigger or "hover focus",
2540
+ autohide=True if autohide is None else bool(autohide),
2541
+ open=bool(is_open) if is_open is not None else False,
2542
+ )
2543
+ if delay is not None:
2544
+ self._props["data-ngbs-delay"] = (
2545
+ json.dumps(delay) if isinstance(delay, dict) else str(delay)
2546
+ )
2547
+ _portal_to_overlay_root(self)
2548
+
2549
+ def _handle_delete(self) -> None:
2550
+ super()._handle_delete()
2551
+
2552
+
2553
+ Modal, DbcModal = _make_surfaces(_ModalImpl)
2554
+ ModalHeader, DbcModalHeader = _make_surfaces(_ModalHeaderImpl)
2555
+ ModalTitle, DbcModalTitle = _make_surfaces(_ModalTitleImpl)
2556
+ ModalBody, DbcModalBody = _make_surfaces(_ModalBodyImpl)
2557
+ ModalFooter, DbcModalFooter = _make_surfaces(_ModalFooterImpl)
2558
+ Collapse, DbcCollapse = _make_surfaces(_CollapseImpl)
2559
+ Fade, DbcFade = _make_surfaces(_FadeImpl)
2560
+ Offcanvas, DbcOffcanvas = _make_surfaces(_OffcanvasImpl)
2561
+ DropdownMenu, DbcDropdownMenu = _make_surfaces(_DropdownMenuImpl)
2562
+ DropdownMenuItem, DbcDropdownMenuItem = _make_surfaces(_DropdownMenuItemImpl)
2563
+ Accordion, DbcAccordion = _make_surfaces(_AccordionImpl)
2564
+ AccordionItem, DbcAccordionItem = _make_surfaces(_AccordionItemImpl)
2565
+ Tooltip, DbcTooltip = _make_surfaces(_TooltipImpl)
2566
+
2567
+ modal = Modal
2568
+ modal_header = ModalHeader
2569
+ modal_title = ModalTitle
2570
+ modal_body = ModalBody
2571
+ modal_footer = ModalFooter
2572
+ collapse = Collapse
2573
+ fade = Fade
2574
+ offcanvas = Offcanvas
2575
+ dropdown_menu = DropdownMenu
2576
+ dropdown_menu_item = DropdownMenuItem
2577
+ accordion = Accordion
2578
+ accordion_item = AccordionItem
2579
+ tooltip = Tooltip
2580
+
2581
+ __all__ = [
2582
+ "Modal",
2583
+ "DbcModal",
2584
+ "modal",
2585
+ "ModalHeader",
2586
+ "DbcModalHeader",
2587
+ "modal_header",
2588
+ "ModalTitle",
2589
+ "DbcModalTitle",
2590
+ "modal_title",
2591
+ "ModalBody",
2592
+ "DbcModalBody",
2593
+ "modal_body",
2594
+ "ModalFooter",
2595
+ "DbcModalFooter",
2596
+ "modal_footer",
2597
+ "Collapse",
2598
+ "DbcCollapse",
2599
+ "collapse",
2600
+ "Fade",
2601
+ "DbcFade",
2602
+ "fade",
2603
+ "Offcanvas",
2604
+ "DbcOffcanvas",
2605
+ "offcanvas",
2606
+ "DropdownMenu",
2607
+ "DbcDropdownMenu",
2608
+ "dropdown_menu",
2609
+ "DropdownMenuItem",
2610
+ "DbcDropdownMenuItem",
2611
+ "dropdown_menu_item",
2612
+ "Accordion",
2613
+ "DbcAccordion",
2614
+ "accordion",
2615
+ "AccordionItem",
2616
+ "DbcAccordionItem",
2617
+ "accordion_item",
2618
+ "Tooltip",
2619
+ "DbcTooltip",
2620
+ "tooltip",
2621
+ "accordion_structural_classes",
2622
+ "collapse_structural_classes",
2623
+ "dropdown_direction_class",
2624
+ "dropdown_item_inner_classes",
2625
+ "dropdown_menu_classes",
2626
+ "dropdown_root_classes",
2627
+ "dropdown_toggle_classes",
2628
+ "fade_structural_classes",
2629
+ "generate_accordion_item_ids",
2630
+ "modal_dialog_classes",
2631
+ "offcanvas_structural_classes",
2632
+ "resolve_accordion_active",
2633
+ ]