removed requirement for .cotton.html suffix

This commit is contained in:
Will Abbott 2024-07-03 11:35:21 +01:00
parent 2ecfb4d9a3
commit 553fd9d87f
94 changed files with 110 additions and 119 deletions

View file

@ -23,7 +23,7 @@ Cotton aims to overcome certain limitations that exist in the django template sy
### Your first component ### Your first component
```html ```html
<!-- button.cotton.html --> <!-- cotton/button.html -->
<a href="/" class="...">{{ slot }}</a> <a href="/" class="...">{{ slot }}</a>
``` ```
```html ```html
@ -38,7 +38,7 @@ Cotton aims to overcome certain limitations that exist in the django template sy
### Add attributes ### Add attributes
```html ```html
<!-- button.cotton.html --> <!-- cotton/button.html -->
<a href="{{ url }}" class="..."> <a href="{{ url }}" class="...">
{{ slot }} {{ slot }}
</a> </a>
@ -59,7 +59,7 @@ Cotton aims to overcome certain limitations that exist in the django template sy
Named slots are a powerful concept. It allows us to provide HTML to appear in one or more areas in the component. Here we allow the button to optionally display an icon: Named slots are a powerful concept. It allows us to provide HTML to appear in one or more areas in the component. Here we allow the button to optionally display an icon:
```html ```html
<!-- button.cotton.html --> <!-- cotton/button.html -->
<a href="{{ url }}" class="..."> <a href="{{ url }}" class="...">
{{ slot }} {{ slot }}
@ -116,7 +116,7 @@ To pass a template variable you prepend the attribute name with a colon `:`. Con
That has a component definition like: That has a component definition like:
```html ```html
<!-- bio_card.cotton.html --> <!-- cotton/bio_card.html -->
<div class="..."> <div class="...">
<img src="{{ user.avatar }}" alt="..."> <img src="{{ user.avatar }}" alt="...">
{{ user.username }} {{ user.country_code }} {{ user.username }} {{ user.country_code }}
@ -134,7 +134,7 @@ Boolean attributes reduce boilerplate when we just want to indicate a certain at
By passing just the attribute name without a value, it will automatically be provided to the component as `True` By passing just the attribute name without a value, it will automatically be provided to the component as `True`
```html ```html
<!-- button.cotton.html --> <!-- cotton/button.html -->
<a href="{{ url }}" {% if external %} target="_blank" {% endif %} class="..."> <a href="{{ url }}" {% if external %} target="_blank" {% endif %} class="...">
{{ slot }} {{ slot }}
</a> </a>
@ -152,7 +152,7 @@ Using the ':' to prefix an attribute tells Cotton we're passing a dynamic type d
This benefits a number of use-cases, for example if you have a select component that you want to provide the possible options from the parent: This benefits a number of use-cases, for example if you have a select component that you want to provide the possible options from the parent:
```html ```html
<!-- select.cotton.html --> <!-- cotton/select.html -->
<select {{ attrs }}> <select {{ attrs }}>
{% for option in options %} {% for option in options %}
<option value="{{ option }}">{{ option }}</option> <option value="{{ option }}">{{ option }}</option>
@ -165,7 +165,7 @@ This benefits a number of use-cases, for example if you have a select component
``` ```
```html ```html
<!-- output --> <!-- cotton/output -->
<select name="q1"> <select name="q1">
<option value="yes">yes</option> <option value="yes">yes</option>
<option value="no">no</option> <option value="no">no</option>
@ -178,7 +178,7 @@ This benefits a number of use-cases, for example if you have a select component
Django templates adhere quite strictly to the MVC model and does not permit a lot of data manipulation in the View. Fair enough, but what if we want to handle data for the purpose of UI state only? Having this in the back would surely convolute the backend code. For this, Cotton can set simple attribute values that help allow us to set default values for our component attributes. Django templates adhere quite strictly to the MVC model and does not permit a lot of data manipulation in the View. Fair enough, but what if we want to handle data for the purpose of UI state only? Having this in the back would surely convolute the backend code. For this, Cotton can set simple attribute values that help allow us to set default values for our component attributes.
```html ```html
<!-- button.cotton.html --> <!-- cotton/button.html -->
<c-vars theme="bg-purple-500" /> <c-vars theme="bg-purple-500" />
<a href="..." class="{{ theme }}"> <a href="..." class="{{ theme }}">
@ -214,7 +214,7 @@ Now we have a default theme for our button, but it is overridable:
`{{ attrs }}` is a special variable that contains all the attributes passed to the component in an key="value" format. This is useful when you want to pass all attributes to a child element. For example, you have inputs that can have any number of attributes defined: `{{ attrs }}` is a special variable that contains all the attributes passed to the component in an key="value" format. This is useful when you want to pass all attributes to a child element. For example, you have inputs that can have any number of attributes defined:
```html ```html
<!-- input.cotton.html --> <!-- cotton/input.html -->
<input type="text" class="..." {{ attrs }} /> <input type="text" class="..." {{ attrs }} />
``` ```
@ -228,7 +228,7 @@ Now we have a default theme for our button, but it is overridable:
If you combine this with the `c-vars` tag, any property defined there will be excluded from `{{ attrs }}`. For example: If you combine this with the `c-vars` tag, any property defined there will be excluded from `{{ attrs }}`. For example:
```html ```html
<!-- input.cotton.html --> <!-- cotton/input.html -->
<c-vars type="text" /> <c-vars type="text" />
<input {{ attrs }} class="..." /> <input {{ attrs }} class="..." />
@ -245,7 +245,7 @@ If you combine this with the `c-vars` tag, any property defined there will be ex
Cotton helps carve out re-usable components, here we show how to make a re-usable form, reducing code repetition and improving maintainability: Cotton helps carve out re-usable components, here we show how to make a re-usable form, reducing code repetition and improving maintainability:
```html ```html
<!-- form.cotton.html --> <!-- cotton/form.html -->
<div id="result" class="..."></div> <div id="result" class="..."></div>
<form {{ attrs }} hx-target="#result" hx-swap="outerHTML"> <form {{ attrs }} hx-target="#result" hx-swap="outerHTML">
@ -269,10 +269,9 @@ Cotton helps carve out re-usable components, here we show how to make a re-usabl
``` ```
## Usage Basics ## Usage Basics
- **File Extensions:** Views templates that contain Cotton and Cotton components themselves should use the `.cotton.html` extension.
- **Component Placement:** Components should be placed in the `templates/cotton` folder. - **Component Placement:** Components should be placed in the `templates/cotton` folder.
- **Naming Conventions:** - **Naming Conventions:**
- Component filenames use snake_case: `my_component.cotton.html` - Component filenames use snake_case: `my_component.html`
- Components are called using kebab-case: `<c-my-component />` - Components are called using kebab-case: `<c-my-component />`
For full docs and demos, checkout <a href="https://django-cotton.com" target="_blank">django-cotton.com</a> For full docs and demos, checkout <a href="https://django-cotton.com" target="_blank">django-cotton.com</a>

View file

@ -1,4 +1,4 @@
{% cotton_component cotton/benchmarks/partials/main.cotton.html test_key %} {% cotton_component cotton/benchmarks/partials/main.html test_key %}
I'm default I'm default
{% cotton_slot top test_key %} {% cotton_slot top test_key %}
I'm top I'm top

View file

@ -62,7 +62,7 @@ time_compiled_cotton, output_compiled_cotton = benchmark_template_rendering(
"cotton/benchmarks/cotton_compiled.html" "cotton/benchmarks/cotton_compiled.html"
) )
time_cotton, output_cotton = benchmark_template_rendering( time_cotton, output_cotton = benchmark_template_rendering(
"cotton/benchmarks/cotton.cotton.html" "cotton/benchmarks/cotton.html"
) )
# Output results # Output results

View file

@ -67,20 +67,19 @@ class Loader(BaseLoader):
"""Return an Origin object pointing to an absolute path in each directory """Return an Origin object pointing to an absolute path in each directory
in template_dirs. For security reasons, if a path doesn't lie inside in template_dirs. For security reasons, if a path doesn't lie inside
one of the template_dirs it is excluded from the result set.""" one of the template_dirs it is excluded from the result set."""
if template_name.endswith(".cotton.html"): for template_dir in self.get_dirs():
for template_dir in self.get_dirs(): try:
try: name = safe_join(template_dir, template_name)
name = safe_join(template_dir, template_name) except SuspiciousFileOperation:
except SuspiciousFileOperation: # The joined path was located outside of this template_dir
# The joined path was located outside of this template_dir # (it might be inside another one, so this isn't fatal).
# (it might be inside another one, so this isn't fatal). continue
continue
yield Origin( yield Origin(
name=name, name=name,
template_name=template_name, template_name=template_name,
loader=self, loader=self,
) )
class UnsortedAttributes(HTMLFormatter): class UnsortedAttributes(HTMLFormatter):
@ -224,7 +223,7 @@ class CottonTemplateProcessor:
component_key = tag.name[2:] component_key = tag.name[2:]
component_path = component_key.replace(".", "/").replace("-", "_") component_path = component_key.replace(".", "/").replace("-", "_")
opening_tag = f"{{% cotton_component {'cotton/{}.cotton.html'.format(component_path)} {component_key} " opening_tag = f"{{% cotton_component {'cotton/{}.html'.format(component_path)} {component_key} "
# Store attributes that contain template expressions, they are when we use '{{' or '{%' in the value of an attribute # Store attributes that contain template expressions, they are when we use '{{' or '{%' in the value of an attribute
expression_attrs = [] expression_attrs = []
@ -291,6 +290,7 @@ class CottonTemplateCacheHandler:
def get_cached_template(self, cache_key): def get_cached_template(self, cache_key):
if not self.enabled: if not self.enabled:
return None return None
return cache.get(cache_key) return cache.get(cache_key)
def cache_template(self, cache_key, content, timeout=None): def cache_template(self, cache_key, content, timeout=None):

View file

@ -8,7 +8,7 @@ class InlineTestCase(CottonInlineTestCase):
def test_parent_component_is_rendered(self): def test_parent_component_is_rendered(self):
# Create component # Create component
self.create_template( self.create_template(
"cotton/parent.cotton.html", "cotton/parent.html",
""" """
<div class="i-am-parent"> <div class="i-am-parent">
{{ slot }} {{ slot }}
@ -18,7 +18,7 @@ class InlineTestCase(CottonInlineTestCase):
# Create view template # Create view template
self.create_template( self.create_template(
"parent_view.cotton.html", "parent_view.html",
""" """
<c-parent> <c-parent>
Hello, World! Hello, World!
@ -27,7 +27,7 @@ class InlineTestCase(CottonInlineTestCase):
) )
# Create URL # Create URL
self.create_url("parent/", self.create_view("parent_view.cotton.html")) self.create_url("parent/", self.create_view("parent_view.html"))
# Override URLconf # Override URLconf
with self.settings(ROOT_URLCONF=self.get_url_conf()): with self.settings(ROOT_URLCONF=self.get_url_conf()):

View file

@ -6,7 +6,7 @@ app_name = "django_cotton"
class NamedSlotInLoop(TemplateView): class NamedSlotInLoop(TemplateView):
template_name = "named_slot_in_loop.cotton.html" template_name = "named_slot_in_loop.html"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
return { return {
@ -19,15 +19,15 @@ class NamedSlotInLoop(TemplateView):
urlpatterns = [ urlpatterns = [
path("parent", TemplateView.as_view(template_name="parent_test.cotton.html")), path("parent", TemplateView.as_view(template_name="parent_test.html")),
path("child", TemplateView.as_view(template_name="child_test.cotton.html")), path("child", TemplateView.as_view(template_name="child_test.html")),
path( path(
"self-closing", "self-closing",
TemplateView.as_view(template_name="self_closing_test.cotton.html"), TemplateView.as_view(template_name="self_closing_test.html"),
), ),
path("include", TemplateView.as_view(template_name="cotton_include.cotton.html")), path("include", TemplateView.as_view(template_name="cotton_include.html")),
path("playground", TemplateView.as_view(template_name="playground.cotton.html")), path("playground", TemplateView.as_view(template_name="playground.html")),
path("tag", TemplateView.as_view(template_name="tag.cotton.html")), path("tag", TemplateView.as_view(template_name="tag.html")),
path("named-slot-in-loop", NamedSlotInLoop.as_view()), path("named-slot-in-loop", NamedSlotInLoop.as_view()),
path("test/compiled-cotton", views.compiled_cotton_test_view), path("test/compiled-cotton", views.compiled_cotton_test_view),
path("test/cotton", views.cotton_test_view), path("test/cotton", views.cotton_test_view),
@ -39,16 +39,14 @@ urlpatterns = [
path("django-syntax-decoding", views.django_syntax_decoding_test_view), path("django-syntax-decoding", views.django_syntax_decoding_test_view),
path( path(
"string-with-spaces", "string-with-spaces",
TemplateView.as_view(template_name="string_with_spaces.cotton.html"), TemplateView.as_view(template_name="string_with_spaces.html"),
), ),
path("vars-test", TemplateView.as_view(template_name="vars_test.cotton.html")), path("vars-test", TemplateView.as_view(template_name="vars_test.html")),
path("variable-parsing", views.variable_parsing_test_view), path("variable-parsing", views.variable_parsing_test_view),
path("test/eval-vars", views.eval_vars_test_view), path("test/eval-vars", views.eval_vars_test_view),
path("test/eval-attributes", views.eval_attributes_test_view), path("test/eval-attributes", views.eval_attributes_test_view),
path( path(
"test/native-tags-in-attributes", "test/native-tags-in-attributes",
TemplateView.as_view( TemplateView.as_view(template_name="native_tags_in_attributes_view.html"),
template_name="native_tags_in_attributes_view.cotton.html"
),
), ),
] ]

View file

@ -8,7 +8,7 @@ def compiled_cotton_test_view(request):
def cotton_test_view(request): def cotton_test_view(request):
return render(request, "cotton_test.cotton.html") return render(request, "cotton_test.html")
def native_extends_test_view(request): def native_extends_test_view(request):
@ -23,30 +23,28 @@ def native_include_test_view(request):
def attribute_merging_test_view(request): def attribute_merging_test_view(request):
return render(request, "attribute_merging_test.cotton.html") return render(request, "attribute_merging_test.html")
def attribute_passing_test_view(request): def attribute_passing_test_view(request):
return render(request, "attribute_passing_test.cotton.html") return render(request, "attribute_passing_test.html")
def django_syntax_decoding_test_view(request): def django_syntax_decoding_test_view(request):
return render(request, "django_syntax_decoding_test.cotton.html") return render(request, "django_syntax_decoding_test.html")
def variable_parsing_test_view(request): def variable_parsing_test_view(request):
return render( return render(request, "variable_parsing_test.html", {"variable": "some-class"})
request, "variable_parsing_test.cotton.html", {"variable": "some-class"}
)
def valueless_attributes_test_view(request): def valueless_attributes_test_view(request):
return render(request, "valueless_attributes_test_view.cotton.html") return render(request, "valueless_attributes_test_view.html")
def eval_vars_test_view(request): def eval_vars_test_view(request):
return render(request, "eval_vars_test_view.cotton.html") return render(request, "eval_vars_test_view.html")
def eval_attributes_test_view(request): def eval_attributes_test_view(request):
return render(request, "eval_attributes_test_view.cotton.html") return render(request, "eval_attributes_test_view.html")

View file

@ -7,8 +7,8 @@
<c-index-sublink><a href="#preparation" class="no-underline">Preparation</a></c-index-sublink> <c-index-sublink><a href="#preparation" class="no-underline">Preparation</a></c-index-sublink>
<c-index-link><a href="#interactivity" class="no-underline">Interactivity</a></c-index-link> <c-index-link><a href="#interactivity" class="no-underline">Interactivity</a></c-index-link>
<c-index-sublink><a href="#data-component" class="no-underline">Data component</a></c-index-sublink> <c-index-sublink><a href="#data-component" class="no-underline">Data component</a></c-index-sublink>
<c-index-sublink><a href="#modify-tabs" class="no-underline">tabs.cotton.html</a></c-index-sublink> <c-index-sublink><a href="#modify-tabs" class="no-underline">tabs.html</a></c-index-sublink>
<c-index-sublink><a href="#modify-tab" class="no-underline">tab.cotton.html</a></c-index-sublink> <c-index-sublink><a href="#modify-tab" class="no-underline">tab.html</a></c-index-sublink>
<c-index-sublink><a href="#example" class="no-underline">Example</a></c-index-sublink> <c-index-sublink><a href="#example" class="no-underline">Example</a></c-index-sublink>
</c-slot> </c-slot>
@ -69,9 +69,9 @@
<p>Now we have the design right, let's chop it up into components.</p> <p>Now we have the design right, let's chop it up into components.</p>
<h4>tabs.cotton.html</h4> <h4>Tabs component</h4>
<c-snippet>{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/tabs.html">{% cotton_verbatim %}{% verbatim %}
<div class="bg-white rounded-lg overflow-hidden border shadow"> <div class="bg-white rounded-lg overflow-hidden border shadow">
<div class="flex items-center space-x-5 w-full bg-gray-100"> <div class="flex items-center space-x-5 w-full bg-gray-100">
<!-- tab navigation will go here --> <!-- tab navigation will go here -->
@ -85,9 +85,9 @@
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<h4>tab.cotton.html</h4> <h4>Tab component</h4>
<c-snippet>{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/tab.html">{% cotton_verbatim %}{% verbatim %}
<div> <div>
{{ slot }} {{ slot }}
</div> </div>
@ -134,9 +134,9 @@
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<h3 id="modify-tabs">Modifying tabs.cotton.html</h3> <h3 id="modify-tabs">Modifying tabs.html</h3>
<p>Next we'll make the appropriate changes to our tabs.cotton.html component to integrate alpine.js</p> <p>Next we'll make the appropriate changes to our tabs.html component to integrate alpine.js</p>
<c-snippet>{% cotton_verbatim %}{% verbatim %} <c-snippet>{% cotton_verbatim %}{% verbatim %}
<div x-data="tabs" x-init="$watch('tabs', () => currentTab = tabs[0])" class="..."> <div x-data="tabs" x-init="$watch('tabs', () => currentTab = tabs[0])" class="...">
@ -156,7 +156,7 @@
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<h3 id="modify-tab">Modifying tab.cotton.html</h3> <h3 id="modify-tab">Modifying tab.html</h3>
<p>We now need to modify the tab component so that it is registered each time it is imported into the dom.</p> <p>We now need to modify the tab component so that it is registered each time it is imported into the dom.</p>

View file

@ -16,15 +16,14 @@
</c-slot> </c-slot>
<p>Components are reusable pieces of view template. They can contain native Django template syntax and can be used <p>Components are reusable pieces of view template. They can contain native Django template syntax and can be used
inside standard Django templates. The only requirement is that both, the component and the view that contains inside standard Django templates.</p>
the component must use a <c-highlight>.cotton.html</c-highlight> extension. </p>
<h3>A minimal example:</h3> <h3>A minimal example:</h3>
<c-snippet label="my_component.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/my_component.html">{% cotton_verbatim %}{% verbatim %}
{{ slot }} {{ slot }}
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="my_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="my_view.html">{% cotton_verbatim %}{% verbatim %}
<c-my-component> <c-my-component>
<p>Some content</p> <p>Some content</p>
</c-my-component> </c-my-component>
@ -42,7 +41,7 @@
<p>Components are highly configurable. One way to control the content and behaviour of a component is through attributes.</p> <p>Components are highly configurable. One way to control the content and behaviour of a component is through attributes.</p>
<c-snippet label="weather.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/weather.html">{% cotton_verbatim %}{% verbatim %}
<p>It's {{ temperature }}<sup>{{ unit }}</sup> and the condition is {{ condition }}.</p> <p>It's {{ temperature }}<sup>{{ unit }}</sup> and the condition is {{ condition }}.</p>
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
@ -75,17 +74,17 @@
<c-snippet language="python" label="views.py">{% cotton_verbatim %}{% verbatim %} <c-snippet language="python" label="views.py">{% cotton_verbatim %}{% verbatim %}
def weather_view(request): def weather_view(request):
return render(request, 'view.cotton.html', { return render(request, 'view.html', {
'today': get_weather() 'today': get_weather()
}) })
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="view.html">{% cotton_verbatim %}{% verbatim %}
<c-weather :today="today"></c-weather> <c-weather :today="today"></c-weather>
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="weather.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/weather.html">{% cotton_verbatim %}{% verbatim %}
<p>It's {{ today.temperature }}<sup>{{ today.unit }}</sup> and the condition is {{ today.condition }}.</p> <p>It's {{ today.temperature }}<sup>{{ today.unit }}</sup> and the condition is {{ today.condition }}.</p>
{% endcotton_verbatim %}{% endverbatim %}</c-snippet> {% endcotton_verbatim %}{% endverbatim %}</c-snippet>
@ -101,14 +100,14 @@ def weather_view(request):
<p>After writing a couple of components like this, you will notice the fluidity of this approach.</p> <p>After writing a couple of components like this, you will notice the fluidity of this approach.</p>
<c-snippet label="weather_card.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/weather_card.html">{% cotton_verbatim %}{% verbatim %}
<div class="flex ..."> <div class="flex ...">
<h2>{{ day }}:</h2> {{ icon }} {{ label }} <h2>{{ day }}:</h2> {{ icon }} {{ label }}
</div> </div>
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="view.html">{% cotton_verbatim %}{% verbatim %}
<c-weather-card day="Tuesday"> <c-weather-card day="Tuesday">
<c-slot name="icon"> <c-slot name="icon">
<svg>...</svg> <svg>...</svg>
@ -132,12 +131,12 @@ def weather_view(request):
<p>It's sometimes useful to be able to print all provided attributes to the components in one "key=value" string. This is particularly useful when you are building form input components.</p> <p>It's sometimes useful to be able to print all provided attributes to the components in one "key=value" string. This is particularly useful when you are building form input components.</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<input type="text" class="border ..." {{ attrs }} /> <input type="text" class="border ..." {{ attrs }} />
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="first_name" placeholder="First name" /> <c-input name="first_name" placeholder="First name" />
<c-input name="last_name" placeholder="Last name" value="Smith" readonly /> <c-input name="last_name" placeholder="Last name" value="Smith" readonly />
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
@ -151,7 +150,7 @@ def weather_view(request):
<p>Sometimes you just want to pass a simple boolean to a component. Cotton supports providing the attribute name without a value which will provide a boolean True to the component.</p> <p>Sometimes you just want to pass a simple boolean to a component. Cotton supports providing the attribute name without a value which will provide a boolean True to the component.</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<input type="text" {{ attrs }} /> <input type="text" {{ attrs }} />
{% if required is True %} {% if required is True %}
@ -160,7 +159,7 @@ def weather_view(request):
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="telephone" required /> <c-input name="telephone" required />
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
<c-slot name="preview"> <c-slot name="preview">
@ -182,7 +181,7 @@ def weather_view(request):
<p>This benefits a number of use-cases, for example if you have a select component that you want to provide some value:</p> <p>This benefits a number of use-cases, for example if you have a select component that you want to provide some value:</p>
<c-snippet label="select.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/select.html">{% cotton_verbatim %}{% verbatim %}
<select {{ attrs }}> <select {{ attrs }}>
{% for option in options %} {% for option in options %}
<option value="{{ option }}">{{ option }}</option> <option value="{{ option }}">{{ option }}</option>
@ -191,7 +190,7 @@ def weather_view(request):
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="question_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="question_view.html">{% cotton_verbatim %}{% verbatim %}
<c-select name="q1" :options="['yes', 'no', 'maybe']" /> <c-select name="q1" :options="['yes', 'no', 'maybe']" />
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
<c-slot name="preview"> <c-slot name="preview">
@ -206,7 +205,7 @@ def weather_view(request):
<p>This approach can also be utilised by in-component vars in the c-vars tag:</p> <p>This approach can also be utilised by in-component vars in the c-vars tag:</p>
<c-snippet label="search.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/search.html">{% cotton_verbatim %}{% verbatim %}
<c-vars :config="{'category': 'fruit', 'limit': 10}" /> <c-vars :config="{'category': 'fruit', 'limit': 10}" />
<div> <div>
@ -229,7 +228,7 @@ def weather_view(request):
<p>You may design a component that will often have a default behaviour and rarely needs overriding. In this case, you may opt to give a default value to your component.</p> <p>You may design a component that will often have a default behaviour and rarely needs overriding. In this case, you may opt to give a default value to your component.</p>
<c-snippet label="alert.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/alert.html">{% cotton_verbatim %}{% verbatim %}
<c-vars type="success" /> <c-vars type="success" />
<div class="{% if type == 'success' %} .. {% elif type == 'danger' %} .. {% endif %}"> <div class="{% if type == 'success' %} .. {% elif type == 'danger' %} .. {% endif %}">
@ -238,7 +237,7 @@ def weather_view(request):
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-alert>All good!</c-alert> <c-alert>All good!</c-alert>
<c-alert type="danger">Oh no!</c-alert> <c-alert type="danger">Oh no!</c-alert>
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
@ -252,7 +251,7 @@ def weather_view(request):
<p>In order to provide more control when using {{ attrs }} to pass down all attributes as a key=value string to a component. Keys defined in {{ '<c-vars />'|force_escape }} will not be included in this string. This is beneficial when you want to pass any attribute to an html element inside a component using {% verbatim %}{{ attrs }}{% endverbatim %}, but also want to provide additional vars for some other reason.</p> <p>In order to provide more control when using {{ attrs }} to pass down all attributes as a key=value string to a component. Keys defined in {{ '<c-vars />'|force_escape }} will not be included in this string. This is beneficial when you want to pass any attribute to an html element inside a component using {% verbatim %}{{ attrs }}{% endverbatim %}, but also want to provide additional vars for some other reason.</p>
<c-snippet label="input_group.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input_group.html">{% cotton_verbatim %}{% verbatim %}
<c-vars label errors /> <c-vars label errors />
<label>{{ label }}</label> <label>{{ label }}</label>
@ -267,7 +266,7 @@ def weather_view(request):
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}
</c-snippet> </c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input-group label="First name" placeholder="First name" :errors="errors.first_name" /> <c-input-group label="First name" placeholder="First name" :errors="errors.first_name" />
<c-input-group label="Last name" placeholder="Last name" :errors="errors.last_name" /> <c-input-group label="Last name" placeholder="Last name" :errors="errors.last_name" />
{% endcotton_verbatim %}{% endverbatim %} {% endcotton_verbatim %}{% endverbatim %}

View file

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 419 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 419 B

After

Width:  |  Height:  |  Size: 419 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 665 B

After

Width:  |  Height:  |  Size: 665 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 854 B

After

Width:  |  Height:  |  Size: 854 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 389 B

After

Width:  |  Height:  |  Size: 389 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 373 B

After

Width:  |  Height:  |  Size: 373 B

Before After
Before After

View file

@ -23,11 +23,11 @@
<p>Having to repeat this code more than once may get tedious and difficult to maintain. Let's componentize.</p> <p>Having to repeat this code more than once may get tedious and difficult to maintain. Let's componentize.</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<input type="text" name="{{ name }}" placeholder="{{ placeholder }}" class="border rounded shadow px-3 py-1.5"> <input type="text" name="{{ name }}" placeholder="{{ placeholder }}" class="border rounded shadow px-3 py-1.5">
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="shoe_size" placeholder="Show Size" /> <c-input name="shoe_size" placeholder="Show Size" />
<c-input name="country" placeholder="Country" /> <c-input name="country" placeholder="Country" />
<c-input name="age" placeholder="Age" /> <c-input name="age" placeholder="Age" />
@ -43,13 +43,13 @@
<p>You will probably need more than just a text input in your project. So let's declare an attribute `text` in <c-highlight>{{ '<c-vars />'|force_escape }}</c-highlight>. Adding it as a var will allow us to set "text" as the default. Additionally, it will be excluded from <c-highlight>{% verbatim %}{{ attrs }}{% endverbatim %}</c-highlight>:</p> <p>You will probably need more than just a text input in your project. So let's declare an attribute `text` in <c-highlight>{{ '<c-vars />'|force_escape }}</c-highlight>. Adding it as a var will allow us to set "text" as the default. Additionally, it will be excluded from <c-highlight>{% verbatim %}{{ attrs }}{% endverbatim %}</c-highlight>:</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<c-vars type="text" /> <c-vars type="text" />
<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5"> <input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5">
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="some_text" placeholder="Just a text field" /> <c-input name="some_text" placeholder="Just a text field" />
<c-input type="email" name="email" placeholder="Email" /> <c-input type="email" name="email" placeholder="Email" />
<c-input type="password" name="password" placeholder="Password" /> <c-input type="password" name="password" placeholder="Password" />
@ -67,7 +67,7 @@
<p>Let's also handle displaying errors when we need to.</p> <p>Let's also handle displaying errors when we need to.</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<c-vars type="text" errors /> <c-vars type="text" errors />
<input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5 {% if errors %}border-red-500{% endif %}"> <input type="{{ type }}" {{ attrs }} class="border rounded shadow px-3 py-1.5 {% if errors %}border-red-500{% endif %}">
@ -77,7 +77,7 @@
{% endif %} {% endif %}
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="surname" placeholder="Surname" errors="form.surname.errors" /> <c-input name="surname" placeholder="Surname" errors="form.surname.errors" />
{% endverbatim %}{% endcotton_verbatim %} {% endverbatim %}{% endcotton_verbatim %}
<c-slot name="preview"> <c-slot name="preview">
@ -90,7 +90,7 @@
<p>To take customization a step further and to demonstrate the flexibility of cotton, let's now give our input component the ability to display icons.</p> <p>To take customization a step further and to demonstrate the flexibility of cotton, let's now give our input component the ability to display icons.</p>
<c-snippet label="input.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/input.html">{% cotton_verbatim %}{% verbatim %}
<c-vars type="text" leading_icon trailing_icon /> <c-vars type="text" leading_icon trailing_icon />
<div class="border rounded shadow flex items-center"> <div class="border rounded shadow flex items-center">
@ -102,7 +102,7 @@
</div> </div>
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="form_view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="form_view.html">{% cotton_verbatim %}{% verbatim %}
<c-input name="text" name="username" placeholder="Username"> <c-input name="text" name="username" placeholder="Username">
<c-slot name="leading_icon"> <c-slot name="leading_icon">

View file

@ -87,7 +87,7 @@ Item Title
<div class="col-span-1 rounded-lg overflow-hidden flex flex-col"> <div class="col-span-1 rounded-lg overflow-hidden flex flex-col">
<h2 class="!font-normal !text-xl mb-3 mt-0 text-center"><span class="font-semibold">After:</span> Decoupled, Clean &amp; Re-usable</h2> <h2 class="!font-normal !text-xl mb-3 mt-0 text-center"><span class="font-semibold">After:</span> Decoupled, Clean &amp; Re-usable</h2>
<div class="flex h-full rounded-xl overflow-hidden"> <div class="flex h-full rounded-xl overflow-hidden">
<c-demo.snippet-tabs labels="'view.cotton.html|product.cotton.html'" tabs_id="compare"> <c-demo.snippet-tabs labels="'view.html|product.html'" tabs_id="compare">
<div class="flex flex-col h-full" x-show="active === 0"> <div class="flex flex-col h-full" x-show="active === 0">
<c-snippet rounded="rounded-b-xl">{% cotton_verbatim %}{% verbatim %} <c-snippet rounded="rounded-b-xl">{% cotton_verbatim %}{% verbatim %}
@ -132,7 +132,7 @@ Item Title
</c-demo.example-multi> </c-demo.example-multi>
<c-demo.example-multi labels="'template.cotton.html|product.cotton.html'" tabs_id="compare"> <c-demo.example-multi labels="'template.html|product.html'" tabs_id="compare">
<div class="flex flex-col h-full" x-show="active === 0"> <div class="flex flex-col h-full" x-show="active === 0">
<c-snippet rounded="rounded-b-xl">{% cotton_verbatim %}{% verbatim %} <c-snippet rounded="rounded-b-xl">{% cotton_verbatim %}{% verbatim %}

View file

@ -29,7 +29,7 @@
<h2 id="cottonizing">Let's "cotton-ize"</h2> <h2 id="cottonizing">Let's "cotton-ize"</h2>
<c-snippet label="icons/plane.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/icons/plane.html">{% cotton_verbatim %}{% verbatim %}
<c-vars stroke_width="30" /> <c-vars stroke_width="30" />
<svg {{ attrs }} viewBox="0 0 512 512"> <svg {{ attrs }} viewBox="0 0 512 512">
@ -41,7 +41,7 @@
</svg>{% endverbatim %}{% endcotton_verbatim %} </svg>{% endverbatim %}{% endcotton_verbatim %}
</c-snippet> </c-snippet>
<c-snippet label="view.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="view.html">{% cotton_verbatim %}{% verbatim %}
<c-icons.plane class="w-20 h-20 text-red-500" /> <c-icons.plane class="w-20 h-20 text-red-500" />
<c-icons.plane class="w-20 h-20 text-teal-500" /> <c-icons.plane class="w-20 h-20 text-teal-500" />
<c-icons.plane class="w-20 h-20 text-gray-500 animate-bounce" /> <c-icons.plane class="w-20 h-20 text-gray-500 animate-bounce" />

View file

@ -10,7 +10,7 @@
<p>Cottons makes building layouts a piece of cake. Let's start with composing a base layout that contains the base of our view:</p> <p>Cottons makes building layouts a piece of cake. Let's start with composing a base layout that contains the base of our view:</p>
<c-snippet label="layouts/base.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/layouts/base.html">{% cotton_verbatim %}{% verbatim %}
<html> <html>
<head> <head>
... ...
@ -25,13 +25,13 @@
<p>Imagine that we're creating a web app with an authenticated area, so we'll need areas for guests and users. Let's create these, extending from the base component.</p> <p>Imagine that we're creating a web app with an authenticated area, so we'll need areas for guests and users. Let's create these, extending from the base component.</p>
<c-snippet label="layouts/guest.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/layouts/guest.html">{% cotton_verbatim %}{% verbatim %}
<c-layouts.base> <c-layouts.base>
{{ slot }} {{ slot }}
</c-layouts.base> </c-layouts.base>
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="layouts/app.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="cotton/layouts/app.html">{% cotton_verbatim %}{% verbatim %}
<c-layouts.base> <c-layouts.base>
<div class="sidebar"> <div class="sidebar">
{{ sidebar }} {{ sidebar }}
@ -45,7 +45,7 @@
<h2 id="usage">Using the layouts</h2> <h2 id="usage">Using the layouts</h2>
<c-snippet label="login.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="login.html">{% cotton_verbatim %}{% verbatim %}
<c-layouts.guest> <c-layouts.guest>
<div id="loginForm"> <div id="loginForm">
<input name="email" type="email" placeholder="Email"> <input name="email" type="email" placeholder="Email">
@ -54,7 +54,7 @@
</c-layouts.guest> </c-layouts.guest>
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="dashboard.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="dashboard.html">{% cotton_verbatim %}{% verbatim %}
<c-layouts.app> <c-layouts.app>
<c-slot name="sidebar"> <c-slot name="sidebar">
<a href="/dashboard">Dashboard</a> <a href="/dashboard">Dashboard</a>

View file

@ -47,9 +47,9 @@ TEMPLATES = [
<h2>Create a component</h2> <h2>Create a component</h2>
<p>Create a new directory in your templates directory called <span class="font-mono">cotton</span>. Inside this directory create a new file called <span class="font-mono">card.cotton.html</span> with the following content:</p> <p>Create a new directory in your templates directory called <span class="font-mono">cotton</span>. Inside this directory create a new file called <span class="font-mono">card.html</span> with the following content:</p>
<c-snippet label="templates/cotton/card.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="templates/cotton/card.html">{% cotton_verbatim %}{% verbatim %}
<div class="bg-white shadow rounded border p-4"> <div class="bg-white shadow rounded border p-4">
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
<p>{{ slot }}</p> <p>{{ slot }}</p>
@ -62,14 +62,12 @@ TEMPLATES = [
<h2>Include a component</h2> <h2>Include a component</h2>
<p>Create a view with template. View templates that contain cotton components must also carry the ".cotton.html" extension.</p>
<c-snippet language="python" label="views.py">{% cotton_verbatim %}{% verbatim %} <c-snippet language="python" label="views.py">{% cotton_verbatim %}{% verbatim %}
def dashboard_view(request): def dashboard_view(request):
return render(request, "dashboard.cotton.html") return render(request, "dashboard.html")
{% endverbatim %}{% endcotton_verbatim %}</c-snippet> {% endverbatim %}{% endcotton_verbatim %}</c-snippet>
<c-snippet label="templates/dashboard.cotton.html">{% cotton_verbatim %}{% verbatim %} <c-snippet label="templates/dashboard.html">{% cotton_verbatim %}{% verbatim %}
<c-card title="Trees" url="trees"> <c-card title="Trees" url="trees">
We have the best trees We have the best trees
</c-card> </c-card>
@ -98,14 +96,13 @@ def dashboard_view(request):
<h3 id="basics">Basics</h3> <h3 id="basics">Basics</h3>
<c-ul> <c-ul>
<li>View templates that include cotton components should have <c-highlight>.cotton.html</c-highlight> extension</li> <li>Cotton components should be placed in the <c-highlight>templates/cotton</c-highlight> folder</li>
<li>Cotton components should also be suffixed with <c-highlight>.cotton.html</c-highlight> and be placed in the <c-highlight>templates/cotton</c-highlight> folder</li>
</c-ul> </c-ul>
<h3 id="naming">Naming</h3> <h3 id="naming">Naming</h3>
<p>Cotton uses the following naming conventions:</p> <p>Cotton uses the following naming conventions:</p>
<c-ul> <c-ul>
<li>Component file names are in snake_case: <c-highlight>my_component.cotton.html</c-highlight></li> <li>Component file names are in snake_case: <c-highlight>my_component.html</c-highlight></li>
<li>but are called using kebab-case: <c-highlight>{{ "<c-my-component />"|force_escape }}</c-highlight></li> <li>but are called using kebab-case: <c-highlight>{{ "<c-my-component />"|force_escape }}</c-highlight></li>
</c-ul> </c-ul>
@ -113,7 +110,7 @@ def dashboard_view(request):
<c-ul> <c-ul>
<li>Components in subfolders can be defined using dot notation</li> <li>Components in subfolders can be defined using dot notation</li>
<li>A component in <c-highlight>sidebar/menu/link.cotton.html</c-highlight> would be included as <c-highlight>{{ "<c-sidebar.menu.link />"|force_escape }}</c-highlight></li> <li>A component in <c-highlight>sidebar/menu/link.html</c-highlight> would be included as <c-highlight>{{ "<c-sidebar.menu.link />"|force_escape }}</c-highlight></li>
</c-ul> </c-ul>
<h3 class="tag-style">Tag Style</h3> <h3 class="tag-style">Tag Style</h3>

View file

@ -3,34 +3,34 @@ from django.shortcuts import render
def build_view(template_name): def build_view(template_name):
def view(request): def view(request):
return render(request, f"{template_name}.cotton.html") return render(request, f"{template_name}.html")
return view return view
def home(request): def home(request):
return render(request, "home.cotton.html") return render(request, "home.html")
def quickstart(request): def quickstart(request):
return render(request, "quickstart.cotton.html") return render(request, "quickstart.html")
def installation(request): def installation(request):
return render(request, "installation.cotton.html") return render(request, "installation.html")
def usage(request): def usage(request):
return render(request, "usage.cotton.html") return render(request, "usage.html")
def form_fields(request): def form_fields(request):
return render(request, "form_fields.cotton.html") return render(request, "form_fields.html")
def components(request): def components(request):
return render(request, "components.cotton.html") return render(request, "components.html")
def alpine_js(request): def alpine_js(request):
return render(request, "alpine_js.cotton.html") return render(request, "alpine_js.html")