use Elementor\Controls_Manager;
use Elementor\Utils;
+use function Roots\asset;
use function Roots\view;
* @return array Widget scripts dependencies.
*/
public function get_script_depends() {
- return [];
+
+ wp_register_script(
+ 'cube-timeline-horizontal',
+ asset('scripts/timeline-horizontal.js'),
+ ['jquery', 'swiper'], // Dependencies
+ null, // Version
+ true // In footer?
+ );
+
+ // Using Swiper because it is already included and heavily used by Elementor
+
+ return [ 'cube-timeline-horizontal' ];
}
/**
]
);
- $this->add_control(
- 'image_desktop',
- [
- 'label' => __( 'Image pour grands écrans', 'cube' ),
- 'type' => Controls_Manager::MEDIA,
- 'default' => [
- 'url' => Utils::get_placeholder_image_src(),
- ],
- ]
- );
-
- $this->add_control(
- 'image_mobile',
- [
- 'label' => __( 'Image pour petits écrans', 'cube' ),
- 'type' => Controls_Manager::MEDIA,
- 'default' => [
- 'url' => Utils::get_placeholder_image_src(),
- ],
- ]
- );
-
$this->add_control(
'items',
[
'label' => __('Items', 'cube'),
'type' => Controls_Manager::REPEATER,
'fields' => [
+ [
+ 'name' => 'image',
+ 'label' => __('Image', 'cube'),
+ 'type' => Controls_Manager::MEDIA,
+ 'default' => [
+ 'url' => Utils::get_placeholder_image_src(),
+ ],
+ ],
[
'name' => 'title',
'label' => __('Title', 'cube'),
'default' => '',
],
[
- 'name' => 'cta_text',
- 'label' => __('Call to Action text', 'cube'),
- 'type' => Controls_Manager::TEXT,
+ 'name' => 'content',
+ 'label' => __('Contenu', 'cube'),
+ 'type' => Controls_Manager::WYSIWYG,
'label_block' => true,
'default' => ''
],
- [
- 'name' => 'cta_link',
- 'label' => __('Call to Action link', 'cube'),
- 'type' => Controls_Manager::URL,
- 'default' => [
- 'url' => '',
- 'is_external' => false,
- ],
- 'show_external' => true
- ]
],
'title_field' => '{{{ title }}}',
]
* @access protected
*/
protected function render() {
- $image_desktop = $this->get_settings('image_desktop');
- $image_mobile = $this->get_settings('image_mobile');
$items = $this->get_settings('items');
- echo view('widgets/timeline-horizontal', compact('image_desktop', 'image_mobile', 'items'));
+
+ foreach ($items as $item) {
+ $item['content'] = $this->parse_text_editor($item['content']);
+ }
+
+ echo view('widgets/timeline-horizontal', compact('items'));
}
}
--- /dev/null
+// ELEMENTOR Trigger
+(function($) {
+ $(window).on('elementor/frontend/init', function () {
+ elementorFrontend.hooks.addAction('frontend/element_ready/cube-timeline-horizontal.default', function ($scope) {
+ const elementSelector = `[data-id="${$scope.data('id')}"] .timeline-horizontal`;
+ const swiperSettings = $(elementSelector).data('swiper'); // Get parameters from data-swiper attribute in HTML
+
+ // Custom paging bullets
+ swiperSettings.pagination.renderBullet = function (index, className) {
+ // In loop mode, Swiper duplicates slides so the collection really starts from 1 instead of 0
+ const title = this.slides[index + 1 ].dataset.title;
+ return '<div class="' + className + '"><span class="bullet">' + (index + 1) +'</span><span class="timeline-horizontal-pagination-bullet-title">'+ title +'</span></div>';
+ };
+
+ const swiper = new Swiper(elementSelector, swiperSettings);
+ });
+ });
+})(jQuery);
&-content
.header-slideshow & // Need some extra specificity to override H1 CSS
@apply text-purple-dark px-5 py-3
- background: rgba(#fff, 0.8)
+ background: $title_bg
width: 50% // Size ratio for intermediate screens
max-width: 870px
z-index: 10
$breakpoint-timeline-horizontal = 900px // When timeline changes to mobile view
.timeline-horizontal
- +below($breakpoint-timeline-horizontal)
- @apply flex flex-row
- // Scale all em children
- font-size: 22px fixed
-
- +below(550px)
- font-size: 18px fixed
- +below(450px)
- font-size: 14px fixed
- +below(380px)
- font-size: 12px fixed
-
- &-items
- @apply flex justify-between items-start
- @apply mb-6
-
- +below($breakpoint-timeline-horizontal)
- @apply flex-col items-center
- flex: 1 1 75%
- margin-bottom: 3em
-
- > * + *
- margin-top: 1.5em
+ .swiper-wrapper
+ @apply items-center
&-item
- @apply flex flex-col justify-center items-center
- @apply text-center
- flex-basis: 22% // ~1/5 columns to match horizontal image
- max-width: 345px
-
- +below(1100px)
- flex-basis: 25%
-
- &-number
- @apply flex items-center justify-center
- @apply bg-purple-dark rounded-full mb-2
- @apply text-white
- font-size: (54/22)em // Relative units so size can be controlled from parent
- width: 1.5em
- height: @width
-
- +below(1280px)
- font-size: (36/22)em
-
- &-title
- @apply font-normal
- font-size: (30/22)em
-
- +below(1280px)
- font-size: 1em
-
- &-btn
- font-size: (18/22)em
- padding: 0.5em 1em !important
-
- &-image
-
- &-desktop
- +below($breakpoint-timeline-horizontal)
- display: none
-
- &-mobile
- width: 100%
- max-width: 250px !important;
- padding-left: 2em;
-
- +above($breakpoint-timeline-horizontal)
- display: none
+ @apply bg-white flex items-center
+ horizontal-spacing(12.5vw)
+
+ &-image-wrapper
+ display: flex
+ flex: 1 1 40%
+ align-items: center
+ justify-content: center
+ constrain(margin-right, 5vw)
+
+ &-content
+ flex: 1 1 60%
+
+ a
+ @apply text-pink
+
+ // Timeline Pagination
+ &-pagination
+ position: relative
+ display: flex
+ justify-content: space-between
+ align-items: flex-start
+ width: 80% !important
+ left: 10% !important
+ padding: 0
+ margin: 0
+ z-index: 1
+
+ // Timeline horizontal line
+ &:before
+ content: ''
+ position: absolute
+ top: r(32px)
+ left: 0
+ right: 0
+ height: r(10px)
+ background: #EDEDF4
+ z-index: -1
+
+
+ &-bullet
+ @apply font-display font-normal
+ font-size: 24px
+ display: flex
+ flex-direction: column
+ justify-content: center
+ align-items: center
+ width: auto
+ height: auto
+ border-radius: 0
+ background: transparent
+ opacity: 1
+ text-transform: uppercase
+ outline: none
+ position: relative
+ transition: color 0.3s
+
+ &:first-child:before
+ content: ''
+ background: #fff
+ position: absolute
+ width: 50%
+ height: 100%
+ left: 0
+ top: 0
+ z-index: 0
+
+ &:last-child:before
+ content: ''
+ background: #fff
+ position: absolute
+ width: 50%
+ height: 100%
+ right: 0
+ top: 0
+ z-index: 0
+
+ .bullet
+ height: r(68px)
+ width: @height
+ line-height: @height
+ font-size: 52px
+ display: inline-block
+ border-radius: 50%
+ margin-right: 0.5em
+ margin-bottom: 0.5em
+ text-align: center
+ color: transparent
+ background: transparent
+ position: relative
+ transition: all 0.15s
+ z-index: 1
+
+ // Non-active timeline node
+ &:before
+ content: ''
+ position: absolute
+ top: 50%
+ left: 50%
+ transform: translate(-50%, -50%)
+ width: r(32px)
+ height: @width
+ background: #fff
+ border-radius: 50%
+ border: r(10px) solid #EDEDF4
+ z-index: 0
+ transition: opacity 0.15s
+
+ &-title
+ display: block
+ text-align: center
+ max-width: 8em
+ z-index: 1
+
+ &-active
+ @apply font-bold text-teal bg-transparent
+
+ .bullet
+ @apply text-white bg-teal
+
+ &:before
+ opacity: 0
+
+
+ // Navigation Arrows
+ &-prev, &-next
+ position: absolute
+ top: 50%
+ transform: translateY(-50%)
+ z-index: 10
+ stroke: currentColor
+ transition: color 0.25s
+ outline: none
+
+ &:hover
+ @apply text-pink
+
+ +below(900px)
+ svg
+ width: 20px
+
+ &-prev
+ constrain(left, 5vw)
+ &-next
+ constrain(right, 5vw)
{{-- TIMELINE --}}
-<div class="timeline-horizontal">
- <div class="timeline-horizontal-items">
+@php
+ $settings = [
+ 'slidesPerView' => 1,
+ 'loop' => true,
+ //'effect' => 'fade', // TODO: try to make this work properly so slides are all the same height
+ 'touchEventsTarget' => 'wrapper', // Fix pagination links sometimes going to wrong place
+ 'pagination' => [
+ 'el' => '.timeline-horizontal-pagination',
+ 'modifierClass' => 'timeline-horizontal-pagination-',
+ 'bulletClass' => 'timeline-horizontal-pagination-bullet',
+ 'bulletActiveClass' => 'timeline-horizontal-pagination-bullet-active',
+ 'clickable' => true,
+ // renderBullet => NOTE: this is handled in timeline-horizontal.js since we can't easily encode a JS function in JSON.
+ ],
+ 'navigation' => [
+ 'nextEl' => '.timeline-horizontal-next',
+ 'prevEl' => '.timeline-horizontal-prev',
+ ],
+ ];
+
+ $swiper = json_encode($settings);
+
+@endphp
+
+<div class="timeline-horizontal-pagination mb-2v"></div>
+<div class="timeline-horizontal swiper-container" data-swiper="{{ $swiper }}">
+ <div class="swiper-wrapper">
@foreach ($items as $item)
- <div class="timeline-horizontal-item">
+ <div class="timeline-horizontal-item swiper-slide" data-title="{{ $item['title'] }}">
- <div class="timeline-horizontal-number">
- {{ $loop->iteration }}
- </div>
+ {{-- TODO: mobile title
+ <div class="timeline-horizontal-number">
+ {{ $loop->iteration }}
+ </div>
+ --}}
- <div class="timeline-horizontal-title">
- {{ $item['title'] }}
+ <div class="timeline-horizontal-item-image-wrapper">
+ @image($item['image']['id'], 'full', ['class' => 'timeline-horizontal-item-image'])
</div>
- @if ($item['cta_text'])
- @php
- $target = $item['cta_link']['is_external'] ? ' target="_blank"' : '';
- $rel = $item['cta_link']['nofollow'] ? ' rel="nofollow"' : '';
- @endphp
- <a class="timeline-horizontal-btn btn px-4 py-2 mt-4" href="{{ $item['cta_link']['url'] }}"{!! $target !!}{!! $rel !!}>
- {{ $item['cta_text'] }}
- </a>
- @endif
-
+ <div class="timeline-horizontal-item-content">
+ <h3 class="text-2xl uppercase">{{ $item['title'] }}</h3>
+ {!! $item['content'] !!}
+ </div>
</div>
@endforeach
</div>
- <div class="timeline-horizontal-image">
- @image($image_desktop['id'], 'full', ['class' => 'timeline-horizontal-image-desktop'])
- @image($image_mobile['id'], 'full', ['class' => 'timeline-horizontal-image-mobile'])
- </div>
+ <div class="timeline-horizontal-prev">@svg('carousel-prev', 'stroke-current')</div>
+ <div class="timeline-horizontal-next">@svg('carousel-next', 'stroke-current')</div>
</div>
mix.js(src`scripts/app.js`, 'scripts')
.js(src`scripts/consultation.js`, 'scripts')
.js(src`scripts/header-slideshow.js`, 'scripts')
+ .js(src`scripts/timeline-horizontal.js`, 'scripts')
.js(src`scripts/link-carousel.js`, 'scripts')
.js(src`scripts/testimonial-carousel.js`, 'scripts')
.js(src`scripts/customizer.js`, 'scripts')