diff --git a/djangocms_frontend/contrib/carousel/cms_plugins.py b/djangocms_frontend/contrib/carousel/cms_plugins.py index 98eed47d..f9d421dc 100644 --- a/djangocms_frontend/contrib/carousel/cms_plugins.py +++ b/djangocms_frontend/contrib/carousel/cms_plugins.py @@ -6,6 +6,7 @@ from ... import settings from ...cms_plugins import CMSUIPlugin from ...common.attributes import AttributesMixin +from ...common.background import BackgroundMixin from .. import carousel from ..link.cms_plugins import LinkPluginMixin from . import forms, models @@ -37,7 +38,8 @@ class CarouselPlugin(mixin_factory("Carousel"), AttributesMixin, CMSUIPlugin): ("carousel_aspect_ratio", "carousel_interval"), ("carousel_controls", "carousel_indicators"), ("carousel_keyboard", "carousel_wrap"), - ("carousel_ride", "carousel_pause"), + ("carousel_ride",), + ("carousel_transition", "carousel_pause",), ) }, ), @@ -51,7 +53,7 @@ def get_render_template(self, context, instance, placeholder): @plugin_pool.register_plugin class CarouselSlidePlugin( - mixin_factory("CarouselSlide"), AttributesMixin, LinkPluginMixin, CMSUIPlugin + mixin_factory("CarouselSlide"), AttributesMixin, BackgroundMixin, LinkPluginMixin, CMSUIPlugin ): """ Components > "Carousel Slide" Plugin diff --git a/djangocms_frontend/contrib/carousel/constants.py b/djangocms_frontend/contrib/carousel/constants.py index 75a08025..54575003 100644 --- a/djangocms_frontend/contrib/carousel/constants.py +++ b/djangocms_frontend/contrib/carousel/constants.py @@ -2,15 +2,8 @@ from django.utils.translation import gettext_lazy as _ CAROUSEL_PAUSE_CHOICES = ( - ("hover", "hover"), - ("mouseenter", "mouseenter"), - ("mouseleave", "mouseleave"), - ("false", "off"), -) - -CAROUSEL_RIDE_CHOICES = ( - ("carousel", "carousel"), - ("false", "off"), + ("hover", _("On hover")), + ("false", _("Never")), ) CAROUSEL_TEMPLATE_CHOICES = getattr( @@ -38,3 +31,8 @@ CAROUSEL_ASPECT_RATIO_CHOICES = tuple( (f"{x}x{y}", f"{x}x{y}") for x, y in CAROUSEL_ASPECT_RATIOS ) + +CAROUSEL_TRANSITION_CHOICES = ( + ("", _("Slide")), + ("carousel-fade", _("Fade")), +) diff --git a/djangocms_frontend/contrib/carousel/forms.py b/djangocms_frontend/contrib/carousel/forms.py index 56d74886..482e2cec 100644 --- a/djangocms_frontend/contrib/carousel/forms.py +++ b/djangocms_frontend/contrib/carousel/forms.py @@ -7,11 +7,13 @@ from djangocms_frontend.fields import ( AttributesFormField, + ButtonGroup, TagTypeFormField, TemplateChoiceMixin, ) from ... import settings +from ...common.background import BackgroundFormMixin from ...fields import HTMLFormField from ...helpers import first_choice from ...models import FrontendUIItem @@ -20,8 +22,8 @@ from .constants import ( CAROUSEL_ASPECT_RATIO_CHOICES, CAROUSEL_PAUSE_CHOICES, - CAROUSEL_RIDE_CHOICES, CAROUSEL_TEMPLATE_CHOICES, + CAROUSEL_TRANSITION_CHOICES, ) mixin_factory = settings.get_forms(carousel) @@ -46,6 +48,7 @@ class Meta: "carousel_pause", "carousel_ride", "carousel_wrap", + "carousel_transition", "attributes", ] } @@ -93,11 +96,12 @@ class Meta: '"mouseleave". If set to "false", hovering over the carousel ' "won't pause it." ), + widget=ButtonGroup(attrs=dict(property="text")), ) - carousel_ride = forms.ChoiceField( - label=_("Ride"), - choices=CAROUSEL_RIDE_CHOICES, - initial=first_choice(CAROUSEL_RIDE_CHOICES), + carousel_ride = forms.BooleanField( + label=_("Auto start"), + initial=True, + required=False, help_text=_( "Autoplays the carousel after the user manually cycles the " 'first item. If "carousel", autoplays the carousel on load.' @@ -121,6 +125,16 @@ class Meta: "according to the selected ratio." ), ) + carousel_transition = forms.ChoiceField( + label=_("Transition"), + choices=CAROUSEL_TRANSITION_CHOICES, + required=False, + initial=CAROUSEL_TRANSITION_CHOICES[0][0], + help_text=_( + "Determines if slides change by sliding or fading." + ), + widget=ButtonGroup(attrs=dict(property="text")), + ) attributes = AttributesFormField( excluded_keys=[ "id", @@ -135,7 +149,7 @@ class Meta: class CarouselSlideForm( - mixin_factory("CarouselSlide"), AbstractLinkForm, EntangledModelForm + mixin_factory("CarouselSlide"), AbstractLinkForm, BackgroundFormMixin, EntangledModelForm ): """ Components > "Slide" Plugin @@ -160,6 +174,7 @@ class Meta: queryset=Image.objects.all(), to_field_name="id", label=_("Slide image"), + required=False, ) carousel_content = HTMLFormField( label=_("Content"), diff --git a/djangocms_frontend/contrib/carousel/frameworks/bootstrap5.py b/djangocms_frontend/contrib/carousel/frameworks/bootstrap5.py index e26275a5..b3460266 100644 --- a/djangocms_frontend/contrib/carousel/frameworks/bootstrap5.py +++ b/djangocms_frontend/contrib/carousel/frameworks/bootstrap5.py @@ -4,6 +4,8 @@ class CarouselRenderMixin: def render(self, context, instance, placeholder): instance.add_classes("carousel slide") + if instance.config.get("carousel_transition", None): + instance.add_classes(instance.carousel_transition) return super().render(context, instance, placeholder) diff --git a/djangocms_frontend/contrib/carousel/models.py b/djangocms_frontend/contrib/carousel/models.py index 3b96156a..0906ab2d 100644 --- a/djangocms_frontend/contrib/carousel/models.py +++ b/djangocms_frontend/contrib/carousel/models.py @@ -27,7 +27,7 @@ def get_short_description(self): text += ", {}: {}".format(_("Keyboard"), self.carousel_keyboard) text += ", {}: {}".format(_("Pause"), self.carousel_pause) text += ", {}: {}".format(_("Ride"), self.carousel_ride) - text += "{}: {}".format(_("Wrap"), self.carousel_wrap) + text += ", {}: {}".format(_("Wrap"), self.carousel_wrap) return text diff --git a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/carousel.html b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/carousel.html index dcd78dc6..abe0e164 100644 --- a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/carousel.html +++ b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/carousel.html @@ -4,7 +4,7 @@ data-bs-interval="{{ instance.carousel_interval|lower }}" data-bs-keyboard="{{ instance.carousel_keyboard|lower }}" data-bs-pause="{{ instance.carousel_pause|lower }}" - data-bs-ride="{{ instance.carousel_ride|lower }}" + data-bs-ride="{% if instance.carousel_ride %}carousel{% else %}false{% endif %}" data-bs-wrap="{{ instance.carousel_wrap|lower }}" > {% if instance.carousel_indicators %} diff --git a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/image.html b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/image.html index ba9fa9a4..cd6bdef4 100644 --- a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/image.html +++ b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/image.html @@ -4,6 +4,10 @@ {{ instance.rel_image.default_alt_text|default:'' }} {% else %} -
+
+ {% for plugin in instance.child_plugin_instances %} + {% render_plugin plugin %} + {% endfor %} +
{% endif %}{% endspaceless %} diff --git a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/slide.html b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/slide.html index e0f3b89a..bb13a91c 100644 --- a/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/slide.html +++ b/djangocms_frontend/contrib/carousel/templates/djangocms_frontend/bootstrap5/carousel/default/slide.html @@ -13,13 +13,13 @@ {% endif %} {% endspaceless %} diff --git a/docs/source/components.rst b/docs/source/components.rst index 1a1e7a36..a2b8605d 100644 --- a/docs/source/components.rst +++ b/docs/source/components.rst @@ -32,7 +32,7 @@ each collapsable section. .. image:: screenshots/accordion-plugins.png :width: 394 -Also see Bootstrap 5 `Accordion `_ +Also see Bootstrap 5 `Accordion `_ documentation. .. index:: @@ -58,7 +58,7 @@ the right hand side. New features: Alerts can have **shadows** to optically lift them. -Also see Bootstrap 5 `Alerts `_ +Also see Bootstrap 5 `Alerts `_ documentation. .. index:: @@ -76,7 +76,7 @@ plugin, badges are useful, e.g., to mark featured or new headers. .. image:: screenshots/badge-example.png :width: 180 -Also see Bootstrap 5 `Badge `_ +Also see Bootstrap 5 `Badge `_ documentation. .. index:: @@ -146,7 +146,7 @@ Here is an example of the new card **Image overlay** feature: .. image:: screenshots/card-overlay-example.png :width: 298 -Also see Bootstrap 5 `Card `_ +Also see Bootstrap 5 `Card `_ documentation. .. index:: @@ -156,15 +156,20 @@ documentation. Carousel component ****************** -A `Carousel `_ +A `Carousel `_ is a set of images (potentially with some description) that slide in (or fade in) one after the other after a certain amount of time. +Each slide requires a Carousel Slide child plugin. The simplest case specifies an image, potentially a caption and a link which is followed once the slide is clicked. + +Since the design of carousels is somewhat opinionated template sets can be specified using the ``DJANGOCMS_FRONTEND_CAROUSEL_TEMPLATES`` setting. +.. note:: A Carousel Slide plugin can have child plugins itself. If an image is specified the child plugins add to the caption. If no image is specified the child plugins make up the slide. + ****************** Collapse component ****************** -The `Collapse `_ +The `Collapse `_ hides text behind its headline and offers the user a trigger (e.g., a button) to reveal itself. diff --git a/tests/carousel/test_models.py b/tests/carousel/test_models.py index 0fc84a96..c13e475b 100644 --- a/tests/carousel/test_models.py +++ b/tests/carousel/test_models.py @@ -22,7 +22,7 @@ def test_carousel_instance(self): self.assertEqual( instance.get_short_description(), "(default) Interval: 5000, Controls: True, Indicators: True, " - "Keyboard: True, Pause: hover, Ride: carouselWrap: True", + "Keyboard: True, Pause: hover, Ride: True, Wrap: True", ) def test_carousel_slide_instance(self):