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,238 @@
1
+ """Per-client color mode and stylesheet selection."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+ from . import themes as _themes
9
+ from ._host import add_head_html, client_store, get_client, run_javascript
10
+ from .assets import _validate_stylesheet_url, _validated_theme_cdn, get_asset_manager
11
+
12
+ __all__ = ["ThemeController"]
13
+
14
+ THEME_STYLE_ID = "ngbs-theme-client"
15
+
16
+ _MODE_VALUES = {"light", "dark", "auto"}
17
+
18
+ _RUNTIME_JS = r"""
19
+ (() => {
20
+ if (window.ngbsSetColorMode) return;
21
+ const themeStyleId = __NGBS_THEME_STYLE_ID__;
22
+ const styleMode = __NGBS_STYLE_MODE__;
23
+ let observer = null;
24
+ let media = null;
25
+ let domReadyHandler = null;
26
+ let lastResolved = 'light';
27
+ const targets = () => {
28
+ if (styleMode === 'unscoped') {
29
+ return document.documentElement ? [document.documentElement] : [];
30
+ }
31
+ return document.querySelectorAll('.ngbs, .ngbs-overlay');
32
+ };
33
+ const apply = (value) => {
34
+ lastResolved = value;
35
+ targets().forEach((el) => el.setAttribute('data-bs-theme', value));
36
+ };
37
+ const clearNiceguiObserver = () => {
38
+ if (observer) { observer.disconnect(); observer = null; }
39
+ if (domReadyHandler) {
40
+ document.removeEventListener('DOMContentLoaded', domReadyHandler);
41
+ domReadyHandler = null;
42
+ }
43
+ };
44
+ const armNiceguiObserver = (read) => {
45
+ const body = document.body;
46
+ if (!body) {
47
+ domReadyHandler = () => {
48
+ domReadyHandler = null;
49
+ armNiceguiObserver(read);
50
+ };
51
+ document.addEventListener('DOMContentLoaded', domReadyHandler, { once: true });
52
+ return;
53
+ }
54
+ observer = new MutationObserver(() => apply(read() ? 'dark' : 'light'));
55
+ observer.observe(body, { attributes: true, attributeFilter: ['class'] });
56
+ apply(read() ? 'dark' : 'light');
57
+ };
58
+ window.ngbsApplyCurrentTheme = () => apply(lastResolved);
59
+ window.ngbsSetColorMode = (mode, followNiceguiDark) => {
60
+ clearNiceguiObserver();
61
+ if (media) { media.removeEventListener('change', media._ngbsRun); media = null; }
62
+ if (mode === 'light' || mode === 'dark') { apply(mode); return; }
63
+ if (followNiceguiDark) {
64
+ const read = () => document.body ? document.body.classList.contains('body--dark') : false;
65
+ armNiceguiObserver(read);
66
+ return;
67
+ }
68
+ media = window.matchMedia('(prefers-color-scheme: dark)');
69
+ media._ngbsRun = () => apply(media.matches ? 'dark' : 'light');
70
+ media.addEventListener('change', media._ngbsRun);
71
+ media._ngbsRun();
72
+ };
73
+ const escapeCssString = (value) => value
74
+ .replace(/\\/g, '\\\\')
75
+ .replace(/"/g, '\\"')
76
+ .replace(/\r/g, '\\D ')
77
+ .replace(/\n/g, '\\A ');
78
+ window.ngbsSetThemeSheet = (href) => {
79
+ let tag = document.getElementById(themeStyleId);
80
+ if (!tag) {
81
+ tag = document.createElement('style');
82
+ tag.id = themeStyleId;
83
+ document.head.appendChild(tag);
84
+ }
85
+ tag.textContent = '@import url("' + escapeCssString(href) + '") layer(overrides);';
86
+ };
87
+ })();
88
+ """
89
+
90
+
91
+ def _runtime_js(style_mode: str) -> str:
92
+ return _RUNTIME_JS.replace("__NGBS_THEME_STYLE_ID__", json.dumps(THEME_STYLE_ID)).replace(
93
+ "__NGBS_STYLE_MODE__", json.dumps(style_mode)
94
+ )
95
+
96
+
97
+ def _normalize_mode(mode: str | None) -> str:
98
+ value = str(mode).lower()
99
+ if value not in _MODE_VALUES:
100
+ raise ValueError(f"color_mode must be light|dark|auto, got {mode!r}")
101
+ return value
102
+
103
+
104
+ class ThemeController:
105
+ """Page-lifetime theme state for one NiceGUI client.
106
+
107
+ Obtained through :meth:`for_client`; never constructed directly.
108
+ """
109
+
110
+ def __init__(
111
+ self,
112
+ client: Any,
113
+ *,
114
+ color_mode: str,
115
+ follow_nicegui_dark: bool,
116
+ theme: _themes.Theme,
117
+ ) -> None:
118
+ self._client = client
119
+ self._color_mode = color_mode
120
+ self._follow_nicegui_dark = follow_nicegui_dark
121
+ self._theme = theme
122
+
123
+ @classmethod
124
+ def for_client(cls, client: Any | None = None) -> ThemeController:
125
+ """Bind or return the controller for ``client`` (defaults to the current one)."""
126
+ target = client if client is not None else get_client()
127
+ if target is None:
128
+ raise RuntimeError("ThemeController requires an active client")
129
+ store = client_store(target)
130
+ existing = store.get("theme_controller")
131
+ if isinstance(existing, cls):
132
+ return existing
133
+ manager = get_asset_manager()
134
+ raw_mode = manager.color_mode if manager is not None else "auto"
135
+ color_mode = _normalize_mode(raw_mode)
136
+ style_mode = manager.mode.value if manager is not None else "mixed"
137
+ controller = cls(
138
+ target,
139
+ color_mode=color_mode,
140
+ follow_nicegui_dark=manager.follow_nicegui_dark if manager is not None else True,
141
+ theme=manager.theme if manager is not None else _themes.BOOTSTRAP,
142
+ )
143
+ add_head_html(f"<script>{_runtime_js(style_mode)}</script>", shared=False, client=target)
144
+ controller._apply()
145
+ store["theme_controller"] = controller
146
+ return controller
147
+
148
+ # -- state ---------------------------------------------------------
149
+
150
+ @property
151
+ def color_mode(self) -> str:
152
+ return self._color_mode
153
+
154
+ @property
155
+ def theme(self) -> _themes.Theme:
156
+ return self._theme
157
+
158
+ def set_color_mode(self, mode: str) -> None:
159
+ """Switch this client to ``light``, ``dark``, or ``auto``."""
160
+ self._color_mode = _normalize_mode(mode)
161
+ self._apply()
162
+
163
+ def set_theme(self, theme: _themes.Theme | str) -> None:
164
+ """Swap this client's stylesheet without touching other clients."""
165
+ resolved = _resolve_theme(theme)
166
+ manager = get_asset_manager()
167
+ href = _stylesheet_url(resolved, manager)
168
+ if href is None:
169
+ raise RuntimeError("bundled theme selection requires setup() to serve its stylesheet")
170
+ run_javascript(
171
+ f"window.ngbsSetThemeSheet && window.ngbsSetThemeSheet({json.dumps(href)});",
172
+ client=self._client,
173
+ )
174
+ self._theme = resolved
175
+
176
+ def bind_to_nicegui_dark(self, enabled: bool = True) -> None:
177
+ """Choose whether ``auto`` follows the host dark state or the OS preference."""
178
+ self._follow_nicegui_dark = bool(enabled)
179
+ if self._color_mode == "auto":
180
+ self._apply()
181
+
182
+ # -- internals -----------------------------------------------------
183
+
184
+ def _apply(self) -> None:
185
+ mode = self._color_mode
186
+ follow = "true" if self._follow_nicegui_dark else "false"
187
+ run_javascript(
188
+ f"window.ngbsSetColorMode && window.ngbsSetColorMode({json.dumps(mode)}, {follow});",
189
+ client=self._client,
190
+ )
191
+ # Live anchors are updated by the runtime; the stored value seeds any
192
+ # scope or overlay root created later in this client.
193
+ store = client_store(self._client)
194
+ store["overlay_theme"] = None if mode == "auto" else mode
195
+
196
+
197
+ def _resolve_theme(theme: _themes.Theme | str) -> _themes.Theme:
198
+ if isinstance(theme, _themes.Theme):
199
+ for candidate in _themes._CATALOGUE:
200
+ if theme is candidate or theme == candidate:
201
+ return candidate
202
+ if theme.cdn:
203
+ _validate_stylesheet_url(theme.cdn)
204
+ elif not theme.bundled:
205
+ raise ValueError("theme must provide a bundled name or a plain http(s) URL")
206
+ return theme
207
+ if isinstance(theme, str):
208
+ for candidate in _themes._CATALOGUE:
209
+ if theme in {candidate.name, candidate.bundled}:
210
+ return candidate
211
+ if theme.lower().startswith(("http://", "https://")):
212
+ _validate_stylesheet_url(theme)
213
+ return _themes.Theme(name=theme, cdn=theme, bundled="")
214
+ valid_names = ", ".join(candidate.name for candidate in _themes._CATALOGUE)
215
+ raise ValueError(f"unknown theme {theme!r}; valid names: {valid_names}")
216
+ raise TypeError(f"theme must be a Theme or a name/url string, got {type(theme)!r}")
217
+
218
+
219
+ def _stylesheet_url(theme: _themes.Theme, manager: Any) -> str | None:
220
+ if manager is not None and manager.cdn:
221
+ return _validated_theme_cdn(theme)
222
+ if not theme.bundled:
223
+ return _validate_stylesheet_url(theme.cdn)
224
+ return manager.bundled_url(theme) if manager is not None else None
225
+
226
+
227
+ def ensure_theme_bound() -> None:
228
+ """Lazy-bind the controller on first library use inside a page."""
229
+ try:
230
+ store = client_store()
231
+ except Exception:
232
+ return
233
+ if "theme_controller" in store:
234
+ return
235
+ try:
236
+ ThemeController.for_client()
237
+ except Exception:
238
+ return
@@ -0,0 +1,131 @@
1
+ """Selectable stylesheet constants and their pinned CDN twins."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+
7
+ __all__ = [
8
+ "BOOTSTRAP",
9
+ "GRID",
10
+ "CERULEAN",
11
+ "COSMO",
12
+ "CYBORG",
13
+ "DARKLY",
14
+ "FLATLY",
15
+ "JOURNAL",
16
+ "LITERA",
17
+ "LUMEN",
18
+ "LUX",
19
+ "MATERIA",
20
+ "MINTY",
21
+ "MORPH",
22
+ "PULSE",
23
+ "QUARTZ",
24
+ "SANDSTONE",
25
+ "SIMPLEX",
26
+ "SKETCHY",
27
+ "SLATE",
28
+ "SOLAR",
29
+ "SPACELAB",
30
+ "SUPERHERO",
31
+ "UNITED",
32
+ "VAPOR",
33
+ "YETI",
34
+ "ZEPHYR",
35
+ "Theme",
36
+ ]
37
+
38
+ _CDN_ROOT = "https://cdn.jsdelivr.net/npm"
39
+ _BOOTSTRAP_VERSION = "5.3.8"
40
+
41
+
42
+ @dataclass(frozen=True)
43
+ class Theme:
44
+ """One selectable stylesheet: a bundled build plus its pinned CDN twin.
45
+
46
+ ``bundled`` names the package stylesheet pair: ``<bundled>.css`` is the
47
+ scoped build and ``<bundled>-unscoped.css`` applies document-wide.
48
+ """
49
+
50
+ name: str
51
+ cdn: str
52
+ bundled: str
53
+
54
+
55
+ BOOTSTRAP = Theme(
56
+ name="bootstrap",
57
+ cdn=f"{_CDN_ROOT}/bootstrap@{_BOOTSTRAP_VERSION}/dist/css/bootstrap.min.css",
58
+ bundled="ngbs-bootstrap",
59
+ )
60
+
61
+ GRID = Theme(
62
+ name="grid",
63
+ cdn=f"{_CDN_ROOT}/bootstrap@{_BOOTSTRAP_VERSION}/dist/css/bootstrap-grid.min.css",
64
+ bundled="ngbs-grid",
65
+ )
66
+
67
+
68
+ def _bootswatch(name: str) -> Theme:
69
+ return Theme(
70
+ name=name,
71
+ cdn=f"{_CDN_ROOT}/bootswatch@{_BOOTSTRAP_VERSION}/dist/{name}/bootstrap.min.css",
72
+ bundled=f"ngbs-{name}",
73
+ )
74
+
75
+
76
+ CERULEAN = _bootswatch("cerulean")
77
+ COSMO = _bootswatch("cosmo")
78
+ CYBORG = _bootswatch("cyborg")
79
+ DARKLY = _bootswatch("darkly")
80
+ FLATLY = _bootswatch("flatly")
81
+ JOURNAL = _bootswatch("journal")
82
+ LITERA = _bootswatch("litera")
83
+ LUMEN = _bootswatch("lumen")
84
+ LUX = _bootswatch("lux")
85
+ MATERIA = _bootswatch("materia")
86
+ MINTY = _bootswatch("minty")
87
+ MORPH = _bootswatch("morph")
88
+ PULSE = _bootswatch("pulse")
89
+ QUARTZ = _bootswatch("quartz")
90
+ SANDSTONE = _bootswatch("sandstone")
91
+ SIMPLEX = _bootswatch("simplex")
92
+ SKETCHY = _bootswatch("sketchy")
93
+ SLATE = _bootswatch("slate")
94
+ SOLAR = _bootswatch("solar")
95
+ SPACELAB = _bootswatch("spacelab")
96
+ SUPERHERO = _bootswatch("superhero")
97
+ UNITED = _bootswatch("united")
98
+ VAPOR = _bootswatch("vapor")
99
+ YETI = _bootswatch("yeti")
100
+ ZEPHYR = _bootswatch("zephyr")
101
+
102
+
103
+ _CATALOGUE = (
104
+ BOOTSTRAP,
105
+ GRID,
106
+ CERULEAN,
107
+ COSMO,
108
+ CYBORG,
109
+ DARKLY,
110
+ FLATLY,
111
+ JOURNAL,
112
+ LITERA,
113
+ LUMEN,
114
+ LUX,
115
+ MATERIA,
116
+ MINTY,
117
+ MORPH,
118
+ PULSE,
119
+ QUARTZ,
120
+ SANDSTONE,
121
+ SIMPLEX,
122
+ SKETCHY,
123
+ SLATE,
124
+ SOLAR,
125
+ SPACELAB,
126
+ SUPERHERO,
127
+ UNITED,
128
+ VAPOR,
129
+ YETI,
130
+ ZEPHYR,
131
+ )
@@ -0,0 +1,234 @@
1
+ Metadata-Version: 2.5
2
+ Name: nicegui-bootstrap-components
3
+ Version: 0.1.0
4
+ Summary: Bootstrap 5 components for NiceGUI, matching the dash-bootstrap-components API, docs, and examples
5
+ Project-URL: Homepage, https://github.com/raffieeey/nicegui-bootstrap-components
6
+ Project-URL: Documentation, https://raffieeey.github.io/nicegui-bootstrap-components/
7
+ Author: raffieeey
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: bootstrap,components,dashboard,nicegui,ui
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: User Interfaces
19
+ Requires-Python: >=3.10
20
+ Requires-Dist: nicegui<4,>=3.17.1
21
+ Provides-Extra: dbc-ref
22
+ Requires-Dist: dash-bootstrap-components==2.0.4; extra == 'dbc-ref'
23
+ Provides-Extra: dev
24
+ Requires-Dist: mypy>=1.11; extra == 'dev'
25
+ Requires-Dist: pandas-stubs; extra == 'dev'
26
+ Requires-Dist: pillow>=10; extra == 'dev'
27
+ Requires-Dist: playwright>=1.47; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
29
+ Requires-Dist: pytest>=8; extra == 'dev'
30
+ Requires-Dist: ruff>=0.6; extra == 'dev'
31
+ Requires-Dist: selenium>=4.20; extra == 'dev'
32
+ Provides-Extra: examples
33
+ Requires-Dist: pandas; extra == 'examples'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # nicegui-bootstrap-components
37
+
38
+ Bootstrap 5 components for [NiceGUI](https://nicegui.io). The public API matches
39
+ the [dash-bootstrap-components](https://github.com/facultyai/dash-bootstrap-components)
40
+ 2.0.4 API for the component set listed below.
41
+
42
+ [![CI](https://github.com/raffieeey/nicegui-bootstrap-components/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/raffieeey/nicegui-bootstrap-components/actions/workflows/ci.yml)
43
+ [![Python >=3.10](https://img.shields.io/badge/python-%3E%3D3.10-blue)](https://www.python.org/)
44
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
45
+
46
+ ## Install
47
+
48
+ The package requires Python 3.10 or later and NiceGUI >=3.17.1,<4; it has been
49
+ tested against NiceGUI 3.17.1. It bundles Bootstrap 5.3.8. CI checks the oldest
50
+ supported NiceGUI release and the newest available 3.x release.
51
+
52
+ ```bash
53
+ pip install nicegui-bootstrap-components
54
+ ```
55
+
56
+ `Table.from_dataframe` needs pandas, installed by the optional extra:
57
+
58
+ ```bash
59
+ pip install "nicegui-bootstrap-components[examples]"
60
+ ```
61
+
62
+ ## Quickstart
63
+
64
+ Save this as `app.py` and run `python app.py`. Open the local URL printed in the
65
+ terminal.
66
+
67
+ ```python
68
+ from nicegui import ui
69
+ from nicegui_bootstrap_components import bs
70
+
71
+ with bs.scope(), bs.container():
72
+ with bs.row():
73
+ with bs.col(width=12, md=6):
74
+ bs.input(placeholder="Name")
75
+ bs.button("Save", color="primary", on_click=lambda: ui.notify("Saved"))
76
+
77
+ ui.run()
78
+ ```
79
+
80
+ ## Two API surfaces
81
+
82
+ `nicegui_bootstrap_components.bs` is the native surface: 67 PascalCase classes
83
+ and 67 snake_case aliases (134 exports). Use it for new NiceGUI code.
84
+
85
+ `nicegui_bootstrap_components.dbc` is the compatibility surface (66 exports)
86
+ with dash-bootstrap-components 2.0.4 prop names. Use it when porting Dash layouts.
87
+
88
+ ```python
89
+ from nicegui import ui
90
+ from nicegui_bootstrap_components import dbc
91
+
92
+ dbc.Container(
93
+ dbc.Row(dbc.Col(dbc.Button("Save", color="primary"), md=6)),
94
+ fluid=True,
95
+ )
96
+ ```
97
+
98
+ Top-level exports: `StyleMode`, `ThemeController`, `bs`, `dbc`, `icons`, `setup`,
99
+ `themes`, `__version__`.
100
+
101
+ ## Style modes
102
+
103
+ `setup()` registers library CSS once, process-wide, and is idempotent for the same
104
+ arguments. A conflicting mode, or scoped mode with `cdn=True`, raises `ValueError`.
105
+
106
+ - `StyleMode.UNSCOPED` (`"unscoped"`): Bootstrap-first page.
107
+ - `StyleMode.MIXED` (`"mixed"`): scoped Bootstrap islands inside an existing
108
+ NiceGUI/Quasar page. Mark islands with `bs.scope()` (`bs.Scope` is the class form).
109
+
110
+ ```python
111
+ from nicegui_bootstrap_components import StyleMode, setup, themes
112
+
113
+ setup(mode=StyleMode.MIXED, theme=themes.FLATLY, color_mode="auto")
114
+ ```
115
+
116
+ ## Themes and dark mode
117
+
118
+ 27 constants on `themes`: Bootstrap itself, the grid-only build, and 25 Bootswatch
119
+ themes. `color_mode="auto"` follows the host app. Pass `icons.BOOTSTRAP` or
120
+ `icons.FONT_AWESOME` to `setup(icons=...)`.
121
+
122
+ `BOOTSTRAP`, `CERULEAN`, `COSMO`, `CYBORG`, `DARKLY`, `FLATLY`, `GRID`, `JOURNAL`,
123
+ `LITERA`, `LUMEN`, `LUX`, `MATERIA`, `MINTY`, `MORPH`, `PULSE`, `QUARTZ`, `SANDSTONE`,
124
+ `SIMPLEX`, `SKETCHY`, `SLATE`, `SOLAR`, `SPACELAB`, `SUPERHERO`, `UNITED`, `VAPOR`,
125
+ `YETI`, `ZEPHYR`.
126
+
127
+ ## Components
128
+
129
+ 67 native names (each has a snake_case alias on `bs`):
130
+
131
+ Accordion, AccordionItem, Alert, Badge, Breadcrumb, Button, ButtonGroup, Card, CardBody,
132
+ CardFooter, CardGroup, CardHeader, CardImg, CardImgOverlay, CardLink, Carousel, Checkbox,
133
+ Checklist, Col, Collapse, Container, DropdownMenu, DropdownMenuItem, Fade, Form,
134
+ FormFeedback, FormFloating, FormText, Input, InputGroup, InputGroupText, Label, ListGroup,
135
+ ListGroupItem, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle, Nav, NavItem,
136
+ NavLink, Navbar, NavbarBrand, NavbarSimple, NavbarToggler, Offcanvas, Pagination,
137
+ Placeholder, Popover, PopoverBody, PopoverHeader, Progress, RadioButton, RadioItems, Row,
138
+ Scope, Select, Spinner, Stack, Switch, Tab, Table, Tabs, Textarea, Toast, Tooltip.
139
+
140
+ ## Layout
141
+
142
+ ```python
143
+ from nicegui_bootstrap_components import bs
144
+
145
+ with bs.scope(), bs.container():
146
+ with bs.row():
147
+ with bs.col(width=12, md=4):
148
+ bs.label("Full width on phones, one third from md")
149
+ with bs.col(width=12, md=4):
150
+ bs.label("Middle")
151
+ with bs.col(width=12, md=4):
152
+ bs.label("Right")
153
+ ```
154
+
155
+ ## Forms
156
+
157
+ ```python
158
+ from nicegui_bootstrap_components import bs
159
+
160
+ with bs.scope():
161
+ bs.input(placeholder="Your name", on_change=lambda: None)
162
+ bs.select(options={"my": "Malaysia", "sg": "Singapore"}, value="my")
163
+ bs.checkbox(label="Remember me")
164
+ bs.switch(label="Enable alerts")
165
+ ```
166
+
167
+ ## Cards, alerts, and badges
168
+
169
+ ```python
170
+ from nicegui_bootstrap_components import bs
171
+
172
+ with bs.scope(), bs.card():
173
+ bs.card_header("Header")
174
+ with bs.card_body():
175
+ bs.alert("Something happened.", color="info", dismissable=True)
176
+ bs.badge("New", color="success")
177
+ ```
178
+
179
+ ## Modal
180
+
181
+ ```python
182
+ from nicegui_bootstrap_components import bs
183
+
184
+ with bs.scope():
185
+ with bs.modal() as modal:
186
+ bs.modal_header("Title")
187
+ bs.modal_body("Content")
188
+ bs.modal_footer(bs.button("Close", on_click=modal.close))
189
+ ```
190
+
191
+ ## Table
192
+
193
+ ```python
194
+ import pandas as pd
195
+ from nicegui_bootstrap_components import bs
196
+
197
+ df = pd.DataFrame({"city": ["Kuala Lumpur"], "country": ["Malaysia"]})
198
+
199
+ with bs.scope():
200
+ bs.Table.from_dataframe(df, striped=True, bordered=False, hover=True)
201
+ ```
202
+
203
+ ## Examples
204
+
205
+ The gallery mirrors dash-bootstrap-components: Layer 1 component examples, Layer 2
206
+ composite apps (Iris, Graphs in Tabs, Simple Sidebar), and Layer 3 templates.
207
+
208
+ ## Documentation
209
+
210
+ [https://raffieeey.github.io/nicegui-bootstrap-components/](https://raffieeey.github.io/nicegui-bootstrap-components/)
211
+
212
+ ## Development
213
+
214
+ ```bash
215
+ git clone https://github.com/raffieeey/nicegui-bootstrap-components.git
216
+ cd nicegui-bootstrap-components
217
+ pip install -e ".[dev]"
218
+ pytest
219
+ ```
220
+
221
+ `python -m playwright install chromium`. At 0.1.0, the default run carries
222
+ 545 tests plus the Playwright browser suite and the visual matrix; mypy and
223
+ ruff are clean. CI runs three GitHub Actions jobs (checks, docs, browser). The
224
+ browser suite uses real Chromium. Every Python block in the docs and every
225
+ example is executed by tests, so a snippet that cannot run fails CI.
226
+
227
+ ## License
228
+
229
+ MIT. See [LICENSE](LICENSE).
230
+
231
+ ## Status
232
+
233
+ 0.1.0, first public release. Matches the dash-bootstrap-components 2.0.4 API for the
234
+ component set listed above.