From 8a83f24a0bcb88b4c818c33cdd53ca9fb4b03f61 Mon Sep 17 00:00:00 2001 From: Sascha Krug Date: Sun, 7 Jan 2024 00:21:42 +0100 Subject: [PATCH] [Feature] Add assets support --- CHANGELOG.md | 1 + README.md | 33 +++++++++++++++++++ django_component_kit/__init__.py | 6 ++-- django_component_kit/assets.py | 25 ++++++++++++++ django_component_kit/decorators.py | 11 +++++-- django_component_kit/nodes.py | 9 +++++ django_component_kit/tags.py | 7 +++- .../templatetags/components.py | 4 +-- pyproject.toml | 2 +- 9 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 django_component_kit/assets.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 68f0e9e..88ae271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Changelog ## [0.3.0] - 2024-01-09 +- Add assets support - Add 'is_partial' attribute when rendering partials - Add support for strings with ' enclosures - Add PartialResponse class to render partials in views diff --git a/README.md b/README.md index 13933a6..898e414 100644 --- a/README.md +++ b/README.md @@ -689,6 +689,39 @@ def toggle_color_action() -> dict: ``` +# Assets +*Since: 0.3.0* + +When registering a component, you can assign JS oder CSS assets to it. You can do this using the `js` or `css` arguments +in the `component_inline_tag` or `component_block_tag`. They will be ordered in the order they are registered, +CSS in front of JS. Duplicates are removed. You can then use the assets via the `assets` tag. + +*Hint:* If you want to modify the assets, you can import the `assets` object and modify it before usage. + +## Usage +```python +# templatetags/mycomponents.py +from django import template +from django.template.loader import get_template +from django_component_kit import component_inline_tag + +register = template.Library() + +@register.tag +@component_inline_tag(get_template("mycomponents/card.html"), js=["myjs.js"]) +def card() -> dict: + return dict() +``` + +```html + +{% assets %} +``` + +### Result +```html + +``` # Contribution diff --git a/django_component_kit/__init__.py b/django_component_kit/__init__.py index 6c3be2c..c1f77b2 100644 --- a/django_component_kit/__init__.py +++ b/django_component_kit/__init__.py @@ -1,13 +1,15 @@ -from .tags import do_merge_attrs, do_slot, do_render_slot, do_partial +from .tags import do_merge_attrs, do_slot, do_render_slot, do_partial, do_assets from .decorators import component_inline_tag, component_block_tag -from .partials import render_partial_from_template +from .partials import render_partial_from_template, PartialResponse __all__ = [ "do_merge_attrs", "do_slot", "do_render_slot", + "do_assets", "component_inline_tag", "component_block_tag", "do_partial", "render_partial_from_template", + "PartialResponse", ] diff --git a/django_component_kit/assets.py b/django_component_kit/assets.py new file mode 100644 index 0000000..17b02fe --- /dev/null +++ b/django_component_kit/assets.py @@ -0,0 +1,25 @@ +""" +Assets management for Django Component Kit. +""" +from typing import Iterable + + +class AssetRegistry: + """Registry for assets""" + + def __init__(self): + self.js = set() + self.css = set() + + def add_js(self, js_list: Iterable[str] | None): + """Add JS files""" + js_list = js_list or [] + self.js.update([f'' for js in js_list]) + + def add_css(self, css_list: Iterable[str] | None): + """Add CSS files""" + css_list = css_list or [] + self.css.update([f'' for css in css_list]) + + +assets = AssetRegistry() diff --git a/django_component_kit/decorators.py b/django_component_kit/decorators.py index 0dbd745..c0cb629 100644 --- a/django_component_kit/decorators.py +++ b/django_component_kit/decorators.py @@ -4,6 +4,7 @@ from django.template import NodeList, TemplateSyntaxError from django.template.base import Parser, Token, Template +from django_component_kit.assets import assets from django_component_kit.attributes import split_attributes from django_component_kit.nodes import ComponentNode, SlotNodeList, SlotNode, INNER_SLOT_NAME from django_component_kit.utils import token_kwargs @@ -49,9 +50,12 @@ def _parse_bits(bits: list, parser: Parser, nodelist: NodeList) -> tuple: return slots, attrs -def component_inline_tag(template: Template, partial: str = None) -> callable: +def component_inline_tag(template: Template, partial: str = None, js: list = None, css: list = None) -> callable: """Decorator for creating an inline component tag.""" + assets.add_js(js) + assets.add_css(css) + def dec(func): def do_component(parser: Parser, token: Token) -> ComponentNode: """Creates a ComponentNode for the inline component tag.""" @@ -66,9 +70,12 @@ def do_component(parser: Parser, token: Token) -> ComponentNode: return dec -def component_block_tag(template: Template, partial: str = None) -> callable: +def component_block_tag(template: Template, partial: str = None, js: list = None, css: list = None) -> callable: """Decorator for creating a block component tag.""" + assets.add_js(js) + assets.add_css(css) + def dec(func): def do_component(parser: Parser, token: Token) -> ComponentNode: """Creates a ComponentNode for the block component tag.""" diff --git a/django_component_kit/nodes.py b/django_component_kit/nodes.py index 27e8774..1aacedf 100644 --- a/django_component_kit/nodes.py +++ b/django_component_kit/nodes.py @@ -10,6 +10,7 @@ from django.template.base import FilterExpression, Node, NodeList from django.utils.safestring import SafeString +from django_component_kit.assets import assets from django_component_kit.attributes import AttributeBag, attributes_to_string, merge_attributes, append_attributes from django_component_kit.partials import render_partial_from_template @@ -172,3 +173,11 @@ def render(self, context: Context) -> str: attrs = merge_attributes(default_attrs, bound_attributes) attrs = append_attributes(attrs, append_attrs) return attributes_to_string(attrs) + + +class AssetsNode(Node): + """Represents an asset node in the template.""" + + def render(self, _): + """Renders all assets.""" + return "\n".join(assets.js.union(assets.css)) diff --git a/django_component_kit/tags.py b/django_component_kit/tags.py index cc29f19..b663d1d 100644 --- a/django_component_kit/tags.py +++ b/django_component_kit/tags.py @@ -5,7 +5,7 @@ from django.template.base import Parser, Token from django_component_kit.attributes import split_attributes -from django_component_kit.nodes import MergeAttrsNode, RenderSlotNode, SlotNode +from django_component_kit.nodes import MergeAttrsNode, RenderSlotNode, SlotNode, AssetsNode from django_component_kit.partials import PartialNode from django_component_kit.utils import attribute_re, token_kwargs @@ -117,3 +117,8 @@ def do_partial(parser: Parser, token: Token) -> PartialNode: nodelist = parser.parse(("endpartial",)) parser.delete_first_token() return PartialNode(partial_name, inline, nodelist) + + +def do_assets(*_) -> AssetsNode: + """Add JS and CSS assets.""" + return AssetsNode() diff --git a/django_component_kit/templatetags/components.py b/django_component_kit/templatetags/components.py index 4b90335..d4d3c6a 100644 --- a/django_component_kit/templatetags/components.py +++ b/django_component_kit/templatetags/components.py @@ -3,11 +3,11 @@ """ from django import template -from django_component_kit.tags import do_merge_attrs, do_render_slot, do_slot, do_partial - +from django_component_kit.tags import do_merge_attrs, do_render_slot, do_slot, do_partial, do_assets register = template.Library() register.tag("merge_attrs", do_merge_attrs) register.tag("render_slot", do_render_slot) register.tag("slot", do_slot) register.tag("partial", do_partial) +register.tag("assets", do_assets) diff --git a/pyproject.toml b/pyproject.toml index 352cbd3..9083594 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "django-component-kit" -version = "0.2.1" +version = "0.3.0" description = "A lightweight library for flexible Django component building, following Django's principles." authors = ["Sascha Krug"] license = "MIT"