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:
Nijat Babakhanov 2024-04-07 17:48:54 +02:00
parent 62a07fa5b7
commit 7abc419b97
10 changed files with 421 additions and 1 deletions

View File

@ -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,

View File

@ -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",
{ {

View File

@ -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

View File

@ -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;
}
}

View File

@ -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>
);
}
}

View File

@ -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();
}
}

View File

@ -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);

View File

@ -79,6 +79,7 @@
* { * {
transition: all 150ms; transition: all 150ms;
} }
container-type: inline-size;
} }
@media (scripting: none) { @media (scripting: none) {

View File

@ -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);

View File

@ -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";