evtn/soda

`class` attribute cannot be set

Closed this issue ยท 13 comments

gnbl commented

because it's a Python keyword...

evtn commented

You can use Tag.whatever(..., **{"class": value}) as a workaround
If you have a more elegant proposal, I would be happy to hear it

eolme commented

I was very disappointed when I found out you can't assign a class, my sadness is overwhelming, my mental state is terrible, I can't sleep at night thinking about how sad it makes me to know you can't assign a class. It's unbearably hard, but we're with you and always ready to support you in this difficult time.

eolme commented

But you can choose either:

  • clazz (java style)
  • className (js style)
  • _class (mongodb style)
  • $class (php style)
evtn commented

Also, if you don't mind an extra statement, you can write

tag = Tag.whatever(...)
tag["class"] = value
gnbl commented

Maybe add a named parameter "attributes" that holds a dict (or is a dict view) of the attributes, so attributes = {"class": value} - generic but verbose. Perhaps an unnamed dict parameter (..., {"class": value}, ...) could be used to specify attributes?
JavaScript DOM API tag elements have a classList property. classes = [] or `classes=""`` But I haven't checked whether other keywords are conflicting.

evtn commented

Right now Tag constructor signature is

def __init__(
    self,
    tag_name: str,
    *children: Node,
    self_closing: bool = True,
    **attributes: Value,
):

So there is a parameter called attributes which holds all named arguments (**{"class": value} workaround works on top of that).

I'm unsure about unnamed dictionary parameter (although I considered that) because it would go to children and it would require some additional checks on that parameter.

classes proposal makes sense, but while soda is aimed at SVG, I aim to support arbitrary XML generation and as such I don't want to add some SVG-specific conversions of parameters.

Right now you can use something like this:

from soda import Tag

def set_class(self, *classes):
    return self(**{"class": " ".join(classes)})

Tag.set_class = set_class

tag = Tag.whatever(...).set_class("some_class", "another_class")

You can create a PR with this (or I would maybe add it myself in a few days)

gnbl commented

tag = Tag.whatever(...).set_class("some_class", "another_class")
keeps the one-line instantiation but isn't it HTML/SVG/class-attribute specific? So maybe

def set_attr(self, attribute, *classes):
    return self(**{attribute: " ".join(classes)})

To add to the confusion with another option:

  • expand on the # '_' to '-' conversion with class_= ...
evtn commented

tag = Tag.whatever(...).set_class("some_class", "another_class") keeps the one-line instantiation but isn't it HTML/SVG/class-attribute specific? So maybe

def set_attr(self, attribute, *classes):
    return self(**{attribute: " ".join(classes)})

I think a more appropriate version of set_attr could look like that:

def set_attr(self, attribute: str, value: Value) -> "Tag":
    self[attribute] = value
    return self

To add to the confusion with another option:

* expand on the `# '_' to '-' conversion` with `class_= ...`

Honestly, I got a bit confused by this part, could you please explain?

gnbl commented

You are converting underscores in attribute names into hyphens, e.g. clip_path becomes clip-path.
Similarly, removing a trailing underscore from an attribute name should allow quick typing while avoiding keyword collisions, e.g. class_ becomes class.

evtn commented

I totally forgot about Tag.set_attribute method which already does roughly what proposed set_attr should do

I will consider '_' stripping but for now you can already use

tag = Tag.whatever(...).set_attribute("class", "some_class")

gnbl commented

Also consider namespaces, e.g. Inkscape needs obsolete <use xlink:href=""/>...

evtn commented

I guess it could be done with set_attribute, or with **{"xlink:href": "your_link_here"}