瀏覽代碼

Implement Accordion Block (#502)

Jeremy Childers 2 年之前
父節點
當前提交
70fe2fbcb2

+ 2 - 0
coderedcms/blocks/__init__.py

@@ -37,6 +37,7 @@ from .html_blocks import (
     TableBlock
 )
 from .content_blocks import (  # noqa
+    AccordionBlock,
     CardBlock,
     CarouselBlock,
     ContentWallBlock,
@@ -82,6 +83,7 @@ HTML_STREAMBLOCKS = [
 ]
 
 CONTENT_STREAMBLOCKS = HTML_STREAMBLOCKS + [
+    ('accordion', AccordionBlock()),
     ('card', CardBlock()),
     ('carousel', CarouselBlock()),
     ('image_gallery', ImageGalleryBlock()),

+ 13 - 0
coderedcms/blocks/content_blocks.py

@@ -12,6 +12,19 @@ from .base_blocks import BaseBlock, BaseLayoutBlock, ButtonMixin, CollectionChoo
 from .html_blocks import ButtonBlock
 
 
+class AccordionBlock(BaseBlock):
+    """
+    Allows selecting an accordion snippet
+    """
+
+    accordion = SnippetChooserBlock('coderedcms.Accordion')
+
+    class Meta:
+        template = 'coderedcms/blocks/accordion_block.html'
+        icon = 'bars'
+        label = 'Accordion'
+
+
 class CardBlock(BaseBlock):
     """
     A component of information with image, text, and buttons.

+ 43 - 0
coderedcms/migrations/0032_accordion_accordionpanel.py

@@ -0,0 +1,43 @@
+# Generated by Django 4.0.6 on 2022-08-01 21:10
+
+import coderedcms.fields
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('coderedcms', '0031_wagtail3'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Accordion',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=255, verbose_name='Name')),
+            ],
+            options={
+                'verbose_name': 'Accordion',
+                'verbose_name_plural': 'Accordions',
+            },
+        ),
+        migrations.CreateModel(
+            name='AccordionPanel',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
+                ('name', models.CharField(max_length=255, verbose_name='Name')),
+                ('content', coderedcms.fields.CoderedStreamField(blank=True, use_json_field=True)),
+                ('custom_css_class', models.CharField(blank=True, max_length=255, verbose_name='Custom CSS class')),
+                ('custom_id', models.CharField(blank=True, max_length=255, verbose_name='Custom ID')),
+                ('accordion', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='accordion_panels', to='coderedcms.accordion', verbose_name='Accordion')),
+            ],
+            options={
+                'ordering': ['sort_order'],
+                'abstract': False,
+            },
+        ),
+    ]

+ 69 - 0
coderedcms/models/snippet_models.py

@@ -349,6 +349,75 @@ class ReusableContent(models.Model):
         return self.name
 
 
+@register_snippet
+class Accordion(ClusterableModel):
+    """Class for reusable content in a collapsible block."""
+    class Meta:
+        verbose_name = _('Accordion')
+        verbose_name_plural = _("Accordions")
+
+    name = models.CharField(
+        max_length=255,
+        verbose_name=_('Name'),
+    )
+
+    panels = (
+        [
+            MultiFieldPanel(
+                heading=_('Accordion'),
+                children=[
+                    FieldPanel('name'),
+                ]
+            ),
+            InlinePanel('accordion_panels', label=_('Panels'))
+        ]
+    )
+
+    def __str__(self):
+        return self.name
+
+
+class AccordionPanel(Orderable, models.Model):
+    """A panel for a collapsible accordion"""
+
+    accordion = ParentalKey(
+        Accordion,
+        related_name='accordion_panels',
+        verbose_name=_('Accordion'),
+    )
+
+    name = models.CharField(
+        max_length=255,
+        verbose_name=_('Name'),
+    )
+
+    content = CoderedStreamField(
+        HTML_STREAMBLOCKS,
+        blank=True,
+        use_json_field=True,
+    )
+
+    custom_css_class = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('Custom CSS class'),
+    )
+    custom_id = models.CharField(
+        max_length=255,
+        blank=True,
+        verbose_name=_('Custom ID'),
+    )
+
+    panels = (
+        [
+            FieldPanel('custom_css_class'),
+            FieldPanel('custom_id'),
+            FieldPanel("name"),
+            FieldPanel('content'),
+        ]
+    )
+
+
 @register_snippet
 class ContentWall(models.Model):
     """

文件差異過大導致無法顯示
+ 3 - 1
coderedcms/project_template/basic/website/migrations/0001_initial.py


文件差異過大導致無法顯示
+ 3 - 1
coderedcms/project_template/sass/website/migrations/0001_initial.py


+ 27 - 0
coderedcms/templates/coderedcms/blocks/accordion_block.html

@@ -0,0 +1,27 @@
+{% extends 'coderedcms/blocks/base_block.html' %}
+
+{% load wagtailcore_tags coderedcms_tags %}
+
+{% block block_render %}
+
+{% with a_id=self.accordion.id accordion=self.accordion %}
+<div class="accordion" id="accordion-{{a_id}}">
+  {% for panel in accordion.accordion_panels.all %}
+    <div class="card">
+      <div class="card-header" id="accordion-heading-{{forloop.counter}}">
+        <h2 class="mb-0">
+          <button class="btn btn-link collapsed" type="button" data-toggle="collapse" data-target="#collapse-{{a_id}}-{{forloop.counter}}" aria-expanded="false" aria-controls="collapse-{{a_id}}-{{forloop.counter}}">
+            {{panel.name}}
+          </button>
+        </h2>
+      </div>
+      <div id="collapse-{{a_id}}-{{forloop.counter}}" class="collapse" aria-labelledby="accordion-heading-{{forloop.counter}}" data-parent="#accordion-{{a_id}}">
+        <div class="card-body">
+          {% include_block panel.content %}
+        </div>
+      </div>
+    </div>
+  {% endfor %}
+</div>
+{% endwith %}
+{% endblock %}

+ 11 - 0
docs/features/blocks/contentblocks/accordion.rst

@@ -0,0 +1,11 @@
+Accordion Block
+===============
+
+Creates a collapsible section with a button to toggle.
+
+Field Reference
+---------------
+
+Fields and purposes:
+
+* **Accordion** - Choose a preexisting accordion snippet, created under snippets.

+ 2 - 1
docs/features/blocks/contentblocks/index.rst

@@ -8,6 +8,7 @@ Content blocks are the blocks for adding various types of content to your site.
 .. toctree::
     :maxdepth: 1
 
+    accordion
     button
     card
     carousel
@@ -26,4 +27,4 @@ Content blocks are the blocks for adding various types of content to your site.
     quote
     table
     text
-    
+

+ 26 - 0
docs/features/snippets/accordions.rst

@@ -0,0 +1,26 @@
+Accordions
+==========
+
+A representation of an accordion or other collapsible content.
+
+Usage
+-----
+
+You define your Accordions in the Snippets > Accordion section of the admin.  Once defined, any page with a body streamfield can show that Accordion by selecting it with a Accordion block.
+
+Fields
+------
+
+Accordion
+~~~~~~~~~
+**Name**: A unique name for your accordion.  It can be anything, it is just used as a personal reference to easily tell them apart.
+
+Panel
+~~~~~
+
+To add a panel to your accordion, click the "Add Panels" button.
+
+**Name**: A name for this panel, will be displayed at the top of the panel
+**Custom CSS class**: If you need to add a specific css class for this panel, add it here.
+**Custom ID**: If you need to add a specific ID for this panel, add it here.
+**Content**: A streamfield that contains the content blocks for the panel.

+ 1 - 0
docs/features/snippets/index.rst

@@ -7,6 +7,7 @@ functionality.
 .. toctree::
     :maxdepth: 1
 
+    accordions
     carousels
     classifiers
     content_walls

部分文件因文件數量過多而無法顯示