Skip to content

Commit

Permalink
feat: advanced components (#886)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasvinclav authored Dec 3, 2024
1 parent 3f463d2 commit 299d22c
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 13 deletions.
47 changes: 47 additions & 0 deletions src/unfold/components.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import Any, Dict, Optional, Type

from django.http import HttpRequest


class ComponentRegistry:
_registry: Dict[str, Type] = {}

@classmethod
def register_class(cls, component_cls: Type) -> None:
if not issubclass(component_cls, BaseComponent):
raise ValueError(
f"Class '{component_cls.__name__}' must inherit from BaseComponent."
)

class_name = component_cls.__name__

if class_name in cls._registry:
raise ValueError(f"Class '{class_name}' is already registered.")

cls._registry[class_name] = component_cls

@classmethod
def get_class(cls, class_name: str) -> Optional[Type]:
return cls._registry.get(class_name)

@classmethod
def create_instance(cls, class_name: str, **kwargs: Any) -> Any:
component_cls = cls.get_class(class_name)

if component_cls is None:
raise ValueError(f"Class '{class_name}' is not registered.")

return component_cls(**kwargs)


def register_component(cls: Type) -> Type:
ComponentRegistry.register_class(cls)
return cls


class BaseComponent:
def __init__(self, request: HttpRequest):
self.request = request

def get_context_data(self, **kwargs):
return kwargs
2 changes: 1 addition & 1 deletion src/unfold/static/unfold/css/styles.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/unfold/templates/unfold/components/card.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="border flex flex-col flex-grow overflow-hidden p-6 relative rounded-md shadow-sm dark:border-gray-800 {% if class %} {{ class }}{% endif %}">
{% if title %}
<h2 class="border-b font-semibold mb-6 -mt-2 -mx-6 pb-4 px-6 text-font-important-light dark:text-font-important-dark dark:border-gray-800">
<h2 class="bg-gray-50 border-b font-semibold mb-6 -mt-6 -mx-6 py-4 px-6 text-font-important-light dark:text-font-important-dark dark:border-gray-800 dark:bg-white/[.02]">
{{ title }}
</h2>
{% endif %}
Expand Down
51 changes: 51 additions & 0 deletions src/unfold/templates/unfold/components/chart/cohort.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<div class="overflow-auto w-full" data-simplebar>
<table class="w-full">
<thead>
<tr>
<th></th>

{% for header in data.headers %}
<th class="font-normal px-3 pb-2.5 text-left ">
<div class="font-semibold text-font-important-light truncate dark:text-font-important-dark">
{{ header.title }}
</div>

{% if header.subtitle %}
<div class="mt-0.5 text-xs truncate">
{{ header.subtitle }}
</div>
{% endif %}
</th>
{% endfor %}
</tr>
</thead>

<tbody>
{% for row in data.rows %}
<tr class="h-full">
<td>
<div class="pr-3 py-2.5">
<div class="font-semibold text-font-important-light dark:text-font-important-dark">
{{ row.header.title }}
</div>

{% if row.header.subtitle %}
<div class="mt-0.5 text-xs">
{{ row.header.subtitle }}
</div>
{% endif %}
</div>
</td>

{% for col in row.cols %}
<td class="h-full">
<div class="flex flex-col h-full justify-center px-3 py-2.5 rounded-md {% if col.color %}{{ col.color }}{% else %}bg-gray-50 border border-dashed dark:bg-gray-800 dark:border-gray-700{% endif %}">
{{ col.value }}
</div>
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
</div>
5 changes: 5 additions & 0 deletions src/unfold/templates/unfold/components/tracker.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ul class="flex flex-row gap-0.5 overflow-hidden rounded">
{% for item in data %}
<li class="h-8 px-px size-full {% if item.color %}{{ item.color }}{% else %}bg-gray-300 dark:bg-gray-400{% endif %} hover:opacity-50" title="{{ item.tooltip }}"></li>
{% endfor %}
</ul>
2 changes: 1 addition & 1 deletion src/unfold/templates/unfold/layouts/skeleton.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@

{% if colors %}
<style>
html {
:root {
{% for name, weights in colors.items %}
{% for weight, value in weights.items %}
--color-{{ name }}-{{ weight }}: {{ value }};
Expand Down
33 changes: 26 additions & 7 deletions src/unfold/templatetags/unfold.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from django.template.loader import render_to_string
from django.utils.safestring import SafeText

from unfold.components import ComponentRegistry

register = Library()


Expand Down Expand Up @@ -154,24 +156,37 @@ def __init__(
template_name: str,
nodelist: NodeList,
extra_context: Optional[Dict] = None,
include_context: bool = False,
*args,
**kwargs,
):
self.template_name = template_name
self.nodelist = nodelist
self.extra_context = extra_context or {}
self.include_context = include_context
super().__init__(*args, **kwargs)

def render(self, context: RequestContext) -> str:
result = self.nodelist.render(context)
values = {
name: var.resolve(context) for name, var in self.extra_context.items()
}

values.update(
{
"children": self.nodelist.render(context),
}
)

ctx = {name: var.resolve(context) for name, var in self.extra_context.items()}
ctx.update({"children": result})
if "component_class" in values:
values = ComponentRegistry.create_instance(
values["component_class"], request=context.request
).get_context_data(**values)

if self.include_context:
values.update(context.flatten())

return render_to_string(
self.template_name,
request=context.request,
context=ctx,
self.template_name, request=context.request, context=values
)


Expand Down Expand Up @@ -202,17 +217,21 @@ def do_component(parser: Parser, token: Token) -> str:
raise TemplateSyntaxError(
'"with" in {bits[0]} tag needs at least one keyword argument.'
)
elif option == "include_context":
value = True
else:
raise TemplateSyntaxError(f"Unknown argument for {bits[0]} tag: {option}.")

options[option] = value

include_context = options.get("include_context", False)
nodelist = parser.parse(("endcomponent",))
template_name = bits[1][1:-1]

extra_context = options.get("with", {})
parser.next_token()

return RenderComponentNode(template_name, nodelist, extra_context)
return RenderComponentNode(template_name, nodelist, extra_context, include_context)


@register.filter
Expand Down
15 changes: 12 additions & 3 deletions tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,24 @@ module.exports = {
"md:border-r",
"md:w-48",
{
pattern: /bg-primary-+/
pattern: /col-span-+/,
variants: ["md", "lg"],
},
{
pattern: /grid-cols-+/,
variants: ["md", "lg"],
},
{
pattern: /gap-+/,
variants: ["lg"],
variants: ["md", "lg"],
},
{
pattern: /bg-(primary)-(50|100|200|300|400|500|600|700|800|900|950)/,
variants: ["dark"],
},
{
pattern: /w-(1\/2|1\/3|2\/3|1\/4|2\/4|3\/4|1\/5|2\/5|3\/5|4\/5)/,
variants: ["lg"],
variants: ["md", "lg"],
},
],
};

0 comments on commit 299d22c

Please sign in to comment.