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,560 @@
1
+ """Input and Textarea controls with Bootstrap form-control styling."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import contextlib
6
+ from collections.abc import Callable
7
+ from typing import Any
8
+
9
+ from nicegui import ui
10
+
11
+ from .._base import (
12
+ BootstrapValueElement,
13
+ PropConflictError,
14
+ UnsupportedPropError,
15
+ make_surface_classes,
16
+ )
17
+
18
+ _UNSET = object()
19
+
20
+ COMPAT_INPUT_TYPES = frozenset(
21
+ {"text", "number", "password", "email", "range", "search", "tel", "url", "hidden", "time"}
22
+ )
23
+ NATIVE_INPUT_TYPES = COMPAT_INPUT_TYPES | {"color"}
24
+
25
+ _JS_INPUT = """(e) => {
26
+ const root = e.currentTarget || e.target;
27
+ let input = e.target;
28
+ if (!input || (input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA')) {
29
+ input = root && root.querySelector ? root.querySelector('input,textarea') : input;
30
+ }
31
+ if (!input) return;
32
+ if (e.isComposing) return;
33
+ const mode = input.getAttribute('data-ngbs-debounce')
34
+ || (root && root.getAttribute && root.getAttribute('data-ngbs-debounce'))
35
+ || 'false';
36
+ const send = () => emit(input.value == null ? '' : String(input.value));
37
+ if (mode === 'false') { send(); return; }
38
+ if (mode === 'true') { return; }
39
+ const ms = Number(mode);
40
+ if (!Number.isFinite(ms) || ms < 0) { send(); return; }
41
+ if (input._ngbsDebounceTimer) clearTimeout(input._ngbsDebounceTimer);
42
+ input._ngbsDebounceTimer = setTimeout(() => {
43
+ input._ngbsDebounceTimer = null;
44
+ emit(input.value == null ? '' : String(input.value));
45
+ }, ms);
46
+ }"""
47
+
48
+ _JS_BLUR = """(e) => {
49
+ const root = e.currentTarget || e.target;
50
+ let input = e.target;
51
+ if (!input || (input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA')) {
52
+ input = root && root.querySelector ? root.querySelector('input,textarea') : input;
53
+ }
54
+ if (!input) return;
55
+ if (input._ngbsDebounceTimer) {
56
+ clearTimeout(input._ngbsDebounceTimer);
57
+ input._ngbsDebounceTimer = null;
58
+ }
59
+ emit(input.value == null ? '' : String(input.value));
60
+ }"""
61
+
62
+ _JS_ENTER = """(e) => {
63
+ const root = e.currentTarget || e.target;
64
+ let input = e.target;
65
+ if (!input || (input.tagName !== 'INPUT' && input.tagName !== 'TEXTAREA')) {
66
+ input = root && root.querySelector ? root.querySelector('input,textarea') : input;
67
+ }
68
+ if (!input) return;
69
+ if (e.isComposing || e.key !== 'Enter') return;
70
+ if (input.tagName === 'TEXTAREA') return;
71
+ if (input._ngbsDebounceTimer) {
72
+ clearTimeout(input._ngbsDebounceTimer);
73
+ input._ngbsDebounceTimer = null;
74
+ }
75
+ emit(input.value == null ? '' : String(input.value));
76
+ }"""
77
+
78
+
79
+ def _make_surfaces(impl_cls: type) -> tuple[type, type]:
80
+ name = getattr(impl_cls, "component_name", None) or impl_cls.__name__
81
+ if name.startswith("_"):
82
+ name = name[1:]
83
+ if name.endswith("Impl"):
84
+ name = name[:-4]
85
+ attempts = (
86
+ lambda: make_surface_classes(impl_cls, component_name=name),
87
+ lambda: make_surface_classes(impl_cls),
88
+ )
89
+ for attempt in attempts:
90
+ try:
91
+ result = attempt()
92
+ except TypeError:
93
+ continue
94
+ if isinstance(result, (tuple, list)) and len(result) >= 2:
95
+ return result[0], result[1]
96
+
97
+ def _wrap(surface: str, cls_name: str) -> type:
98
+ def __init__(self, *args, **kwargs): # type: ignore[no-untyped-def]
99
+ kwargs.setdefault("_surface", surface)
100
+ impl_cls.__init__(self, *args, **kwargs)
101
+
102
+ return type(
103
+ cls_name,
104
+ (impl_cls,),
105
+ {
106
+ "_surface": surface,
107
+ "component_name": getattr(impl_cls, "component_name", name),
108
+ "__init__": __init__,
109
+ "__module__": impl_cls.__module__,
110
+ },
111
+ )
112
+
113
+ return _wrap("native", name), _wrap("compat", f"Dbc{name}")
114
+
115
+
116
+ def _surface_of(el: Any, explicit: str | None) -> str:
117
+ if explicit in {"native", "compat"}:
118
+ return explicit
119
+ value = getattr(el, "_surface", None) or getattr(type(el), "_surface", None)
120
+ return value if value in {"native", "compat"} else "native"
121
+
122
+
123
+ def _payload(e: Any) -> Any:
124
+ args = getattr(e, "args", e)
125
+ if isinstance(args, dict) and "value" in args:
126
+ return args["value"]
127
+ if isinstance(args, (list, tuple)):
128
+ if not args:
129
+ return ""
130
+ first = args[0]
131
+ if isinstance(first, dict) and "value" in first:
132
+ return first["value"]
133
+ return first
134
+ return args
135
+
136
+
137
+ def _validate_type(surface: str, type_: str) -> str:
138
+ if type_ == "color" and surface == "compat":
139
+ raise UnsupportedPropError("type='color' is a native-only Input extension")
140
+ allowed = NATIVE_INPUT_TYPES if surface == "native" else COMPAT_INPUT_TYPES
141
+ if type_ not in allowed:
142
+ raise UnsupportedPropError(f"Unsupported Input type={type_!r} on {surface} surface")
143
+ return type_
144
+
145
+
146
+ def _validate_debounce(surface: str, debounce: bool | int) -> bool | int:
147
+ if isinstance(debounce, bool):
148
+ return debounce
149
+ if isinstance(debounce, int):
150
+ if surface == "compat":
151
+ raise UnsupportedPropError("integer debounce is a native-only Input/Textarea extension")
152
+ if debounce < 0:
153
+ raise ValueError("debounce milliseconds must be >= 0")
154
+ return int(debounce)
155
+ raise TypeError(f"debounce must be bool or int, got {type(debounce)!r}")
156
+
157
+
158
+ def _debounce_attr(debounce: bool | int) -> str:
159
+ if debounce is False:
160
+ return "false"
161
+ if debounce is True:
162
+ return "true"
163
+ return str(int(debounce))
164
+
165
+
166
+ def _set_attr(el: Any, name: str, value: Any) -> None:
167
+ if value is None:
168
+ return
169
+ el._props[name] = value
170
+
171
+
172
+ class _TextControlBase(BootstrapValueElement):
173
+ """Shared value publishing for Input/Textarea. Root tag must be the native control."""
174
+
175
+ component_name = "Input"
176
+ styling_target = "root"
177
+ reserved_classes: tuple[str, ...] = (
178
+ "form-control",
179
+ "form-control-sm",
180
+ "form-control-lg",
181
+ "form-control-plaintext",
182
+ "form-range",
183
+ "is-valid",
184
+ "is-invalid",
185
+ )
186
+
187
+ def _configure_control(
188
+ self,
189
+ *,
190
+ tag: str,
191
+ type_: str | None,
192
+ placeholder: str | None,
193
+ size: str | None,
194
+ plaintext: bool,
195
+ disabled: bool,
196
+ readonly: bool,
197
+ debounce: bool | int,
198
+ valid: bool | None,
199
+ invalid: bool | None,
200
+ min_value: Any,
201
+ max_value: Any,
202
+ step: Any,
203
+ maxlength: int | None,
204
+ autocomplete: str | None,
205
+ name: str | None,
206
+ list_id: str | None,
207
+ pattern: str | None,
208
+ required: bool,
209
+ html_size: int | None,
210
+ rows: int | None,
211
+ allow_submit: bool,
212
+ n_submit: int,
213
+ n_blur: int,
214
+ on_submit: Callable[..., Any] | None,
215
+ on_blur: Callable[..., Any] | None,
216
+ ) -> None:
217
+ self.n_submit = int(n_submit)
218
+ self.n_blur = int(n_blur)
219
+ self._on_submit_cb = on_submit
220
+ self._on_blur_cb = on_blur
221
+ self._allow_submit = allow_submit
222
+ self._debounce = debounce
223
+
224
+ with contextlib.suppress(Exception):
225
+ self._surface = _surface_of(self, getattr(self, "_surface", None))
226
+
227
+ if tag == "input" and type_:
228
+ _set_attr(self, "type", type_)
229
+ if placeholder is not None:
230
+ _set_attr(self, "placeholder", placeholder)
231
+ if html_size is not None:
232
+ _set_attr(self, "size", int(html_size))
233
+ if rows is not None:
234
+ _set_attr(self, "rows", int(rows))
235
+ if min_value is not None:
236
+ _set_attr(self, "min", min_value)
237
+ if max_value is not None:
238
+ _set_attr(self, "max", max_value)
239
+ if step is not None:
240
+ _set_attr(self, "step", step)
241
+ if maxlength is not None:
242
+ _set_attr(self, "maxlength", int(maxlength))
243
+ if autocomplete is not None:
244
+ _set_attr(self, "autocomplete", autocomplete)
245
+ if name is not None:
246
+ _set_attr(self, "name", name)
247
+ if list_id is not None:
248
+ _set_attr(self, "list", list_id)
249
+ if pattern is not None:
250
+ _set_attr(self, "pattern", pattern)
251
+ if required:
252
+ _set_attr(self, "required", True)
253
+ if disabled:
254
+ _set_attr(self, "disabled", True)
255
+ if hasattr(self, "disable"):
256
+ with contextlib.suppress(Exception):
257
+ self.disable()
258
+ if readonly:
259
+ _set_attr(self, "readonly", True)
260
+
261
+ _set_attr(self, "data-ngbs-debounce", _debounce_attr(debounce))
262
+ _set_attr(self, "data-ngbs-component", self.component_name)
263
+ with contextlib.suppress(Exception):
264
+ _set_attr(self, "data-ngbs-eid", str(self.id))
265
+
266
+ self._apply_form_classes(
267
+ type_=type_, size=size, plaintext=plaintext, valid=valid, invalid=invalid
268
+ )
269
+ self._sync_value_attr(getattr(self, "value", ""))
270
+
271
+ self.on("input", self._on_input, js_handler=_JS_INPUT)
272
+ self.on("compositionend", self._on_input, js_handler=_JS_INPUT)
273
+ self.on("blur", self._on_blur, js_handler=_JS_BLUR)
274
+ if allow_submit:
275
+ self.on("keydown", self._on_enter, js_handler=_JS_ENTER)
276
+
277
+ def _apply_form_classes(
278
+ self,
279
+ *,
280
+ type_: str | None,
281
+ size: str | None,
282
+ plaintext: bool,
283
+ valid: bool | None,
284
+ invalid: bool | None,
285
+ ) -> None:
286
+ if type_ == "hidden":
287
+ return
288
+ classes: list[str] = []
289
+ if type_ == "range":
290
+ classes.append("form-range")
291
+ elif plaintext:
292
+ classes.append("form-control-plaintext")
293
+ else:
294
+ classes.append("form-control")
295
+ if size in {"sm", "lg"} and type_ != "range" and not plaintext:
296
+ classes.append(f"form-control-{size}")
297
+ elif size in {"sm", "lg"} and type_ == "range":
298
+ classes.append(f"form-range-{size}")
299
+ if valid:
300
+ classes.append("is-valid")
301
+ if invalid:
302
+ classes.append("is-invalid")
303
+ if classes:
304
+ self.classes(" ".join(classes))
305
+
306
+ def _sync_value_attr(self, value: Any) -> None:
307
+ text = "" if value is None else str(value)
308
+ self._props["value"] = text
309
+ self._props["model-value"] = text
310
+
311
+ def _publish(self, e: Any) -> None:
312
+ raw = _payload(e)
313
+ if raw is None:
314
+ raw = ""
315
+ try:
316
+ current = self.value
317
+ except Exception:
318
+ current = None
319
+ if current is None:
320
+ current = ""
321
+ if str(raw) == str(current):
322
+ self._sync_value_attr(raw)
323
+ return
324
+ if hasattr(self, "set_value"):
325
+ self.set_value(raw)
326
+ else:
327
+ self.value = raw
328
+ self._sync_value_attr(raw)
329
+
330
+ def _on_input(self, e: Any) -> None:
331
+ self._publish(e)
332
+
333
+ def _on_blur(self, e: Any) -> None:
334
+ self.n_blur = int(getattr(self, "n_blur", 0)) + 1
335
+ self._publish(e)
336
+ if self._on_blur_cb is not None:
337
+ self._on_blur_cb(getattr(self, "value", ""))
338
+
339
+ def _on_enter(self, e: Any) -> None:
340
+ self.n_submit = int(getattr(self, "n_submit", 0)) + 1
341
+ self._publish(e)
342
+ if self._on_submit_cb is not None:
343
+ self._on_submit_cb(getattr(self, "value", ""))
344
+
345
+
346
+ class _InputImpl(_TextControlBase):
347
+ component_name = "Input"
348
+
349
+ def __init__(
350
+ self,
351
+ value: str | float | None = "",
352
+ *,
353
+ _surface: str | None = None,
354
+ type: str = "text",
355
+ placeholder: str | None = None,
356
+ size: str | None = None,
357
+ plaintext: bool = False,
358
+ disabled: bool = False,
359
+ readonly: bool = False,
360
+ read_only: bool | None = None,
361
+ debounce: bool | int = False,
362
+ n_submit: int = 0,
363
+ n_blur: int = 0,
364
+ valid: bool | None = None,
365
+ invalid: bool | None = None,
366
+ min: Any = None,
367
+ max: Any = None,
368
+ step: Any = None,
369
+ maxlength: int | None = None,
370
+ autocomplete: str | None = None,
371
+ name: str | None = None,
372
+ list: str | None = None,
373
+ pattern: str | None = None,
374
+ required: bool = False,
375
+ persist: str = "off",
376
+ on_change: Callable[..., Any] | None = None,
377
+ on_submit: Callable[..., Any] | None = None,
378
+ on_blur: Callable[..., Any] | None = None,
379
+ class_name: str | None = None,
380
+ className: str | None = None,
381
+ style: dict[str, Any] | None = None,
382
+ id: str | None = None,
383
+ html_size: int | None = None,
384
+ key: Any = None,
385
+ children: Any = None,
386
+ ) -> None:
387
+ surface = _surface_of(self, _surface)
388
+ with contextlib.suppress(Exception):
389
+ self._surface = surface
390
+ type_ = _validate_type(surface, type)
391
+ debounce = _validate_debounce(surface, debounce)
392
+ if read_only is not None:
393
+ if surface == "compat":
394
+ raise UnsupportedPropError("read_only is a native alias of readonly")
395
+ readonly = bool(read_only)
396
+ if valid and invalid:
397
+ raise PropConflictError("valid and invalid cannot both be True")
398
+ if persist not in {"off", "local", "session"}:
399
+ raise ValueError("persist must be 'off', 'local', or 'session'")
400
+ if persist != "off" and not id:
401
+ raise ValueError("persist requires a public id")
402
+ if value is None:
403
+ value = ""
404
+ super().__init__(
405
+ children=children,
406
+ value=value,
407
+ on_change=on_change,
408
+ id=id,
409
+ class_name=class_name,
410
+ className=className,
411
+ style=style,
412
+ tag="input",
413
+ key=key,
414
+ )
415
+ self._surface = surface
416
+ self._configure_control(
417
+ tag="input",
418
+ type_=type_,
419
+ placeholder=placeholder,
420
+ size=size,
421
+ plaintext=plaintext,
422
+ disabled=disabled,
423
+ readonly=readonly,
424
+ debounce=debounce,
425
+ valid=valid,
426
+ invalid=invalid,
427
+ min_value=min,
428
+ max_value=max,
429
+ step=step,
430
+ maxlength=maxlength,
431
+ autocomplete=autocomplete,
432
+ name=name,
433
+ list_id=list,
434
+ pattern=pattern,
435
+ required=required,
436
+ html_size=html_size,
437
+ rows=None,
438
+ allow_submit=True,
439
+ n_submit=n_submit,
440
+ n_blur=n_blur,
441
+ on_submit=on_submit,
442
+ on_blur=on_blur,
443
+ )
444
+
445
+
446
+ class _TextareaImpl(_TextControlBase):
447
+ component_name = "Textarea"
448
+ reserved_classes = (
449
+ "form-control",
450
+ "form-control-sm",
451
+ "form-control-lg",
452
+ "form-control-plaintext",
453
+ "is-valid",
454
+ "is-invalid",
455
+ )
456
+
457
+ def __init__(
458
+ self,
459
+ value: str | None = "",
460
+ *,
461
+ _surface: str | None = None,
462
+ placeholder: str | None = None,
463
+ size: str | None = None,
464
+ plaintext: bool = False,
465
+ disabled: bool = False,
466
+ readonly: bool = False,
467
+ read_only: bool | None = None,
468
+ debounce: bool | int = False,
469
+ n_blur: int = 0,
470
+ valid: bool | None = None,
471
+ invalid: bool | None = None,
472
+ maxlength: int | None = None,
473
+ autocomplete: str | None = None,
474
+ name: str | None = None,
475
+ required: bool = False,
476
+ persist: str = "off",
477
+ rows: int | None = None,
478
+ on_change: Callable[..., Any] | None = None,
479
+ on_blur: Callable[..., Any] | None = None,
480
+ class_name: str | None = None,
481
+ className: str | None = None,
482
+ style: dict[str, Any] | None = None,
483
+ id: str | None = None,
484
+ key: Any = None,
485
+ children: Any = None,
486
+ ) -> None:
487
+ surface = _surface_of(self, _surface)
488
+ with contextlib.suppress(Exception):
489
+ self._surface = surface
490
+ debounce = _validate_debounce(surface, debounce)
491
+ if read_only is not None:
492
+ if surface == "compat":
493
+ raise UnsupportedPropError("read_only is a native alias of readonly")
494
+ readonly = bool(read_only)
495
+ if valid and invalid:
496
+ raise PropConflictError("valid and invalid cannot both be True")
497
+ if persist not in {"off", "local", "session"}:
498
+ raise ValueError("persist must be 'off', 'local', or 'session'")
499
+ if persist != "off" and not id:
500
+ raise ValueError("persist requires a public id")
501
+ if value is None:
502
+ value = ""
503
+ super().__init__(
504
+ children=children,
505
+ value=value,
506
+ on_change=on_change,
507
+ id=id,
508
+ class_name=class_name,
509
+ className=className,
510
+ style=style,
511
+ tag="textarea",
512
+ key=key,
513
+ )
514
+ self._surface = surface
515
+ self._configure_control(
516
+ tag="textarea",
517
+ type_=None,
518
+ placeholder=placeholder,
519
+ size=size,
520
+ plaintext=plaintext,
521
+ disabled=disabled,
522
+ readonly=readonly,
523
+ debounce=debounce,
524
+ valid=valid,
525
+ invalid=invalid,
526
+ min_value=None,
527
+ max_value=None,
528
+ step=None,
529
+ maxlength=maxlength,
530
+ autocomplete=autocomplete,
531
+ name=name,
532
+ list_id=None,
533
+ pattern=None,
534
+ required=required,
535
+ html_size=None,
536
+ rows=rows,
537
+ allow_submit=False,
538
+ n_submit=0,
539
+ n_blur=n_blur,
540
+ on_submit=None,
541
+ on_blur=on_blur,
542
+ )
543
+
544
+
545
+ Input, DbcInput = _make_surfaces(_InputImpl)
546
+ Textarea, DbcTextarea = _make_surfaces(_TextareaImpl)
547
+ input = Input
548
+ textarea = Textarea
549
+
550
+ __all__ = [
551
+ "Input",
552
+ "DbcInput",
553
+ "input",
554
+ "Textarea",
555
+ "DbcTextarea",
556
+ "textarea",
557
+ ]
558
+
559
+ # Keep ui imported so NiceGUI page context is resolved the same way as other bs modules.
560
+ _ = ui