nicegui-bootstrap-components 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nicegui_bootstrap_components/__init__.py +7 -0
- nicegui_bootstrap_components/_base.py +590 -0
- nicegui_bootstrap_components/_host.py +259 -0
- nicegui_bootstrap_components/assets.py +371 -0
- nicegui_bootstrap_components/bs/__init__.py +267 -0
- nicegui_bootstrap_components/bs/_actions.py +232 -0
- nicegui_bootstrap_components/bs/_alert_spinner.py +423 -0
- nicegui_bootstrap_components/bs/_carousel.py +312 -0
- nicegui_bootstrap_components/bs/_choices.py +779 -0
- nicegui_bootstrap_components/bs/_content.py +1343 -0
- nicegui_bootstrap_components/bs/_forms.py +378 -0
- nicegui_bootstrap_components/bs/_inputs.py +560 -0
- nicegui_bootstrap_components/bs/_layout.py +341 -0
- nicegui_bootstrap_components/bs/_nav.py +650 -0
- nicegui_bootstrap_components/bs/_navbar.py +445 -0
- nicegui_bootstrap_components/bs/_overlay_root.py +73 -0
- nicegui_bootstrap_components/bs/_overlays.py +2633 -0
- nicegui_bootstrap_components/bs/_scope.py +28 -0
- nicegui_bootstrap_components/bs/_tabs.py +605 -0
- nicegui_bootstrap_components/bs/_toast.py +137 -0
- nicegui_bootstrap_components/bs/_tooltip_popover.py +537 -0
- nicegui_bootstrap_components/contracts/dbc-2.0.4.json +16811 -0
- nicegui_bootstrap_components/dbc/__init__.py +108 -0
- nicegui_bootstrap_components/icons.py +29 -0
- nicegui_bootstrap_components/persist.py +201 -0
- nicegui_bootstrap_components/py.typed +0 -0
- nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff +0 -0
- nicegui_bootstrap_components/static/dist/fonts/bootstrap-icons.woff2 +0 -0
- nicegui_bootstrap_components/static/dist/ngbs-bootstrap-unscoped.css +12047 -0
- nicegui_bootstrap_components/static/dist/ngbs-bootstrap.css +12057 -0
- nicegui_bootstrap_components/static/dist/ngbs-cerulean-unscoped.css +12338 -0
- nicegui_bootstrap_components/static/dist/ngbs-cerulean.css +12348 -0
- nicegui_bootstrap_components/static/dist/ngbs-compat-unscoped.css +12 -0
- nicegui_bootstrap_components/static/dist/ngbs-compat.css +12 -0
- nicegui_bootstrap_components/static/dist/ngbs-cosmo-unscoped.css +11825 -0
- nicegui_bootstrap_components/static/dist/ngbs-cosmo.css +11835 -0
- nicegui_bootstrap_components/static/dist/ngbs-cyborg-unscoped.css +12213 -0
- nicegui_bootstrap_components/static/dist/ngbs-cyborg.css +12223 -0
- nicegui_bootstrap_components/static/dist/ngbs-darkly-unscoped.css +12143 -0
- nicegui_bootstrap_components/static/dist/ngbs-darkly.css +12153 -0
- nicegui_bootstrap_components/static/dist/ngbs-flatly-unscoped.css +12129 -0
- nicegui_bootstrap_components/static/dist/ngbs-flatly.css +12139 -0
- nicegui_bootstrap_components/static/dist/ngbs-grid-unscoped.css +4084 -0
- nicegui_bootstrap_components/static/dist/ngbs-grid.css +4094 -0
- nicegui_bootstrap_components/static/dist/ngbs-host-unscoped.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-host.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-icons-bootstrap.css +5 -0
- nicegui_bootstrap_components/static/dist/ngbs-icons-fontawesome.css +9 -0
- nicegui_bootstrap_components/static/dist/ngbs-journal-unscoped.css +12112 -0
- nicegui_bootstrap_components/static/dist/ngbs-journal.css +12122 -0
- nicegui_bootstrap_components/static/dist/ngbs-litera-unscoped.css +12214 -0
- nicegui_bootstrap_components/static/dist/ngbs-litera.css +12224 -0
- nicegui_bootstrap_components/static/dist/ngbs-lumen-unscoped.css +12377 -0
- nicegui_bootstrap_components/static/dist/ngbs-lumen.css +12387 -0
- nicegui_bootstrap_components/static/dist/ngbs-lux-unscoped.css +12048 -0
- nicegui_bootstrap_components/static/dist/ngbs-lux.css +12058 -0
- nicegui_bootstrap_components/static/dist/ngbs-materia-unscoped.css +13197 -0
- nicegui_bootstrap_components/static/dist/ngbs-materia.css +13207 -0
- nicegui_bootstrap_components/static/dist/ngbs-minty-unscoped.css +12184 -0
- nicegui_bootstrap_components/static/dist/ngbs-minty.css +12194 -0
- nicegui_bootstrap_components/static/dist/ngbs-morph-unscoped.css +12829 -0
- nicegui_bootstrap_components/static/dist/ngbs-morph.css +12839 -0
- nicegui_bootstrap_components/static/dist/ngbs-pulse-unscoped.css +11901 -0
- nicegui_bootstrap_components/static/dist/ngbs-pulse.css +11911 -0
- nicegui_bootstrap_components/static/dist/ngbs-quartz-unscoped.css +12625 -0
- nicegui_bootstrap_components/static/dist/ngbs-quartz.css +12635 -0
- nicegui_bootstrap_components/static/dist/ngbs-sandstone-unscoped.css +12214 -0
- nicegui_bootstrap_components/static/dist/ngbs-sandstone.css +12224 -0
- nicegui_bootstrap_components/static/dist/ngbs-simplex-unscoped.css +12199 -0
- nicegui_bootstrap_components/static/dist/ngbs-simplex.css +12209 -0
- nicegui_bootstrap_components/static/dist/ngbs-sketchy-unscoped.css +12518 -0
- nicegui_bootstrap_components/static/dist/ngbs-sketchy.css +12528 -0
- nicegui_bootstrap_components/static/dist/ngbs-slate-unscoped.css +12527 -0
- nicegui_bootstrap_components/static/dist/ngbs-slate.css +12537 -0
- nicegui_bootstrap_components/static/dist/ngbs-solar-unscoped.css +12152 -0
- nicegui_bootstrap_components/static/dist/ngbs-solar.css +12162 -0
- nicegui_bootstrap_components/static/dist/ngbs-spacelab-unscoped.css +12279 -0
- nicegui_bootstrap_components/static/dist/ngbs-spacelab.css +12289 -0
- nicegui_bootstrap_components/static/dist/ngbs-superhero-unscoped.css +12223 -0
- nicegui_bootstrap_components/static/dist/ngbs-superhero.css +12233 -0
- nicegui_bootstrap_components/static/dist/ngbs-united-unscoped.css +12090 -0
- nicegui_bootstrap_components/static/dist/ngbs-united.css +12100 -0
- nicegui_bootstrap_components/static/dist/ngbs-vapor-unscoped.css +12556 -0
- nicegui_bootstrap_components/static/dist/ngbs-vapor.css +12566 -0
- nicegui_bootstrap_components/static/dist/ngbs-yeti-unscoped.css +12333 -0
- nicegui_bootstrap_components/static/dist/ngbs-yeti.css +12343 -0
- nicegui_bootstrap_components/static/dist/ngbs-zephyr-unscoped.css +12296 -0
- nicegui_bootstrap_components/static/dist/ngbs-zephyr.css +12306 -0
- nicegui_bootstrap_components/static/prototype-ngbs.css +541 -0
- nicegui_bootstrap_components/static/webfonts/fa-brands-400.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-brands-400.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-regular-400.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-regular-400.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-solid-900.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-solid-900.woff2 +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.ttf +0 -0
- nicegui_bootstrap_components/static/webfonts/fa-v4compatibility.woff2 +0 -0
- nicegui_bootstrap_components/theme.py +238 -0
- nicegui_bootstrap_components/themes.py +131 -0
- nicegui_bootstrap_components-0.1.0.dist-info/METADATA +234 -0
- nicegui_bootstrap_components-0.1.0.dist-info/RECORD +103 -0
- nicegui_bootstrap_components-0.1.0.dist-info/WHEEL +4 -0
- nicegui_bootstrap_components-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,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
|