pjx-tailwind 0.1.0__tar.gz
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.
- pjx_tailwind-0.1.0/PKG-INFO +6 -0
- pjx_tailwind-0.1.0/pjx_tailwind/__init__.py +3 -0
- pjx_tailwind-0.1.0/pjx_tailwind/cn.py +22 -0
- pjx_tailwind-0.1.0/pjx_tailwind/setup.py +10 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/PKG-INFO +6 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/SOURCES.txt +11 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/dependency_links.txt +1 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/entry_points.txt +2 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/requires.txt +1 -0
- pjx_tailwind-0.1.0/pjx_tailwind.egg-info/top_level.txt +1 -0
- pjx_tailwind-0.1.0/pyproject.toml +16 -0
- pjx_tailwind-0.1.0/setup.cfg +4 -0
- pjx_tailwind-0.1.0/tests/test_cn.py +40 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def cn(*classes: str | bool | None) -> str:
|
|
5
|
+
"""Merge class names, filtering out falsy values.
|
|
6
|
+
|
|
7
|
+
Usage in Jinja2 templates:
|
|
8
|
+
class="{{ cn('base', condition and 'active', other and 'extra') }}"
|
|
9
|
+
|
|
10
|
+
Falsy values (False, None, empty string) are filtered out.
|
|
11
|
+
Remaining strings are joined with spaces and deduplicated.
|
|
12
|
+
"""
|
|
13
|
+
parts: list[str] = []
|
|
14
|
+
seen: set[str] = set()
|
|
15
|
+
for cls in classes:
|
|
16
|
+
if not cls or cls is True:
|
|
17
|
+
continue
|
|
18
|
+
for token in str(cls).split():
|
|
19
|
+
if token and token not in seen:
|
|
20
|
+
seen.add(token)
|
|
21
|
+
parts.append(token)
|
|
22
|
+
return " ".join(parts)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from jinja2 import Environment
|
|
4
|
+
|
|
5
|
+
from pjx_tailwind.cn import cn
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_globals(env: Environment) -> None:
|
|
9
|
+
"""Register pjx-tailwind Jinja2 globals on the environment."""
|
|
10
|
+
env.globals["cn"] = cn # type: ignore[assignment]
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
pjx_tailwind/__init__.py
|
|
3
|
+
pjx_tailwind/cn.py
|
|
4
|
+
pjx_tailwind/setup.py
|
|
5
|
+
pjx_tailwind.egg-info/PKG-INFO
|
|
6
|
+
pjx_tailwind.egg-info/SOURCES.txt
|
|
7
|
+
pjx_tailwind.egg-info/dependency_links.txt
|
|
8
|
+
pjx_tailwind.egg-info/entry_points.txt
|
|
9
|
+
pjx_tailwind.egg-info/requires.txt
|
|
10
|
+
pjx_tailwind.egg-info/top_level.txt
|
|
11
|
+
tests/test_cn.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pjx>=0.2.1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pjx_tailwind
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=75.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pjx-tailwind"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Tailwind CSS utilities for PJX (cn() class merging)"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = ["pjx>=0.2.1"]
|
|
11
|
+
|
|
12
|
+
[project.entry-points."pjx.jinja_globals"]
|
|
13
|
+
cn = "pjx_tailwind.cn:cn"
|
|
14
|
+
|
|
15
|
+
[tool.uv.sources]
|
|
16
|
+
pjx = { workspace = true, editable = true }
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from pjx_tailwind import cn
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_basic_merge():
|
|
5
|
+
assert cn("foo", "bar") == "foo bar"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_filters_false():
|
|
9
|
+
assert cn("base", False, "extra") == "base extra"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_filters_none():
|
|
13
|
+
assert cn("base", None, "extra") == "base extra"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_filters_empty_string():
|
|
17
|
+
assert cn("base", "", "extra") == "base extra"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_conditional_and():
|
|
21
|
+
active = True
|
|
22
|
+
inactive = False
|
|
23
|
+
assert cn("base", active and "selected") == "base selected"
|
|
24
|
+
assert cn("base", inactive and "selected") == "base"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_dedup():
|
|
28
|
+
assert cn("foo bar", "bar baz") == "foo bar baz"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_empty():
|
|
32
|
+
assert cn() == ""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_all_falsy():
|
|
36
|
+
assert cn(False, None, "") == ""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_single():
|
|
40
|
+
assert cn("only") == "only"
|