Skip to content

Commit b04c3ce

Browse files
authored
Merge branch 'main' into feat/benefits-panel
2 parents ee5530d + 64bdba6 commit b04c3ce

File tree

7 files changed

+299
-11
lines changed

7 files changed

+299
-11
lines changed

backend/static/js/logo_carousel.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
*
3+
* Swiper config
4+
*/
5+
6+
(function() {
7+
'use strict';
8+
9+
function initSwiper() {
10+
11+
if (typeof Swiper === 'undefined') {
12+
console.error('Swiper library not loaded!');
13+
return;
14+
}
15+
16+
try {
17+
const swiperElements = document.querySelectorAll('.logo-swiper-carousel');
18+
19+
if (swiperElements.length === 0) {
20+
return;
21+
}
22+
23+
swiperElements.forEach(elem => {
24+
const loop = (elem.dataset.loop) === 'true';
25+
const spaceBetweenSlides = parseInt(elem.dataset.spaceBetweenSlides, 10) || 20;
26+
const delay = parseInt(elem.dataset.delay) || 3000;
27+
const autoplay = (elem.dataset.autoplay === 'true')
28+
? {delay: delay, disableOnInteraction: false}
29+
: false;
30+
31+
new Swiper(`.logoSwiper-${elem.dataset.instanceId}`, {
32+
slidesPerView: 1, // Mobile
33+
spaceBetween: spaceBetweenSlides,
34+
loop: loop,
35+
keyboard: {
36+
enabled: true,
37+
onlyInViewport: true,
38+
},
39+
autoplay: autoplay,
40+
navigation: {
41+
nextEl: `.btn-next-${elem.dataset.instanceId}`,
42+
prevEl: `.btn-prev-${elem.dataset.instanceId}`,
43+
},
44+
breakpoints: {
45+
768: {
46+
slidesPerView: 3, // Tablet
47+
spaceBetween: spaceBetweenSlides
48+
},
49+
1024: {
50+
slidesPerView: 4, // Desktop
51+
spaceBetween: spaceBetweenSlides
52+
}
53+
}
54+
});
55+
});
56+
} catch (e) {
57+
console.error("Error initializing Swiper instance", e);
58+
}
59+
}
60+
61+
if (document.readyState === 'loading') {
62+
document.addEventListener('DOMContentLoaded', initSwiper);
63+
} else {
64+
initSwiper();
65+
}
66+
67+
})();
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
.carousel-container {
2+
position: relative;
3+
margin: 0 auto;
4+
padding: 0 60px;
5+
min-height: 0;
6+
display: flex;
7+
align-items: center;
8+
}
9+
.logo-swiper-carousel {
10+
width: 100%;
11+
height: auto;
12+
}
13+
.swiper-slide.logo-item {
14+
display: flex;
15+
justify-content: center;
16+
align-items: center;
17+
transition: all 0.4s ease;
18+
height: 100px;
19+
img {
20+
max-height: 100px;
21+
width: 178px;
22+
max-width: 100%;
23+
}
24+
a {
25+
cursor: pointer;
26+
}
27+
@media (max-width: 768px) {
28+
margin-top: 0;
29+
}
30+
}
31+
.nav-btn {
32+
position: absolute;
33+
top: 50%;
34+
transform: translateY(-50%);
35+
width: 32px;
36+
height: 32px;
37+
border-radius: 50%;
38+
display: flex;
39+
align-items: center;
40+
justify-content: center;
41+
cursor: pointer;
42+
z-index: 10;
43+
transition: all 0.3s ease;
44+
padding: 0;
45+
svg {
46+
width: 20px;
47+
height: 20px;
48+
stroke: currentColor;
49+
stroke-width: 2;
50+
fill: none;
51+
}
52+
&.btn-outline-dark:hover,
53+
.btn-outline-dark:hover & {
54+
color: $white;
55+
}
56+
&:hover {
57+
color: $dark;
58+
}
59+
&:focus,
60+
&:focus-visible {
61+
outline: 2px solid $dark;
62+
outline-offset: 2px;
63+
}
64+
}
65+
66+
.btn-prev {
67+
left: 0;
68+
}
69+
.btn-next {
70+
right: 0;
71+
}
72+
.swiper-button-next {
73+
&:after {
74+
display: none;
75+
}
76+
}
77+
.swiper-button-prev {
78+
&:after {
79+
display: none;
80+
}
81+
}
82+
.grayscale {
83+
filter: grayscale(100%);
84+
opacity: 0.6;
85+
transition: all 0.4s ease;
86+
&:hover {
87+
filter: grayscale(0%);
88+
opacity: 1;
89+
}
90+
}

backend/static/scss/main.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@import "./background-grid";
1313
@import "./hero";
1414
@import "./cta_panel";
15+
@import "./logo_carousel";
1516
@import "./timeline";
1617
@import "./features";
1718
@import "./footer";

cms_theme/cms_components.py

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
from django import forms
22
from django.conf import settings
3+
from django.core.validators import MinValueValidator
34
from django.utils.translation import gettext_lazy as _
45
from djangocms_frontend.component_base import CMSFrontendComponent
56
from djangocms_frontend.component_pool import components
6-
from djangocms_frontend.fields import HTMLFormField
7+
from djangocms_frontend.fields import ColoredButtonGroup, HTMLFormField
78
from djangocms_frontend.contrib.icon.fields import IconPickerField
89

9-
from djangocms_frontend.fields import ColoredButtonGroup
10-
1110

1211
@components.register
1312
class Hero(CMSFrontendComponent):
@@ -76,7 +75,7 @@ class Meta:
7675
required=False,
7776
initial="default",
7877
)
79-
78+
8079

8180
@components.register
8281
class TimelineContainer(CMSFrontendComponent):
@@ -174,8 +173,8 @@ class Meta:
174173
required=False,
175174
initial="flex-column",
176175
)
177-
178-
176+
177+
179178
@components.register
180179
class CTAPanel(CMSFrontendComponent):
181180
"""CTAPanel component with background grid option"""
@@ -210,13 +209,73 @@ class Meta:
210209
choices=[
211210
("start", _("Start")),
212211
("center", _("Center (Default)")),
213-
("end", _("End"))
212+
("end", _("End")),
214213
],
215214
initial="center",
216-
help_text=_("Controls horizontal alignment of all content")
215+
help_text=_("Controls horizontal alignment of all content"),
217216
)
218217

219218

219+
@components.register
220+
class LogoCarousel(CMSFrontendComponent):
221+
"""LogoCarousel component"""
222+
223+
class Meta:
224+
name = _("Logo Carousel")
225+
render_template = "carousel/logo_carousel.html"
226+
allow_children = True
227+
child_classes = [
228+
"HeadingPlugin",
229+
"CarouselItemPlugin",
230+
]
231+
mixins = ["Background", "Spacing", "Attributes"]
232+
233+
loop = forms.BooleanField(
234+
label=_("Loop Carousel"),
235+
required=False,
236+
initial=False,
237+
help_text=_(
238+
"Turn on to make the slides loop continuously from the last slide back to the first."
239+
),
240+
)
241+
242+
space_between_slides = forms.IntegerField(
243+
label=_("Space Between Slides"),
244+
required=False,
245+
initial=20,
246+
validators=[MinValueValidator(0)],
247+
help_text=_("Set the space (in pixels) between each slide in the carousel."),
248+
)
249+
250+
autoplay = forms.BooleanField(
251+
label=_("AutoPlay"),
252+
required=False,
253+
initial=True,
254+
help_text=_(
255+
"Turn on to make the slides move automatically without manual navigation."
256+
),
257+
)
258+
259+
delay = forms.IntegerField(
260+
label=_("Autoplay delay"),
261+
required=False,
262+
initial=3000,
263+
validators=[MinValueValidator(500)],
264+
help_text=_(
265+
"Set the time (in milliseconds) each slide stays visible before moving to the next one."
266+
),
267+
)
268+
269+
btn_color = forms.ChoiceField(
270+
label=_("Button Color"),
271+
choices=settings.DJANGOCMS_FRONTEND_COLOR_STYLE_CHOICES,
272+
required=False,
273+
initial="primary",
274+
widget=ColoredButtonGroup(attrs={"class": "flex-wrap"}),
275+
help_text=_("Color for the carousel button."),
276+
)
277+
278+
220279
@components.register
221280
class BenefitsPanel(CMSFrontendComponent):
222281
"""Benefits panel component"""
@@ -270,6 +329,4 @@ class Meta:
270329
label=_("Icon"),
271330
required=False,
272331
)
273-
274-
275-
332+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!-- cms_theme/templates/carousel/logo_carousel.html -->
2+
{% load cms_tags frontend sekizai_tags %}
3+
<section class="logo-section {{ instance.get_classes }}"
4+
{{ instance.get_attributes }}>
5+
{% with plugins=instance.child_plugin_instances %}
6+
<div class="container">
7+
{% for plugin in plugins %}
8+
{% if plugin.plugin_type == "HeadingPlugin" %}
9+
{% render_plugin plugin %}
10+
{% endif %}
11+
{% endfor %}
12+
<div class="carousel-container mx-6">
13+
<div class="swiper logo-swiper-carousel logoSwiper-{{ instance.id }}"
14+
data-instance-id="{{ instance.id }}"
15+
data-loop="{% if instance.loop %}true{% else %}false{% endif %}"
16+
data-space-between-slides="{{ instance.space_between_slides }}"
17+
data-autoplay="{% if instance.autoplay %}true{% else %}false{% endif %}"
18+
data-delay="{{ instance.delay }}">
19+
<div class="swiper-wrapper">
20+
{% for plugin in plugins %}
21+
{% if plugin.plugin_type == "CarouselItemPlugin" %}
22+
{% render_plugin plugin %}
23+
{% endif %}
24+
{% endfor %}
25+
</div>
26+
</div>
27+
<button class="nav-btn btn-prev btn-prev-{{ instance.id }} btn btn-{{ instance.background_context }} btn-outline-{{ instance.btn_color }}"
28+
id="prevBtn-{{ instance.id }}"
29+
aria-label="Previous slide">
30+
<i class="bi bi-chevron-left"></i>
31+
</button>
32+
<button class="nav-btn btn-next btn-next-{{ instance.id }} btn btn-outline-{{ instance.btn_color }}"
33+
id="nextBtn-{{ instance.id }}"
34+
aria-label="Next slide">
35+
<i class="ps-1 bi bi-chevron-right"></i>
36+
</button>
37+
</div>
38+
</div>
39+
{% endwith %}
40+
</section>

cms_theme/templates/cms_theme/base.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
<meta charset="UTF-8" />
66
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
77
<link rel="stylesheet" href="{% static 'css/main.css' %}">
8+
<link href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css"
9+
rel="stylesheet">
810
<link href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css"
911
rel="stylesheet">
1012
{% render_block "css" %}
@@ -19,6 +21,8 @@
1921
<script src="{% static 'js/features.js' %}"></script>
2022
{% render_block "js" %}
2123
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
24+
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
25+
<script src="{% static 'js/logo_carousel.js' %}"></script>
2226
<script>hljs.highlightAll();</script>
2327
</body>
2428
</html>
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<!-- cms_theme/templates/cms_theme/cms_components/carousel_item.html -->
2+
{% load frontend cms_component thumbnail sekizai_tags djangocms_link_tags %}
3+
{% cms_component "CarouselItem" name=_("Carousel Item") parent_classes="LogoCarouselPlugin" %}
4+
{% field "logo" ImageFormField required=True %}
5+
{% field "logo_link" LinkFormField required=False %}
6+
{% field "grayscale" forms.BooleanField required=False label=_("Grayscale") help_text=_("Apply grayscale filter to the logo") %}
7+
<div class="swiper-slide logo-item{% if grayscale %} grayscale{% endif %}">
8+
{% with image=logo|get_related_object %}
9+
{% thumbnail image "400x0" crop subject_location=image.subject_location as sm %}
10+
{% thumbnail image "800x0" crop subject_location=image.subject_location as md %}
11+
{% thumbnail image "1200x0" crop subject_location=image.subject_location as lg %}
12+
{% if logo_link %}
13+
<a href="{{ logo_link|to_url }}"
14+
target="_blank"
15+
rel="noopener noreferrer">
16+
{% endif %}
17+
<img src="{{ md.url }}"
18+
srcset="{{ sm.url }} 400w,
19+
{{ md.url }} 800w,
20+
{{ lg.url }} 1200w"
21+
sizes="(max-width: 768px) 90vw,
22+
(max-width: 1200px) 70vw,
23+
1200px"
24+
alt="{{ image.alt }}"
25+
title="{{ image.title }}"
26+
loading="lazy">
27+
{% if logo_link %}</a>{% endif %}
28+
{% endwith %}
29+
</div>

0 commit comments

Comments
 (0)