|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | import json |
| 4 | +import re |
4 | 5 | from collections.abc import Mapping |
5 | | -from typing import Any, Literal, Iterable, TYPE_CHECKING, overload, TypeVar |
| 6 | +from typing import Literal, Iterable, TYPE_CHECKING, overload, TypeVar |
6 | 7 |
|
7 | 8 | if TYPE_CHECKING: |
8 | 9 | from typing import Self |
@@ -227,6 +228,21 @@ def case(self: TAttr, case: Literal["camel", "kebab", "snake", "pascal"]) -> TAt |
227 | 228 | self._mods["case"] = [case] |
228 | 229 | return self |
229 | 230 |
|
| 231 | + def _to_kebab_suffix(self: TAttr, signal_name: str): |
| 232 | + if "-" in signal_name: |
| 233 | + kebab_name, from_case = signal_name.lower(), "kebab" |
| 234 | + elif "_" in signal_name: |
| 235 | + kebab_name, from_case = signal_name.lower().replace("_", "-"), "snake" |
| 236 | + elif signal_name[0].isupper(): |
| 237 | + kebab_name, from_case = re.sub(r"([A-Z])", r"-\1", signal_name).lstrip("-").lower(), "pascal" |
| 238 | + elif signal_name.lower() != signal_name: |
| 239 | + kebab_name, from_case = re.sub(r"([A-Z])", r"-\1", signal_name).lower(), "camel" |
| 240 | + else: |
| 241 | + kebab_name, from_case = signal_name, None |
| 242 | + self._suffix = kebab_name |
| 243 | + if from_case: |
| 244 | + self._mods["case"] = [from_case] |
| 245 | + |
230 | 246 |
|
231 | 247 | class TimingMod: |
232 | 248 | def debounce( |
@@ -280,7 +296,7 @@ def ifmissing(self) -> Self: |
280 | 296 | class ComputedAttr(BaseAttr, CaseMod): |
281 | 297 | def __init__(self, signal_name: str, expression: str): |
282 | 298 | super().__init__("computed") |
283 | | - self._suffix = signal_name |
| 299 | + self._to_kebab_suffix(signal_name) |
284 | 300 | self._value = expression |
285 | 301 |
|
286 | 302 |
|
@@ -331,7 +347,7 @@ def __init__(self, class_object: dict | str): |
331 | 347 | class OnAttr(BaseAttr, TimingMod, ViewtransitionMod, CaseMod): |
332 | 348 | def __init__(self, event: str, expression: str): |
333 | 349 | super().__init__("on") |
334 | | - self._suffix = event |
| 350 | + self._to_kebab_suffix(event) |
335 | 351 | self._value = expression |
336 | 352 |
|
337 | 353 | @property |
@@ -487,3 +503,28 @@ def __init__(self, expression: str): |
487 | 503 |
|
488 | 504 |
|
489 | 505 | data = Attributes() |
| 506 | + |
| 507 | +print(data.persist) |
| 508 | +print(data.persist('mysignal').session) |
| 509 | +print(data.bind('mysignal')) |
| 510 | +print(data.signals({'foo': 'bar'})) |
| 511 | +# If you need expressions you have to fall back to a string |
| 512 | +print(data.signals('{foo: Date.now()}')) |
| 513 | +print(data.computed('foo', '$bar * $baz')) |
| 514 | +print(data.on("load", "console.log('loaded')").delay(100)) |
| 515 | +print(data.on("click", "console.log('clicked')").window.debounce(100)) |
| 516 | +# timings are ms if numbers, but you can use strings |
| 517 | +print(data.on("raf", "console.log('raf')").debounce("1s")) |
| 518 | +# attr and class_ don't use json, so that the values become expressions rather than strings |
| 519 | +print(data.attr({'checked': '$mysignal'})) |
| 520 | +print(data.class_({'coolclass': '$mysignal'})) |
| 521 | +# on and computed are the only ones that allow that need the 'case' modifier |
| 522 | +print(data.computed('FooBar', '$bar * $baz')) |
| 523 | +print(data.computed('fooBar', '$bar * $baz')) |
| 524 | +print(data.computed('foo-bar', '$bar * $baz')) |
| 525 | +print(data.computed('foo_bar', '$bar * $baz')) |
| 526 | +print(data.computed('foobar', '$bar * $baz')) |
| 527 | +# Dunno if it's a good idea, but since some things are properties, and some are methods |
| 528 | +# it always allows calling so you don't have to remember the difference |
| 529 | +print(data.persist.session) |
| 530 | +print(data.persist().session()) |
0 commit comments