on this page collapsible

This commit is contained in:
Will Abbott 2024-12-01 09:39:38 +00:00
parent f41d300874
commit 12833f753c
13 changed files with 169 additions and 32 deletions

View file

@ -40,9 +40,9 @@
</c-slot>
</c-snippet>
<c-hr />
<c-hr id="attributes" />
<h2 id="attributes">2. Adding Component Attributes</h2>
<h2>2. Adding Component Attributes</h2>
<p>We can further customize components with attribute, which allow you to pass specific data into the component as key-value pairs.</p>
@ -59,9 +59,9 @@
</c-slot>
</c-snippet>
<c-hr />
<c-hr id="named-slots" />
<h2 id="named-slots">3. Using Named Slots</h2>
<h2>3. Using Named Slots</h2>
<p>If we want to pass HTML instead of just a string (or another data type) into a component, we can pass them as <c-highlight>named slots</c-highlight> with the <code>{{ '<c-slot name="...">...</c-slot>'|force_escape }}</code> syntax.</p>
@ -98,9 +98,9 @@
Component filenames should be <c-highlight>snake_cased</c-highlight> by default. To use <c-highlight>kebab-cased</c-highlight> / hyphenated filenames instead, set <code>COTTON_SNAKE_CASED_NAMES</code> to <code>False</code> in your settings.py, <a href="{% url 'configuration' %}">more</a>.
</c-note>
<c-hr />
<c-hr id="dynamic-attributes" />
<h2 id="dynamic-attributes">4. Dynamic Attributes with ":"</h2>
<h2>4. Dynamic Attributes with ":"</h2>
<p>We saw how by default, all attributes that we pass to a component are treated as strings. If we want to pass HTML, we can use named slots. But what if we want to pass another data type like a template variable, boolean, integer, float, dictionary, list, dictionary?</p>
@ -181,9 +181,9 @@ context = { 'today': Weather.objects.get(...) }
{% endcotton_verbatim %}{% endverbatim %}
</c-snippet>
<c-hr />
<c-hr id="attrs" />
<h2 id="attrs">5. Pass all attributes with {% verbatim %}{{ attrs }}{% endverbatim %}</h2>
<h2>5. Pass all attributes with {% verbatim %}{{ attrs }}{% endverbatim %}</h2>
<p>Sometimes it's useful to be able to reflect all attributes provided in the parent on to an HTML element in the component. This is particularly powerful when you are building <a href="{% url 'form-fields' %}">form inputs</a>.</p>
@ -209,15 +209,15 @@ context = { 'today': Weather.objects.get(...) }
{% endcotton_verbatim %}{% endverbatim %}
</c-snippet>
<c-hr />
<c-hr id="vars" />
<h2 id="vars">6. Defining Local Variables with <code>{{ '<c-vars />'|force_escape }}</code></h2>
<h2>6. Defining Local Variables with <code>{{ '<c-vars />'|force_escape }}</code></h2>
<p>The <code>{{ '<c-vars />'|force_escape }}</code> tag simplifies component design by allowing local variable definition, reducing the need for repetitive attribute declarations and maintaining backend state.</p>
<p>Place a single <code>{{ '<c-vars />'|force_escape }}</code> at the top of a component to set key-value pairs that provide a default configuration.</p>
<h3 id="default-attributes">Example: Setting Default Attributes</h3>
<h3>Example: Setting Default Attributes</h3>
<p>In components with common defaults, <code>{{ '<c-vars />'|force_escape }}</code> can pre-define attributes that rarely need overriding.</p>
@ -240,7 +240,7 @@ context = { 'today': Weather.objects.get(...) }
</c-slot>
</c-snippet>
<h3 id="excluded">{{ '<c-vars />'|force_escape }} are excluded from {% verbatim %}{{ attrs }}{% endverbatim %}</h3>
<h3>{{ '<c-vars />'|force_escape }} are excluded from {% verbatim %}{{ attrs }}{% endverbatim %}</h3>
<p>Keys in <code>{{ '<c-vars />'|force_escape }}</code> are omitted from <code>{% verbatim %}{{ attrs }}{% endverbatim %}</code>, making them ideal for configuration attributes that shouldn't appear in HTML attributes.</p>
@ -278,9 +278,9 @@ context = { 'today': Weather.objects.get(...) }
<p>By specifying <code>label</code> and <code>errors</code> keys in <code>{{ '<c-vars />'|force_escape }}</code>, these attributes wont be included in <code>{% verbatim %}{{ attrs }}{% endverbatim %}</code>, allowing you to control attributes that are designed for component configuration and those intended as attributes.</p>
<c-hr />
<c-hr id="boolean-attributes" />
<h2 id="boolean-attributes">7. Boolean attributes</h2>
<h2>7. Boolean attributes</h2>
<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>
@ -301,9 +301,9 @@ context = { 'today': Weather.objects.get(...) }
</c-slot>
</c-snippet>
<c-hr />
<c-hr id="dynamic-components" />
<h2 id="dynamic-components">8. Dynamic Components</h2>
<h2>8. Dynamic Components</h2>
<p>There can be times where components need to be included dynamically. For these cases we can reach for a special <code>{{ '<c-component>'|force_escape }}</code> tag with an <code>is</code> attribute:</p>
@ -323,15 +323,15 @@ context = { 'today': Weather.objects.get(...) }
<c-component is="icon_{{ icon_name }}" />
{% endcotton_verbatim %}{% endverbatim %}</c-snippet>
<c-hr />
<c-hr id="context-isolation" />
<h2 id="context-isolation">9. Context Isolation with `only`</h2>
<h2>9. Context Isolation with `only`</h2>
<p>Similar to Django's <code>{% verbatim %}{% include %}{% endverbatim %}</code> tag you can add an "<code>only</code>" attribute which will prevent the component from inheriting the parent's context.</p>
<c-hr />
<c-hr id="alpine-js-support" />
<h2 id="alpine-js-support">10. Alpine.js support</h2>
<h2>10. Alpine.js support</h2>
<p>The following key features allow you to build re-usable components with alpine.js:</p>
@ -344,9 +344,9 @@ context = { 'today': Weather.objects.get(...) }
</li>
</c-ul>
<c-hr />
<c-hr id="summary" />
<h2 id="summary">Summary of Concepts</h2>
<h2>Summary of Concepts</h2>
<ul>
<li><code>{% verbatim %}{{ slot }}{% endverbatim %}</code> - Default content injection.</li>
<li><c-highlight>Attributes</c-highlight> - Simple, straightforward customization.</li>

View file

@ -1 +1 @@
<div {{ attrs }} class="h-[1px] bg-yellow-900 bg-opacity-10 my-8 w-full dark:bg-gray-700"></div>
<div {{ attrs }} class="h-[1px] bg-yellow-900 bg-opacity-10 mb-6 mt-12 w-full dark:bg-gray-700"></div>

View file

@ -0,0 +1,3 @@
<svg {{ attrs }} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>

After

Width:  |  Height:  |  Size: 219 B

View file

@ -0,0 +1,3 @@
<svg {{ attrs }} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>

After

Width:  |  Height:  |  Size: 219 B

View file

@ -11,6 +11,7 @@
<link rel="icon" type="image/png" href="{% static 'favicon/favicon.png' %}">
<script src="{% static 'highlight/highlight.min.js' %}"></script>
<script defer src="https://cdn.jsdelivr.net/npm/@alpinejs/collapse@3.x.x/dist/cdn.min.js"></script>
<script defer src="{% static 'alpine3.min.js' %}"></script>
<link href="{% static 'highlight/styles/atom-one-dark.min.css' %}" rel="stylesheet" />
<script>

View file

@ -17,8 +17,9 @@
prose
prose-a:text-teal-600
prose-h2:text-3xl
prose-h3:text-2xl
prose-h1:text-3xl
prose-h2:text-2xl
prose-h3:text-xl
prose-headings:text-[#1a384a]
prose-headings:font-semibold
prose-code:before:content-none
@ -36,6 +37,35 @@
dark:prose-code:text-gray-300/80
dark:prose-strong:text-[#fff]
">
{% if page_index %}
<div class="lg:hidden not-prose mb-6">
<c-ui.collapse>
<c-slot name="trigger">
<c-ui.button theme="subtle" class="w-full">
<span class="flex w-full justify-between items-center">
<span>On this page</span>
<template x-if="!expanded">
<c-icons.chevron-down class="size-5" />
</template>
<template x-if="expanded">
<c-icons.chevron-up class="size-5" />
</template>
</span>
</c-ui.button>
</c-slot>
<div class="mt-5">
{{ page_index }}
</div>
<c-hr />
</c-ui.collapse>
</div>
{% endif %}
{{ slot }}
</div>
</div>

View file

@ -64,9 +64,9 @@
localStorage.theme = this.dark ? 'dark' : 'light'
}
}">
<c-icons.sun class="size-8 text-yellow-900/40 dark:text-gray-100" stroke-width="1.5" />
<c-icons.sun class="size-8 text-yellow-900/40 dark:text-gray-100/50" stroke-width="1.5" />
</a>
<a href="https://github.com/wrabit/django-cotton" target="_blank" class="text-yellow-900/40 dark:text-gray-100 flex items-center space-x-2">
<a href="https://github.com/wrabit/django-cotton" target="_blank" class="text-yellow-900/40 dark:text-gray-100/50 flex items-center space-x-2">
<c-icons.github class="size-7" />
</a>
</div>

View file

@ -1,5 +1,4 @@
<div class="sticky top-[68px] pb-6 pt-2">
<c-sidebar-block>
<c-slot name="title">Getting Started</c-slot>
<ul>

View file

@ -0,0 +1,39 @@
<c-vars
:themes="{
'primary': 'bg-sky-500',
'subtle': 'bg-neutral-50 text-gray-800 dark:bg-gray-300/10 dark:text-neutral-300',
'danger': 'bg-red-500',
'warning': 'bg-yellow-500',
'info': 'bg-blue-500',
}"
theme="primary"
:outlined-themes="{
'primary': 'border border-sky-500 text-sky-500',
'subtle': 'border border-neutral-50 text-gray-800',
'danger': 'border border-red-500 text-red-500',
'warning': 'border border-yellow-500 text-yellow-500',
'info': 'border border-blue-500 text-blue-500',
}"
:outlined="False"
class
/>
<button {{ attrs }} type="button" class="
{% if outlined %}
{{ outlined_themes|get_item:theme }}
{% else %}
{{ themes|get_item:theme }}
{% endif %}
cursor-pointer whitespace-nowrap rounded-md
px-4 py-2 text-sm font-medium tracking-wide text-white transition hover:opacity-75 text-center
focus-visible:outline
focus-visible:outline-2
focus-visible:outline-offset-2
focus-visible:outline-sky-500
active:opacity-100
active:outline-offset-0
disabled:opacity-75
disabled:cursor-not-allowed
{{ class }}">
{{ slot }}
</button>

View file

@ -0,0 +1,13 @@
<c-vars trigger_text trigger />
<div x-data="{ expanded: false }">
{% if trigger %}
<span @click="expanded = ! expanded" class="cursor-pointer">{{ trigger }}</span>
{% else %}
<button @click="expanded = ! expanded">{% firstof trigger_text "Toggle" %}</button>
{% endif %}
<div x-show="expanded" x-collapse>
{{ slot }}
</div>
</div>

View file

@ -0,0 +1,41 @@
<c-vars trigger_text />
<div x-data="{ isOpen: false, openedWithKeyboard: false }" class="relative" @keydown.esc.window="isOpen = false, openedWithKeyboard = false">
<!-- Toggle Button -->
<button
type="button"
@click="isOpen = ! isOpen"
class="inline-flex cursor-pointer items-center gap-2 whitespace-nowrap rounded-md border border-neutral-300 bg-neutral-50 px-4 py-2 text-sm font-medium tracking-wide transition
hover:opacity-75
focus-visible:outline
focus-visible:outline-2
focus-visible:outline-offset-2
focus-visible:outline-neutral-800
dark:border-neutral-700
dark:bg-gray-800
dark:focus-visible:outline-neutral-300"
aria-haspopup="true"
@keydown.space.prevent="openedWithKeyboard = true"
@keydown.enter.prevent="openedWithKeyboard = true"
@keydown.down.prevent="openedWithKeyboard = true"
:class="isOpen || openedWithKeyboard ? 'text-neutral-900 dark:text-white' : 'text-neutral-600 dark:text-neutral-300'"
aria-expanded="isOpen || openedWithKeyboard">
{% firstof trigger_text "Select" %}
<svg aria-hidden="true" fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4 totate-0">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 8.25l-7.5 7.5-7.5-7.5"/>
</svg>
</button>
<!-- Dropdown Menu -->
<div x-cloak x-show="isOpen || openedWithKeyboard"
x-transition
x-trap="openedWithKeyboard"
@click.outside="isOpen = false, openedWithKeyboard = false"
@keydown.down.prevent="$focus.wrap().next()"
@keydown.up.prevent="$focus.wrap().previous()"
class="absolute top-11 left-0 flex w-full min-w-[12rem] flex-col overflow-hidden rounded-md border border-neutral-300 bg-neutral-50 py-1.5
dark:border-neutral-700
dark:bg-gray-800"
role="menu">
{{ slot }}
</div>
</div>

View file

@ -0,0 +1,8 @@
<a href="#" class="bg-neutral-50 px-4 py-2 text-sm text-neutral-600
hover:bg-neutral-900/5
hover:text-neutral-900 focus-visible:bg-neutral-900/10 focus-visible:text-neutral-900 focus-visible:outline-none
dark:bg-black/10
dark:text-neutral-300
dark:hover:bg-neutral-50/5 dark:hover:text-white
dark:focus-visible:bg-neutral-50/10
dark:focus-visible:text-white" role="menuitem">{{ slot }}</a>

View file

@ -13,7 +13,7 @@
<h1 id="install">Quickstart</h1>
<h2>Install cotton</h2>
<h2 class="mt-0">Install cotton</h2>
<p>Run the following command:</p>
<c-snippet language="python">pip install django-cotton</c-snippet>
@ -124,15 +124,15 @@ def dashboard_view(request):
<h3 id="basics">Basics</h3>
<c-ul>
<li>Cotton components should be placed in the <c-highlight>templates/cotton</c-highlight> folder ('cotton' directory is <a href="{% url 'configuration' %}">configurable</a> using COTTON_DIR).</li>
<li>Cotton components should be placed in the <c-highlight>templates/cotton</c-highlight> folder ('cotton' path is <a href="{% url 'configuration' %}">configurable</a> using <code>COTTON_DIR</code>).</li>
<li>The <code>templates</code> folder can be located in either an app-level or top-level project root folder.</li>
</c-ul>
<h3 id="naming">Naming</h3>
<p>Cotton uses the following naming conventions:</p>
<c-ul>
<li>Component file names are in snake_case: <c-highlight>my_component.html</c-highlight></li>
<li>but are called using kebab-case: <code>{{ "<c-my-component />"|force_escape }}</code></li>
<li>Component file names by default are in snake_case: <c-highlight>my_component.html</c-highlight> (kebab-case filenames can be enabled, see <a href="{% url 'configuration' %}">configuration</a>)</li>
<li>and are included using kebab-case: <code>{{ "<c-my-component />"|force_escape }}</code></li>
</c-ul>
<h3 id="subfolders">Subfolders</h3>