autoscrolling-images
Summary: Ref T2829 Reviewers: #testers, kuba-orlik Reviewed By: #testers, kuba-orlik Subscribers: kuba-orlik, jenkins-user Maniphest Tasks: T2829 Differential Revision: https://hub.sealcode.org/D1413
This commit is contained in:
parent
62a07fa5b7
commit
7abc419b97
@ -20,6 +20,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
"@typescript-eslint/no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
||||||
|
"no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
|
||||||
"@typescript-eslint/require-await": 0,
|
"@typescript-eslint/require-await": 0,
|
||||||
/* "jsdoc/require-description": 2, */
|
/* "jsdoc/require-description": 2, */
|
||||||
"no-await-in-loop": 2,
|
"no-await-in-loop": 2,
|
||||||
|
2
.hintrc
2
.hintrc
@ -11,7 +11,7 @@
|
|||||||
"doctype": "error",
|
"doctype": "error",
|
||||||
"apple-touch-icons": "error",
|
"apple-touch-icons": "error",
|
||||||
"button-type": "error",
|
"button-type": "error",
|
||||||
"compat-api/css": "error",
|
"compat-api/css": "warning",
|
||||||
"compat-api/html": [
|
"compat-api/html": [
|
||||||
"error",
|
"error",
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
<svg width="32" height="12" viewBox="0 0 32 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 5.25C0.585786 5.25 0.25 5.58579 0.25 6C0.25 6.41421 0.585786 6.75 1 6.75V5.25ZM31.5303 6.53033C31.8232 6.23744 31.8232 5.76256 31.5303 5.46967L26.7574 0.696699C26.4645 0.403806 25.9896 0.403806 25.6967 0.696699C25.4038 0.989593 25.4038 1.46447 25.6967 1.75736L29.9393 6L25.6967 10.2426C25.4038 10.5355 25.4038 11.0104 25.6967 11.3033C25.9896 11.5962 26.4645 11.5962 26.7574 11.3033L31.5303 6.53033ZM1 6.75H31V5.25H1V6.75Z" fill="#0D4D69"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 555 B |
@ -0,0 +1,177 @@
|
|||||||
|
.autoscrolling-images {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images-wrapper {
|
||||||
|
display: grid;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__title-wrapper {
|
||||||
|
max-width: 940px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__title {
|
||||||
|
font-size: 32px;
|
||||||
|
color: #0d4d69;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__arrow-carousel-container {
|
||||||
|
position: absolute;
|
||||||
|
top: -51px;
|
||||||
|
right: -90px;
|
||||||
|
width: 92px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__arrow-carousel {
|
||||||
|
transition: none !important;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__arrow-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
flex: 1 0 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__arrow img {
|
||||||
|
max-width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__arrow:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__img-arrow-left {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel-container {
|
||||||
|
position: relative;
|
||||||
|
max-width: 940px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__imgs-carousel {
|
||||||
|
overflow-x: clip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel-page {
|
||||||
|
align-items: center;
|
||||||
|
scroll-snap-align: start;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
flex: 1 0 100%;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-evenly;
|
||||||
|
|
||||||
|
max-width: 940px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__img-wrapper {
|
||||||
|
width: 288px;
|
||||||
|
height: 150px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__radio {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__dots-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__dots {
|
||||||
|
cursor: pointer;
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
background-color: #cadae4;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__dots:hover {
|
||||||
|
background-color: #0d4d69;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes autoscrolling-images-infiniteScroll {
|
||||||
|
0% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateX(calc(-100% + 100cqw));
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(0%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width < 1115px) {
|
||||||
|
.autoscrolling-images__arrow-carousel-container {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@container (width < 800px) {
|
||||||
|
.autoscrolling-images__arrow-carousel-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__dots-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel {
|
||||||
|
max-width: none;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel-container {
|
||||||
|
margin: 0;
|
||||||
|
max-width: calc(100cqw - 20px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel-page {
|
||||||
|
display: flex;
|
||||||
|
flex: none !important;
|
||||||
|
margin: 0;
|
||||||
|
flex-wrap: nowrap !important;
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__img-wrapper {
|
||||||
|
min-width: 288px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.autoscrolling-images__carousel {
|
||||||
|
animation-name: autoscrolling-images-infiniteScroll;
|
||||||
|
animation-duration: var(--animation-length);
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
animation-timing-function: ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
import arrow from "./autoscrolling-images-arrow.svg";
|
||||||
|
|
||||||
|
import { FlatTemplatable, TempstreamJSX } from "tempstream";
|
||||||
|
import {
|
||||||
|
Component,
|
||||||
|
ComponentArguments,
|
||||||
|
ExtractStructuredComponentArgumentsValues,
|
||||||
|
JDDContext,
|
||||||
|
} from "@sealcode/jdd";
|
||||||
|
|
||||||
|
const component_arguments = {
|
||||||
|
title: new ComponentArguments.ShortText(),
|
||||||
|
interval: new ComponentArguments.ShortText().setExampleValues(["5"]),
|
||||||
|
imagesPerPage: new ComponentArguments.ShortText().setExampleValues(["6"]),
|
||||||
|
images: new ComponentArguments.List(
|
||||||
|
new ComponentArguments.Structured({
|
||||||
|
image: new ComponentArguments.Image(),
|
||||||
|
alt: new ComponentArguments.ShortText(),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export class AutoscrollingImages extends Component<typeof component_arguments> {
|
||||||
|
getArguments() {
|
||||||
|
return component_arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
toHTML(
|
||||||
|
{
|
||||||
|
title,
|
||||||
|
interval,
|
||||||
|
imagesPerPage,
|
||||||
|
images,
|
||||||
|
}: ExtractStructuredComponentArgumentsValues<typeof component_arguments>,
|
||||||
|
{ render_image }: JDDContext
|
||||||
|
): FlatTemplatable {
|
||||||
|
const imageNumberPerPage = parseInt(imagesPerPage);
|
||||||
|
let parsedImagesArray = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < images.length; i += imageNumberPerPage) {
|
||||||
|
parsedImagesArray.push(images.slice(i, i + imageNumberPerPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
const radioButtonIdPrefix = "r" + Math.floor(100 + Math.random() * 900);
|
||||||
|
const numberOfImages = images.length * 5;
|
||||||
|
const titleUpperCase = title.toUpperCase();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="autoscrolling-images"
|
||||||
|
data-controller="autoscrolling-images"
|
||||||
|
data-autoscrolling-images-interval={interval}
|
||||||
|
>
|
||||||
|
<style>
|
||||||
|
{parsedImagesArray
|
||||||
|
.map(
|
||||||
|
(_, pageIndex) =>
|
||||||
|
`#${radioButtonIdPrefix}-autoscrolling-images__radio-${pageIndex}:checked ~ .autoscrolling-images__imgs-carousel > .autoscrolling-images__carousel {
|
||||||
|
transform: translateX(calc(${pageIndex} * (-100%)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#${radioButtonIdPrefix}-autoscrolling-images__radio-${pageIndex}:checked ~ .autoscrolling-images__arrow-carousel-container
|
||||||
|
> .autoscrolling-images__arrow-carousel {
|
||||||
|
transform: translateX(calc(${pageIndex} * (-100%)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#${radioButtonIdPrefix}-autoscrolling-images__radio-${pageIndex}:checked ~ .autoscrolling-images__dots-container
|
||||||
|
> label:nth-child(${pageIndex + 1}) {
|
||||||
|
background-color: #0d4d69;
|
||||||
|
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
.join("\n")}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="autoscrolling-images-wrapper"
|
||||||
|
data-carousel-id-prefix={radioButtonIdPrefix}
|
||||||
|
>
|
||||||
|
<div class="autoscrolling-images__title-wrapper">
|
||||||
|
<h2 class="autoscrolling-images__title">{titleUpperCase}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="autoscrolling-images__carousel-container">
|
||||||
|
{parsedImagesArray.map((_, pageIndex) => (
|
||||||
|
<input
|
||||||
|
class="autoscrolling-images__radio"
|
||||||
|
type="radio"
|
||||||
|
name="autoscrolling-images__radio"
|
||||||
|
value={pageIndex}
|
||||||
|
id={`${radioButtonIdPrefix}-autoscrolling-images__radio-${pageIndex}`}
|
||||||
|
checked={pageIndex === 0}
|
||||||
|
data-action="autoscrolling-images#handleRadiochange"
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<div class="autoscrolling-images__arrow-carousel-container">
|
||||||
|
<div class="autoscrolling-images__arrow-carousel">
|
||||||
|
{parsedImagesArray.map((_, pageIndex) => (
|
||||||
|
<div class="autoscrolling-images__arrow-container">
|
||||||
|
<label
|
||||||
|
for={`${radioButtonIdPrefix}-autoscrolling-images__radio-${
|
||||||
|
pageIndex == 0
|
||||||
|
? parsedImagesArray.length - 1
|
||||||
|
: pageIndex - 1
|
||||||
|
}`}
|
||||||
|
class="autoscrolling-images__arrow"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
class="autoscrolling-images__img-arrow-left"
|
||||||
|
src={arrow.url}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label
|
||||||
|
for={`${radioButtonIdPrefix}-autoscrolling-images__radio-${
|
||||||
|
pageIndex == parsedImagesArray.length - 1
|
||||||
|
? 0
|
||||||
|
: pageIndex + 1
|
||||||
|
}`}
|
||||||
|
class="autoscrolling-images__arrow"
|
||||||
|
>
|
||||||
|
<img src={arrow.url} />
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="autoscrolling-images__imgs-carousel">
|
||||||
|
<div
|
||||||
|
class="autoscrolling-images__carousel"
|
||||||
|
style={`--animation-length: ${numberOfImages}s`}
|
||||||
|
>
|
||||||
|
{parsedImagesArray.map((page) => (
|
||||||
|
<div class="autoscrolling-images__carousel-page">
|
||||||
|
{page.map((image) => (
|
||||||
|
<div class="autoscrolling-images__img-wrapper">
|
||||||
|
{render_image(image.image, {
|
||||||
|
container: {
|
||||||
|
width: 288,
|
||||||
|
height: 150,
|
||||||
|
objectFit: "contain",
|
||||||
|
},
|
||||||
|
alt: image.alt,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="autoscrolling-images__dots-container">
|
||||||
|
{parsedImagesArray.map((pageIndex) => (
|
||||||
|
<label
|
||||||
|
for={`${radioButtonIdPrefix}-autoscrolling-images__radio-${pageIndex}`}
|
||||||
|
class="autoscrolling-images__dots"
|
||||||
|
></label>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
import { Controller } from "stimulus";
|
||||||
|
|
||||||
|
export default class AutoscrollingImages extends Controller {
|
||||||
|
currentIndex = 0;
|
||||||
|
interval_id: number;
|
||||||
|
|
||||||
|
getInterval(): number {
|
||||||
|
return parseInt(
|
||||||
|
this.element.getAttribute("data-autoscrolling-images-interval") || "5"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRadioButtons(): Array<HTMLInputElement> {
|
||||||
|
return Array.from(this.element.querySelectorAll(".autoscrolling-images__radio"));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRadioChange() {
|
||||||
|
const selectedRadio = this.getRadioButtons().findIndex(
|
||||||
|
(radio: HTMLInputElement) => radio.checked
|
||||||
|
);
|
||||||
|
this.currentIndex = selectedRadio !== -1 ? selectedRadio : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
async connect() {
|
||||||
|
this.currentIndex = 0;
|
||||||
|
|
||||||
|
let intervalTime: number;
|
||||||
|
const interval = this.getInterval();
|
||||||
|
if (!interval) {
|
||||||
|
intervalTime = interval * 1000;
|
||||||
|
} else {
|
||||||
|
intervalTime = 5000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
this.interval_id = setInterval(
|
||||||
|
() => this.next_slide(),
|
||||||
|
intervalTime
|
||||||
|
) as unknown as number;
|
||||||
|
}
|
||||||
|
|
||||||
|
async disconnect() {
|
||||||
|
clearInterval(this.interval_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
next_slide() {
|
||||||
|
const carouselPages = this.element.querySelectorAll(
|
||||||
|
".autoscrolling-images__carousel-page"
|
||||||
|
);
|
||||||
|
|
||||||
|
const radioButtonIdPrefix = this.element
|
||||||
|
.querySelector(".autoscrolling-images-wrapper")
|
||||||
|
.getAttribute("data-carousel-id-prefix");
|
||||||
|
|
||||||
|
const nextIndex = (this.currentIndex + 1) % carouselPages.length;
|
||||||
|
|
||||||
|
const nextButton =
|
||||||
|
radioButtonIdPrefix + "-autoscrolling-images__radio-" + nextIndex;
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||||
|
(document.getElementById(nextButton) as HTMLInputElement).checked = true;
|
||||||
|
|
||||||
|
this.currentIndex = nextIndex;
|
||||||
|
|
||||||
|
// this.handleRadioChange();
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,9 @@ import { Registry } from "@sealcode/jdd";
|
|||||||
|
|
||||||
export const registry = new Registry();
|
export const registry = new Registry();
|
||||||
|
|
||||||
|
import { AutoscrollingImages } from "./autoscrolling-images/autoscrolling-images.jdd.js";
|
||||||
|
registry.add("autoscrolling-images", AutoscrollingImages);
|
||||||
|
|
||||||
import { ImageDemo } from "./image-demo/image-demo.jdd.js";
|
import { ImageDemo } from "./image-demo/image-demo.jdd.js";
|
||||||
registry.add("image-demo", ImageDemo);
|
registry.add("image-demo", ImageDemo);
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@
|
|||||||
* {
|
* {
|
||||||
transition: all 150ms;
|
transition: all 150ms;
|
||||||
}
|
}
|
||||||
|
container-type: inline-size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (scripting: none) {
|
@media (scripting: none) {
|
||||||
|
@ -10,6 +10,9 @@ application.register("refresh-on-ts-changes", RefreshOnTsChanges);
|
|||||||
import { default as RefreshStyles } from "./../back/html-controllers/refresh-styles.stimulus.js";
|
import { default as RefreshStyles } from "./../back/html-controllers/refresh-styles.stimulus.js";
|
||||||
application.register("refresh-styles", RefreshStyles);
|
application.register("refresh-styles", RefreshStyles);
|
||||||
|
|
||||||
|
import { default as AutoscrollingImages } from "./../back/jdd-components/autoscrolling-images/autoscrolling-images.stimulus.js";
|
||||||
|
application.register("autoscrolling-images", AutoscrollingImages);
|
||||||
|
|
||||||
import { default as MapWithPins } from "./../back/jdd-components/map-with-pins/map-with-pins.stimulus.js";
|
import { default as MapWithPins } from "./../back/jdd-components/map-with-pins/map-with-pins.stimulus.js";
|
||||||
application.register("map-with-pins", MapWithPins);
|
application.register("map-with-pins", MapWithPins);
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
/* DO NOT EDIT! This file is generated automaticaly with npx sealgen generate-css-includes */
|
/* DO NOT EDIT! This file is generated automaticaly with npx sealgen generate-css-includes */
|
||||||
|
|
||||||
@import "../node_modules/@sealcode/sealgen/src/forms/forms.css";
|
@import "../node_modules/@sealcode/sealgen/src/forms/forms.css";
|
||||||
|
@import "back/jdd-components/autoscrolling-images/autoscrolling-images.css";
|
||||||
@import "back/jdd-components/image-demo/image-demo.css";
|
@import "back/jdd-components/image-demo/image-demo.css";
|
||||||
@import "back/jdd-components/map-with-pins/map-with-pins.css";
|
@import "back/jdd-components/map-with-pins/map-with-pins.css";
|
||||||
@import "back/jdd-components/nice-box/nice-box.css";
|
@import "back/jdd-components/nice-box/nice-box.css";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user