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