Skip to content

Commit b5d5c8c

Browse files
committed
Automatically add appropriate case mods for data-computed and data-on
1 parent 95dc48b commit b5d5c8c

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

sdk/python/src/datastar_py/attributes.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from __future__ import annotations
22

33
import json
4+
import re
45
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
67

78
if TYPE_CHECKING:
89
from typing import Self
@@ -227,6 +228,21 @@ def case(self: TAttr, case: Literal["camel", "kebab", "snake", "pascal"]) -> TAt
227228
self._mods["case"] = [case]
228229
return self
229230

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+
230246

231247
class TimingMod:
232248
def debounce(
@@ -280,7 +296,7 @@ def ifmissing(self) -> Self:
280296
class ComputedAttr(BaseAttr, CaseMod):
281297
def __init__(self, signal_name: str, expression: str):
282298
super().__init__("computed")
283-
self._suffix = signal_name
299+
self._to_kebab_suffix(signal_name)
284300
self._value = expression
285301

286302

@@ -331,7 +347,7 @@ def __init__(self, class_object: dict | str):
331347
class OnAttr(BaseAttr, TimingMod, ViewtransitionMod, CaseMod):
332348
def __init__(self, event: str, expression: str):
333349
super().__init__("on")
334-
self._suffix = event
350+
self._to_kebab_suffix(event)
335351
self._value = expression
336352

337353
@property
@@ -487,3 +503,28 @@ def __init__(self, expression: str):
487503

488504

489505
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

Comments
 (0)