From c4b092bb36cddeb052ad424065156338532d7d5f Mon Sep 17 00:00:00 2001
From: Javier Godoy <11554739+javier-godoy@users.noreply.github.com>
Date: Mon, 22 Apr 2024 16:42:24 -0300
Subject: [PATCH] feat: add support for shadow DOM styling
Close #41
---
.../vaadin/addons/carousel/Carousel.java | 3 +-
.../paper-slider/l2t-paper-slider-base.js | 662 ++++++++++++++++++
.../frontend/paper-slider/l2t-paper-slider.js | 659 +----------------
.../addons/carousel/CarouselDemoView.java | 1 +
.../addons/carousel/CustomThemeDemo.java | 64 ++
.../frontend/carousel-demo-styles.css | 3 +
6 files changed, 746 insertions(+), 646 deletions(-)
create mode 100644 src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider-base.js
create mode 100644 src/test/java/com/flowingcode/vaadin/addons/carousel/CustomThemeDemo.java
create mode 100644 src/test/resources/META-INF/resources/frontend/carousel-demo-styles.css
diff --git a/src/main/java/com/flowingcode/vaadin/addons/carousel/Carousel.java b/src/main/java/com/flowingcode/vaadin/addons/carousel/Carousel.java
index 577c5ed..9a30941 100644
--- a/src/main/java/com/flowingcode/vaadin/addons/carousel/Carousel.java
+++ b/src/main/java/com/flowingcode/vaadin/addons/carousel/Carousel.java
@@ -25,6 +25,7 @@
import com.vaadin.flow.component.DomEvent;
import com.vaadin.flow.component.EventData;
import com.vaadin.flow.component.HasSize;
+import com.vaadin.flow.component.HasTheme;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
@@ -39,7 +40,7 @@
@Tag("l2t-paper-slider")
@NpmPackage(value = "@polymer/iron-a11y-keys-behavior", version = "3.0.1")
@JsModule("./paper-slider/l2t-paper-slider.js")
-public class Carousel extends Component implements HasSize {
+public class Carousel extends Component implements HasSize, HasTheme {
private static final String HIDE_NAV = "hideNav";
private static final String DISABLE_SWIPE = "disableSwipe";
diff --git a/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider-base.js b/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider-base.js
new file mode 100644
index 0000000..0f12137
--- /dev/null
+++ b/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider-base.js
@@ -0,0 +1,662 @@
+/*-
+ * #%L
+ * Carousel Addon
+ * %%
+ * Copyright (C) 2018 - 2024 Flowing Code
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+/*
+ * This file incorporates work licensed under MIT.
+ * Copyright (C) 2018 Philippe Desjardins https://github.com/xpertsea/l2t-paper-slider
+ * Copyright (c) 2018 Andrew Bone https://github.com/Link2Twenty/l2t-paper-slider
+ */
+
+import '@polymer/polymer/polymer-legacy.js';
+
+import { IronA11yKeysBehavior } from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
+import { Polymer as Polymer$0 } from '@polymer/polymer/lib/legacy/polymer-fn.js';
+import { html } from '@polymer/polymer/lib/utils/html-tag.js';
+import { addListener, removeListener } from '@polymer/polymer/lib/utils/gestures.js';
+
+/**
+* Polymer element for displaying slides in a carousel.
+* ### Examples
+* Each slide must be within a paper-slide tag, but other than that you have complete control.
+*
+*
+* #1
+* #2
+* #3
+* #4
+*
+*
+* There is also auto progression and slide duration for how long it should remain on one slide
+*
+*
+* #1
+* #2
+* #3
+*
+*
+* You can set a different default start position, the first start postion is 0 (as opposed to 1)
+*
+*
+* #1
+* #2
+*
+*
+* ### Styling
+* The following custom properties are available for styling:
+*
+* Custom property | Description | Default
+* ----------------|-------------|----------
+* `--paper-slide-dot` | Color of unselected Nav Dot. | `rgba(255, 255, 255, .5)
+* `--paper-slide-dot-selected` | Color of selected Nav Dot. | `#FFF`
+* `--paper-slide-width` | Width of slide container. | `100%`
+* `--paper-slide-height` | Height of slide container. | `600px`
+* `--paper-slider-styles` | (Mixin) Customs styles for slider container | NULL
+* `--paper-slider-dot-container-styles` | (Mixin) Custom styles for dot container | NULL
+* `--paper-slide-dot-styles` | (Mixin) Custon styles for dots | NULL
+* `--paper-slide-background` | Default background color for slides | `rgba(0,0,0,0)`
+* `--paper-slide-font-size` | Default font size for slide | `medium`
+*
+* @polymer
+* @element l2t-paper-slider
+* @demo demo/index.html
+*/
+Polymer$0({
+ _template: html`
+
+
+`,
+
+ is: 'l2t-paper-slider-base',
+
+ listeners: {
+ 'container.track': '_swipeHandler'
+ },
+
+ behaviors: [
+ IronA11yKeysBehavior
+ ],
+
+ hostAttributes: {
+ tabindex: 0
+ },
+
+ properties: {
+
+ // Private //
+
+ /**
+ * Array for storing number
+ * leading up to totalSlides
+ *
+ * @attribute _totalDots
+ * @type Array
+ * @default []
+ */
+ _totalDots: {
+ type: Array,
+ value: [],
+ notify: true,
+ computed: '_createDots(totalSlides)'
+ },
+ /**
+ * Object for storing
+ * all the styles of the
+ * dot elements
+ *
+ * @attribute _dotStyles
+ * @type Object
+ * @default NULL
+ */
+ _dotStyles: {
+ type: Object,
+ notify: true,
+ observer: '_animateCSS'
+ },
+
+ // Public //
+
+ /**
+ * Boolean value to
+ * state if slides should
+ * auto proceed
+ *
+ * @attribute auto-progress
+ * @type Boolean
+ * @default false
+ */
+ autoProgress: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ },
+ /**
+ * Boolean value to
+ * state if swipe shoud
+ * work
+ *
+ * @attribute disableSwipe
+ * @type Boolean
+ * @default false
+ */
+ disableSwipe: {
+ type: Boolean,
+ value: false
+ },
+ /**
+ * Boolean value to
+ * state if nav should
+ * should hidden
+ *
+ * @attribute hide-nav
+ * @type Boolean
+ * @default false
+ */
+ hideNav: {
+ type: Boolean,
+ value: false,
+ notify: true,
+ reflectToAttribute: true,
+ observer: '_reInit'
+ },
+ /**
+ * Number for storing start
+ * position of slides
+ *
+ * @attribute position
+ * @type Number
+ * @default 0
+ */
+ position: {
+ type: Number,
+ value: 0,
+ notify: true,
+ reflectToAttribute: true,
+ observer: '_posObs'
+ },
+ /**
+ * String to storing
+ * high, low or default
+ * swipe sensitivity
+ *
+ * @attribute sensitivity
+ * @type String
+ * @default 'default'
+ */
+ sensitivity: {
+ type: String,
+ value: 'default',
+ notify: true
+ },
+ /**
+ * Number of seconds
+ * each slide should
+ * remain for
+ *
+ * @attribute slide-duration
+ * @type Number
+ * @default 5
+ */
+ slideDuration: {
+ type: Number,
+ value: 5,
+ notify: true
+ },
+ /**
+ * Number for storing total
+ * number of slides
+ *
+ * @attribute total-slides
+ * @type Number
+ * @default NULL
+ */
+ totalSlides: {
+ type: Number,
+ notify: true,
+ reflectToAttribute: true,
+ observer: '_reInit'
+ },
+ },
+
+ // Key Bindings //
+
+ keyBindings: {
+ 'right': '_keyRight',
+ 'left': '_keyLeft',
+ 'space': '_spaceCatcher',
+ 'enter': '_spaceCatcher',
+ },
+
+ // Private //
+
+ /**
+ * Method for styling
+ * and animating dots
+ */
+ _animateCSS: function () {
+ var deep = this.$.container,
+ indSel = deep.querySelector(".slider__indicator");
+ if (this._dotStyles) {
+ deep.querySelector(".slider__slides").style.transform = "translateX(-" + 100 * this.position + "%)",
+ indSel.style.opacity = "1";
+ indSel.style.left = "calc((((" + this._dotStyles.marginRight + " + " + this._dotStyles.marginLeft + ") + (" + this._dotStyles.paddingRight + " + " + this._dotStyles.paddingLeft + ") + " + this._dotStyles.width + ") * " + this.position + ") + " + this.position + " * (" + this._dotStyles.borderLeftWidth + " + " + this._dotStyles.borderRightWidth + "))";
+ indSel.style.right = "calc(" + ((this.totalSlides - (this.position + 1))) + "*((" + this._dotStyles.marginRight + " + " + this._dotStyles.marginLeft + ") + (" + this._dotStyles.borderLeftWidth + " + " + this._dotStyles.borderRightWidth + ") + (" + this._dotStyles.paddingLeft + " + " + this._dotStyles.paddingRight + ") + " + this._dotStyles.width + "))";
+ }
+ },
+
+ /**
+ * Method for setting
+ * the aria-checked property
+ * of dotElems on position change
+ *
+ */
+ _ariaChecked: function () {
+ var dotElems = this.$.container.querySelectorAll('.slider__dot');
+ if (dotElems.length > 0) {
+ for (var i = 0; i < this.totalSlides; i++) {
+ dotElems[i].setAttribute("aria-checked", "false");
+ };
+ dotElems[this.position].setAttribute("aria-checked", "true");
+ }
+ },
+
+ /**
+ * Method for moving
+ * automatically ever
+ * slideDuration seconds
+ *
+ */
+ _autoProceed: function () {
+ var this$ = this;
+ var autoProceed = setInterval(function () {
+ this$.moveNext();
+ }, (this$.slideDuration * 1000));
+ this.$.container.addEventListener("mouseover", function () {
+ clearInterval(autoProceed);
+ });
+ this.$.container.addEventListener("mouseout", function () {
+ autoProceed = setInterval(function () {
+ this$.moveNext();
+ }, (this$.slideDuration * 1000));
+ });
+ },
+
+ /**
+ * Count the slides,
+ * and set totalSlides
+ *
+ */
+ _countSlides: function () {
+ this.totalSlides = this.querySelectorAll("paper-slide").length;
+ },
+
+ /**
+ * Create the nav dots,
+ * 1 for each slide
+ *
+ */
+ _createDots: function (t) {
+ var array = [], i;
+ for (i = 0; i < t; ++i) {
+ array.push(i);
+ };
+ return array;
+ },
+
+ /**
+ * Method for moving
+ * to the previous slide or
+ * to the last slide
+ * and setting focus
+ *
+ */
+ _keyLeft: function () {
+ var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
+ nextPos = currentPos > 0 ? (currentPos - 1) : (this.totalSlides - 1);
+ this.movePos(nextPos);
+ this.moveFocus(nextPos);
+ },
+
+ /**
+ * Method for moving
+ * to the next slide or back
+ * to the first slide
+ * and setting focus
+ *
+ */
+ _keyRight: function () {
+ var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
+ nextPos = currentPos < (this.totalSlides - 1) ? (currentPos + 1) : 0;
+ this.movePos(nextPos);
+ this.moveFocus(nextPos);
+ },
+
+ /**
+ * Method to initiate
+ * and animate move
+ *
+ */
+ _moveInd: function (dotElem) {
+ if (dotElem != undefined) {
+ //#TODO: clearInterval(this.moveNext()); ?
+ var sliderElem = this.$.container,
+ indicatorElem = sliderElem.querySelector('.slider__indicator'),
+ currentPos = parseInt(sliderElem.getAttribute('data-pos')),
+ newPos = parseInt(dotElem.getAttribute('aria-posinset')),
+ newDirection = newPos > currentPos ? 'right' : 'left',
+ currentDirection = newPos < currentPos ? 'right' : 'left';
+ indicatorElem.classList.remove('slider__indicator--' + currentDirection);
+ indicatorElem.classList.add('slider__indicator--' + newDirection);
+ this.position = newPos;
+ }
+ },
+
+ /**
+ * Adds onclick listener
+ * To update the position
+ *
+ */
+ _moveManual: function () {
+ var this$ = this;
+ var dotElems = this.$.container.querySelectorAll('.slider__dot'), i;
+ for (i = 0; i < this.totalSlides; ++i) {
+ dotElems[i].setAttribute("aria-label", "Slide " + (parseInt(dotElems[i].getAttribute('aria-posinset')) + 1) + " selector");
+ dotElems[i].addEventListener('click', function (e) {
+ this$.movePos(e.target.getAttribute('aria-posinset'));
+ });
+ };
+ if (this.totalSlides) {
+ this._dotStyles = window.getComputedStyle(dotElems[0]);
+ }
+ },
+
+ /**
+ * Function to store
+ * functions for the
+ * position observer
+ *
+ */
+ _posObs: function () {
+ this._animateCSS();
+ this._setInert();
+ this._ariaChecked();
+ this.fire('change');
+ },
+
+ /**
+ * Method to reinitialise
+ * on totalSlides change.
+ *
+ */
+ _reInit: function () {
+ var this$ = this;
+ this._animateCSS();
+ setTimeout(function () {
+ this$._moveManual();
+ }, 0);
+ },
+
+ /**
+ * Method for setting
+ * inert on hidden slides
+ */
+ _setInert: function () {
+ var deep = this.$.container,
+ sliSel = deep.querySelectorAll('paper-slide');
+ for (var i = 0; i < sliSel.length; i++) {
+ if (i != this.position) {
+ sliSel[i].setAttribute("inert", "")
+ }
+ }
+ if (sliSel[this.position]) {
+ sliSel[this.position].removeAttribute("inert");
+ }
+ },
+
+ /**
+ * Method for moving
+ * to the selected slide
+ * on key press
+ *
+ */
+ _spaceCatcher: function (e) {
+ e.preventDefault();
+ if (this.shadowRoot != null) {
+ var nextPos = this.shadowRoot.activeElement.getAttribute('aria-posinset');
+ } else {
+ var nextPos = document.activeElement.getAttribute('aria-posinset');
+ }
+ if (!nextPos)
+ return;
+ this.movePos(nextPos);
+ },
+
+ /**
+ * Method for adding
+ * swipe event handler
+ */
+ _swipeHandler: function (e) {
+ if (!(this.disableSwipe)) {
+ var deep = this.$.container;
+ switch (e.detail.state) {
+ case 'start':
+ this.startPosX = e.detail.x;
+ deep.querySelector(".slider__slides").classList.remove('mouseup');
+ break;
+ case 'track':
+ var actualWidth = deep.offsetWidth;
+ var swipeTravel = this.startPosX - e.detail.x;
+ var perActual = this.position == 0 && swipeTravel < 0 ? 0 : this.position == (this.totalSlides - 1) && swipeTravel > 0 ? 0 : (swipeTravel / actualWidth) * 100;
+ var percentMove = perActual > 100 ? (this.position * 100) + 100 : perActual < -100 ? (this.position * 100) - 100 : perActual + (this.position * 100);
+ this.perMov = percentMove <= 100 * (this.totalSlides - 1) ? percentMove >= 0 && percentMove : this.totalSlides;
+ deep.querySelector(".slider__slides").style.transform = "translateX(-" + this.perMov + "%)";
+ break;
+ case 'end':
+ var senNumber = this.sensitivity == "high" ? 0.25 : this.sensitivity == "low" ? -0.25 : 0;
+ var senDirection = this.perMov > (this.position * 100) ? 1 : -1;
+ var newPos = Math.round((this.perMov / 100) + (senNumber * senDirection));
+ deep.querySelector(".slider__slides").classList.add('mouseup');
+ this.position == newPos ? deep.querySelector(".slider__slides").style.transform = "translateX(-" + this.position * 100 + "%)" : false;
+ this.movePos(newPos);
+ break;
+ }
+ }
+ },
+
+ // Public //
+
+ /**
+ * Method for moving
+ * focus to a different
+ * dot.
+ *
+ */
+ moveFocus: function (slide) {
+ var focusEl = this.$.container.querySelectorAll('.slider__dot')[slide];
+ focusEl.focus();
+ },
+
+ /**
+ * Method for moving
+ * to the next slide or back
+ * to the first slide
+ *
+ */
+ moveNext: function () {
+ var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
+ nextPos = currentPos < (this.totalSlides - 1) ? (currentPos + 1) : 0;
+ this.movePos(nextPos);
+ },
+
+ /**
+ * Method for moving
+ * to a specific slide
+ *
+ */
+ movePos: function (slide) {
+ var nextPos = this.$.container.querySelectorAll('.slider__dot')[slide];
+ this._moveInd(nextPos);
+ },
+
+ /**
+ * Method for moving
+ * to the previous slide or
+ * to the last slide
+ *
+ */
+ movePrev: function () {
+ var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
+ nextPos = currentPos > 0 ? (currentPos - 1) : (this.totalSlides - 1);
+ this.movePos(nextPos);
+ },
+
+ // Init //
+
+ /**
+ * Starting the scripts
+ *
+ */
+ attached: function () {
+ var this$ = this;
+ this.autoProgress == true
+ ? setTimeout(function () {
+ this$._autoProceed()
+ }, 0)
+ : false;
+ // Strange hack, but it works.
+ setTimeout(function () {
+ this$._moveManual();
+ this$._ariaChecked();
+ this$._countSlides();
+ }, 0);
+
+ //allow vertical scrolling
+ addListener(this.$.container, 'track', e => this._swipeHandler(e));
+ this.setScrollDirection('y', this.$.container);
+ },
+
+ detached: function() {
+ this.autoProgress = false;
+ removeListener(this.$.container, 'track', e => this._swipeHandler(e));
+ },
+
+});
diff --git a/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider.js b/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider.js
index 7b5a0b9..a9a473e 100644
--- a/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider.js
+++ b/src/main/resources/META-INF/resources/frontend/paper-slider/l2t-paper-slider.js
@@ -1,15 +1,15 @@
/*-
* #%L
- * Carousel Addon
+ * Granite Alert
* %%
- * Copyright (C) 2018 - 2024 Flowing Code
+ * Copyright (C) 2024 Flowing Code
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
- *
+ *
* http://www.apache.org/licenses/LICENSE-2.0
- *
+ *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -17,649 +17,18 @@
* limitations under the License.
* #L%
*/
+import {html} from '@polymer/polymer/lib/utils/html-tag.js';
-/*
- * This file incorporates work licensed under MIT.
- * Copyright (C) 2018 Philippe Desjardins https://github.com/xpertsea/l2t-paper-slider
- * Copyright (c) 2018 Andrew Bone https://github.com/Link2Twenty/l2t-paper-slider
- */
-
-import '@polymer/polymer/polymer-legacy.js';
-
-import { IronA11yKeysBehavior } from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
-import { Polymer as Polymer$0 } from '@polymer/polymer/lib/legacy/polymer-fn.js';
-import { html } from '@polymer/polymer/lib/utils/html-tag.js';
-import { addListener, removeListener } from '@polymer/polymer/lib/utils/gestures.js';
-
-/**
-* Polymer element for displaying slides in a carousel.
-* ### Examples
-* Each slide must be within a paper-slide tag, but other than that you have complete control.
-*
-*
-* #1
-* #2
-* #3
-* #4
-*
-*
-* There is also auto progression and slide duration for how long it should remain on one slide
-*
-*
-* #1
-* #2
-* #3
-*
-*
-* You can set a different default start position, the first start postion is 0 (as opposed to 1)
-*
-*
-* #1
-* #2
-*
-*
-* ### Styling
-* The following custom properties are available for styling:
-*
-* Custom property | Description | Default
-* ----------------|-------------|----------
-* `--paper-slide-dot` | Color of unselected Nav Dot. | `rgba(255, 255, 255, .5)
-* `--paper-slide-dot-selected` | Color of selected Nav Dot. | `#FFF`
-* `--paper-slide-width` | Width of slide container. | `100%`
-* `--paper-slide-height` | Height of slide container. | `600px`
-* `--paper-slider-styles` | (Mixin) Customs styles for slider container | NULL
-* `--paper-slider-dot-container-styles` | (Mixin) Custom styles for dot container | NULL
-* `--paper-slide-dot-styles` | (Mixin) Custon styles for dots | NULL
-* `--paper-slide-background` | Default background color for slides | `rgba(0,0,0,0)`
-* `--paper-slide-font-size` | Default font size for slide | `medium`
-*
-* @polymer
-* @element l2t-paper-slider
-* @demo demo/index.html
-*/
-Polymer$0({
- _template: html`
-
-
-`,
-
- is: 'l2t-paper-slider',
-
- listeners: {
- 'container.track': '_swipeHandler'
- },
-
- behaviors: [
- IronA11yKeysBehavior
- ],
-
- hostAttributes: {
- tabindex: 0
- },
-
- properties: {
-
- // Private //
-
- /**
- * Array for storing number
- * leading up to totalSlides
- *
- * @attribute _totalDots
- * @type Array
- * @default []
- */
- _totalDots: {
- type: Array,
- value: [],
- notify: true,
- computed: '_createDots(totalSlides)'
- },
- /**
- * Object for storing
- * all the styles of the
- * dot elements
- *
- * @attribute _dotStyles
- * @type Object
- * @default NULL
- */
- _dotStyles: {
- type: Object,
- notify: true,
- observer: '_animateCSS'
- },
-
- // Public //
-
- /**
- * Boolean value to
- * state if slides should
- * auto proceed
- *
- * @attribute auto-progress
- * @type Boolean
- * @default false
- */
- autoProgress: {
- type: Boolean,
- value: false,
- notify: true,
- },
- /**
- * Boolean value to
- * state if swipe shoud
- * work
- *
- * @attribute disableSwipe
- * @type Boolean
- * @default false
- */
- disableSwipe: {
- type: Boolean,
- value: false
- },
- /**
- * Boolean value to
- * state if nav should
- * should hidden
- *
- * @attribute hide-nav
- * @type Boolean
- * @default false
- */
- hideNav: {
- type: Boolean,
- value: false,
- notify: true,
- reflectToAttribute: true,
- observer: '_reInit'
- },
- /**
- * Number for storing start
- * position of slides
- *
- * @attribute position
- * @type Number
- * @default 0
- */
- position: {
- type: Number,
- value: 0,
- notify: true,
- reflectToAttribute: true,
- observer: '_posObs'
- },
- /**
- * String to storing
- * high, low or default
- * swipe sensitivity
- *
- * @attribute sensitivity
- * @type String
- * @default 'default'
- */
- sensitivity: {
- type: String,
- value: 'default',
- notify: true
- },
- /**
- * Number of seconds
- * each slide should
- * remain for
- *
- * @attribute slide-duration
- * @type Number
- * @default 5
- */
- slideDuration: {
- type: Number,
- value: 5,
- notify: true
- },
- /**
- * Number for storing total
- * number of slides
- *
- * @attribute total-slides
- * @type Number
- * @default NULL
- */
- totalSlides: {
- type: Number,
- notify: true,
- reflectToAttribute: true,
- observer: '_reInit'
- },
- },
-
- // Key Bindings //
-
- keyBindings: {
- 'right': '_keyRight',
- 'left': '_keyLeft',
- 'space': '_spaceCatcher',
- 'enter': '_spaceCatcher',
- },
-
- // Private //
-
- /**
- * Method for styling
- * and animating dots
- */
- _animateCSS: function () {
- var deep = this.$.container,
- indSel = deep.querySelector(".slider__indicator");
- if (this._dotStyles) {
- deep.querySelector(".slider__slides").style.transform = "translateX(-" + 100 * this.position + "%)",
- indSel.style.opacity = "1";
- indSel.style.left = "calc((((" + this._dotStyles.marginRight + " + " + this._dotStyles.marginLeft + ") + (" + this._dotStyles.paddingRight + " + " + this._dotStyles.paddingLeft + ") + " + this._dotStyles.width + ") * " + this.position + ") + " + this.position + " * (" + this._dotStyles.borderLeftWidth + " + " + this._dotStyles.borderRightWidth + "))";
- indSel.style.right = "calc(" + ((this.totalSlides - (this.position + 1))) + "*((" + this._dotStyles.marginRight + " + " + this._dotStyles.marginLeft + ") + (" + this._dotStyles.borderLeftWidth + " + " + this._dotStyles.borderRightWidth + ") + (" + this._dotStyles.paddingLeft + " + " + this._dotStyles.paddingRight + ") + " + this._dotStyles.width + "))";
- }
- },
-
- /**
- * Method for setting
- * the aria-checked property
- * of dotElems on position change
- *
- */
- _ariaChecked: function () {
- var dotElems = this.$.container.querySelectorAll('.slider__dot');
- if (dotElems.length > 0) {
- for (var i = 0; i < this.totalSlides; i++) {
- dotElems[i].setAttribute("aria-checked", "false");
- };
- dotElems[this.position].setAttribute("aria-checked", "true");
- }
- },
-
- /**
- * Method for moving
- * automatically ever
- * slideDuration seconds
- *
- */
- _autoProceed: function () {
- var this$ = this;
- var autoProceed = setInterval(function () {
- this$.moveNext();
- }, (this$.slideDuration * 1000));
- this.$.container.addEventListener("mouseover", function () {
- clearInterval(autoProceed);
- });
- this.$.container.addEventListener("mouseout", function () {
- autoProceed = setInterval(function () {
- this$.moveNext();
- }, (this$.slideDuration * 1000));
- });
- },
-
- /**
- * Count the slides,
- * and set totalSlides
- *
- */
- _countSlides: function () {
- this.totalSlides = this.querySelectorAll("paper-slide").length;
- },
-
- /**
- * Create the nav dots,
- * 1 for each slide
- *
- */
- _createDots: function (t) {
- var array = [], i;
- for (i = 0; i < t; ++i) {
- array.push(i);
- };
- return array;
- },
-
- /**
- * Method for moving
- * to the previous slide or
- * to the last slide
- * and setting focus
- *
- */
- _keyLeft: function () {
- var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
- nextPos = currentPos > 0 ? (currentPos - 1) : (this.totalSlides - 1);
- this.movePos(nextPos);
- this.moveFocus(nextPos);
- },
-
- /**
- * Method for moving
- * to the next slide or back
- * to the first slide
- * and setting focus
- *
- */
- _keyRight: function () {
- var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
- nextPos = currentPos < (this.totalSlides - 1) ? (currentPos + 1) : 0;
- this.movePos(nextPos);
- this.moveFocus(nextPos);
- },
-
- /**
- * Method to initiate
- * and animate move
- *
- */
- _moveInd: function (dotElem) {
- if (dotElem != undefined) {
- //#TODO: clearInterval(this.moveNext()); ?
- var sliderElem = this.$.container,
- indicatorElem = sliderElem.querySelector('.slider__indicator'),
- currentPos = parseInt(sliderElem.getAttribute('data-pos')),
- newPos = parseInt(dotElem.getAttribute('aria-posinset')),
- newDirection = newPos > currentPos ? 'right' : 'left',
- currentDirection = newPos < currentPos ? 'right' : 'left';
- indicatorElem.classList.remove('slider__indicator--' + currentDirection);
- indicatorElem.classList.add('slider__indicator--' + newDirection);
- this.position = newPos;
- }
- },
-
- /**
- * Adds onclick listener
- * To update the position
- *
- */
- _moveManual: function () {
- var this$ = this;
- var dotElems = this.$.container.querySelectorAll('.slider__dot'), i;
- for (i = 0; i < this.totalSlides; ++i) {
- dotElems[i].setAttribute("aria-label", "Slide " + (parseInt(dotElems[i].getAttribute('aria-posinset')) + 1) + " selector");
- dotElems[i].addEventListener('click', function (e) {
- this$.movePos(e.target.getAttribute('aria-posinset'));
- });
- };
- if (this.totalSlides) {
- this._dotStyles = window.getComputedStyle(dotElems[0]);
- }
- },
-
- /**
- * Function to store
- * functions for the
- * position observer
- *
- */
- _posObs: function () {
- this._animateCSS();
- this._setInert();
- this._ariaChecked();
- this.fire('change');
- },
-
- /**
- * Method to reinitialise
- * on totalSlides change.
- *
- */
- _reInit: function () {
- var this$ = this;
- this._animateCSS();
- setTimeout(function () {
- this$._moveManual();
- }, 0);
- },
-
- /**
- * Method for setting
- * inert on hidden slides
- */
- _setInert: function () {
- var deep = this.$.container,
- sliSel = deep.querySelectorAll('paper-slide');
- for (var i = 0; i < sliSel.length; i++) {
- if (i != this.position) {
- sliSel[i].setAttribute("inert", "")
- }
- }
- if (sliSel[this.position]) {
- sliSel[this.position].removeAttribute("inert");
- }
- },
-
- /**
- * Method for moving
- * to the selected slide
- * on key press
- *
- */
- _spaceCatcher: function (e) {
- e.preventDefault();
- if (this.shadowRoot != null) {
- var nextPos = this.shadowRoot.activeElement.getAttribute('aria-posinset');
- } else {
- var nextPos = document.activeElement.getAttribute('aria-posinset');
- }
- if (!nextPos)
- return;
- this.movePos(nextPos);
- },
-
- /**
- * Method for adding
- * swipe event handler
- */
- _swipeHandler: function (e) {
- if (!(this.disableSwipe)) {
- var deep = this.$.container;
- switch (e.detail.state) {
- case 'start':
- this.startPosX = e.detail.x;
- deep.querySelector(".slider__slides").classList.remove('mouseup');
- break;
- case 'track':
- var actualWidth = deep.offsetWidth;
- var swipeTravel = this.startPosX - e.detail.x;
- var perActual = this.position == 0 && swipeTravel < 0 ? 0 : this.position == (this.totalSlides - 1) && swipeTravel > 0 ? 0 : (swipeTravel / actualWidth) * 100;
- var percentMove = perActual > 100 ? (this.position * 100) + 100 : perActual < -100 ? (this.position * 100) - 100 : perActual + (this.position * 100);
- this.perMov = percentMove <= 100 * (this.totalSlides - 1) ? percentMove >= 0 && percentMove : this.totalSlides;
- deep.querySelector(".slider__slides").style.transform = "translateX(-" + this.perMov + "%)";
- break;
- case 'end':
- var senNumber = this.sensitivity == "high" ? 0.25 : this.sensitivity == "low" ? -0.25 : 0;
- var senDirection = this.perMov > (this.position * 100) ? 1 : -1;
- var newPos = Math.round((this.perMov / 100) + (senNumber * senDirection));
- deep.querySelector(".slider__slides").classList.add('mouseup');
- this.position == newPos ? deep.querySelector(".slider__slides").style.transform = "translateX(-" + this.position * 100 + "%)" : false;
- this.movePos(newPos);
- break;
- }
- }
- },
-
- // Public //
-
- /**
- * Method for moving
- * focus to a different
- * dot.
- *
- */
- moveFocus: function (slide) {
- var focusEl = this.$.container.querySelectorAll('.slider__dot')[slide];
- focusEl.focus();
- },
-
- /**
- * Method for moving
- * to the next slide or back
- * to the first slide
- *
- */
- moveNext: function () {
- var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
- nextPos = currentPos < (this.totalSlides - 1) ? (currentPos + 1) : 0;
- this.movePos(nextPos);
- },
-
- /**
- * Method for moving
- * to a specific slide
- *
- */
- movePos: function (slide) {
- var nextPos = this.$.container.querySelectorAll('.slider__dot')[slide];
- this._moveInd(nextPos);
- },
+import './l2t-paper-slider-base.js';
- /**
- * Method for moving
- * to the previous slide or
- * to the last slide
- *
- */
- movePrev: function () {
- var currentPos = parseInt(this.$.container.getAttribute('data-pos')),
- nextPos = currentPos > 0 ? (currentPos - 1) : (this.totalSlides - 1);
- this.movePos(nextPos);
- },
+import {ThemableMixin} from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
- // Init //
+class L2TPaperSlider extends ThemableMixin(customElements.get('l2t-paper-slider-base')) {
+ static get is() { return 'l2t-paper-slider'; }
- /**
- * Starting the scripts
- *
- */
- attached: function () {
- var this$ = this;
- this.autoProgress == true
- ? setTimeout(function () {
- this$._autoProceed()
- }, 0)
- : false;
- // Strange hack, but it works.
- setTimeout(function () {
- this$._moveManual();
- this$._ariaChecked();
- this$._countSlides();
- }, 0);
+ static get template() {
+ return html`${super.template}`;
+ }
+};
- //allow vertical scrolling
- addListener(this.$.container, 'track', e => this._swipeHandler(e));
- this.setScrollDirection('y', this.$.container);
- },
-
- detached: function() {
- this.autoProgress = false;
- removeListener(this.$.container, 'track', e => this._swipeHandler(e));
- },
-
-});
+customElements.define(L2TPaperSlider.is, L2TPaperSlider);
\ No newline at end of file
diff --git a/src/test/java/com/flowingcode/vaadin/addons/carousel/CarouselDemoView.java b/src/test/java/com/flowingcode/vaadin/addons/carousel/CarouselDemoView.java
index c242c87..e5c82ac 100644
--- a/src/test/java/com/flowingcode/vaadin/addons/carousel/CarouselDemoView.java
+++ b/src/test/java/com/flowingcode/vaadin/addons/carousel/CarouselDemoView.java
@@ -37,6 +37,7 @@ public CarouselDemoView() {
addDemo(ListenerDemo.class);
addDemo(AutoProgressDemo.class);
addDemo(SlideButtonsDemo.class);
+ addDemo(CustomThemeDemo.class);
setSizeFull();
}
diff --git a/src/test/java/com/flowingcode/vaadin/addons/carousel/CustomThemeDemo.java b/src/test/java/com/flowingcode/vaadin/addons/carousel/CustomThemeDemo.java
new file mode 100644
index 0000000..168f778
--- /dev/null
+++ b/src/test/java/com/flowingcode/vaadin/addons/carousel/CustomThemeDemo.java
@@ -0,0 +1,64 @@
+/*-
+ * #%L
+ * Carousel Addon
+ * %%
+ * Copyright (C) 2018 - 2024 Flowing Code
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+package com.flowingcode.vaadin.addons.carousel;
+
+import com.flowingcode.vaadin.addons.demo.DemoSource;
+import com.vaadin.flow.component.dependency.CssImport;
+import com.vaadin.flow.component.orderedlayout.VerticalLayout;
+import com.vaadin.flow.router.PageTitle;
+import com.vaadin.flow.router.Route;
+
+@PageTitle("Slide Listener")
+@DemoSource
+@DemoSource("src/test/resources/META-INF/resources/frontend/carousel-demo-styles.css")
+@Route(value = "carousel/custom-theme", layout = CarouselDemoView.class)
+@SuppressWarnings("serial")
+@CssImport(value = "./carousel-demo-styles.css", themeFor = "l2t-paper-slider")
+public class CustomThemeDemo extends VerticalLayout {
+
+ public CustomThemeDemo() {
+ Slide s1 =
+ new Slide(
+ CarouselDemoView.createSlideContent(
+ "Slide 1",
+ "https://www.flowingcode.com/wp-content/uploads/2018/04/birthday-3021071_640.jpg"));
+ Slide s2 =
+ new Slide(
+ CarouselDemoView.createSlideContent(
+ "Slide 2",
+ "https://2.bp.blogspot.com/-nvtIfgN8duc/XKUQh9VEyFI/AAAAAAAABT8/mE7P45E2uqwWlkKimAmes7fT2rdW9UDWwCEwYBhgL/s320/anniversary_1.jpg"));
+ Slide s3 =
+ new Slide(
+ CarouselDemoView.createSlideContent(
+ "Slide 3",
+ "https://www.flowingcode.com/wp-content/uploads/2020/04/photo4blog-300x300.jpg"));
+ Slide s4 =
+ new Slide(
+ CarouselDemoView.createSlideContent(
+ "Slide 4",
+ "https://www.flowingcode.com/wp-content/uploads/2021/03/happy_birthday_2.jpg"));
+
+ Carousel c = new Carousel(s1, s2, s3, s4);
+ c.setSizeFull();
+ c.setThemeName("custom-theme");
+
+ add(c);
+ }
+}
diff --git a/src/test/resources/META-INF/resources/frontend/carousel-demo-styles.css b/src/test/resources/META-INF/resources/frontend/carousel-demo-styles.css
new file mode 100644
index 0000000..521ef52
--- /dev/null
+++ b/src/test/resources/META-INF/resources/frontend/carousel-demo-styles.css
@@ -0,0 +1,3 @@
+:host([theme~="custom-theme"]) .slider__dots > span {
+ background: purple;
+}
\ No newline at end of file