Page Menu
Home
Sealhub
Search
Configure Global Search
Log In
Files
F10361586
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
index 73bbdb3..7df9ee9 100644
--- a/.eslintrc.cjs
+++ b/.eslintrc.cjs
@@ -1,48 +1,49 @@
module.exports = {
env: { node: true },
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "prettier"],
extends: [
"eslint:recommended",
// disabled due to https://github.com/typescript-eslint/typescript-eslint/issues/8804
// "plugin:@typescript-eslint/recommended",
// disabled due to https://github.com/typescript-eslint/typescript-eslint/issues/8804
// "plugin:@typescript-eslint/recommended-requiring-type-checking",
"plugin:prettier/recommended",
],
parserOptions: {
sourceType: "module",
ecmaFeatures: {
modules: true,
},
// disabled due to https://github.com/typescript-eslint/typescript-eslint/issues/8804
//project: ["./tsconfig.json", "./tsconfig-back.json"],
},
rules: {
"@typescript-eslint/no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
+ "no-unused-vars": [2, { varsIgnorePattern: "TempstreamJSX" }],
"@typescript-eslint/require-await": 0,
/* "jsdoc/require-description": 2, */
"no-await-in-loop": 2,
"@typescript-eslint/consistent-type-assertions": [1, { assertionStyle: "never" }],
"no-console": [1, { allow: ["error"] }],
},
ignorePatterns: ["dist/*", "public/dist/*", "coverage/*", "webhint/*"],
settings: { jsdoc: { mode: "typescript" } },
overrides: [
{
files: ["*.subtest.ts", "*.test.ts"],
rules: {
"@typescript-eslint/no-unsafe-member-access": 0,
"prefer-const": 0,
"@typescript-eslint/no-unsafe-call": 0,
"@typescript-eslint/no-unsafe-return": 0,
"@typescript-eslint/no-unsafe-assignment": 0,
"no-await-in-loop": 1, // sometimes it's easier to debug when requests run sequentially
},
},
{
files: ["*.stimulus.ts", "src/front/*.ts"],
env: { browser: true },
},
],
};
diff --git a/.hintrc b/.hintrc
index 29b634a..593bc8e 100644
--- a/.hintrc
+++ b/.hintrc
@@ -1,50 +1,50 @@
{
"connector": {
"name": "jsdom"
},
"formatters": ["codeframe"],
"hintsTimeout": 20000,
"extends": ["web-recommended", "accessibility"],
"hints": {
"no-friendly-error-pages": "off",
"no-broken-links": "warning",
"doctype": "error",
"apple-touch-icons": "error",
"button-type": "error",
- "compat-api/css": "error",
+ "compat-api/css": "warning",
"compat-api/html": [
"error",
{
"ignore": ["img[loading]"]
}
],
"create-element-svg": "error",
"css-prefix-order": "error",
"disown-opener": "error",
"highest-available-document-mode": "error",
"leading-dot-classlist": "error",
"manifest-exists": "error",
"meta-charset-utf-8": "error",
"meta-viewport": "error",
"no-bom": "error",
"no-inline-styles": "error",
"no-protocol-relative-urls": "error",
"html-checker": "error",
"scoped-svg-styles": "error",
"sri": "error",
"axe/aria": "error",
"axe/color": "error",
"axe/forms": "error",
"axe/keyboard": "error",
"axe/language": "error",
"axe/name-role-value": "error",
"axe/parsing": "error",
"axe/semantics": "error",
"axe/sensory-and-visual-cues": "error",
"axe/structure": "error",
"axe/tables": "error",
"axe/text-alternatives": "error",
"axe/time-and-media": "error",
"no-vulnerable-javascript-libraries": "off"
}
}
diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images-arrow.svg b/src/back/jdd-components/autoscrolling-images/autoscrolling-images-arrow.svg
new file mode 100644
index 0000000..03dd402
--- /dev/null
+++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images-arrow.svg
@@ -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>
diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.css b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.css
new file mode 100644
index 0000000..87438cd
--- /dev/null
+++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.css
@@ -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;
+ }
+}
diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx
new file mode 100644
index 0000000..bce8bef
--- /dev/null
+++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.jdd.tsx
@@ -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>
+ );
+ }
+}
diff --git a/src/back/jdd-components/autoscrolling-images/autoscrolling-images.stimulus.ts b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.stimulus.ts
new file mode 100644
index 0000000..d4b1934
--- /dev/null
+++ b/src/back/jdd-components/autoscrolling-images/autoscrolling-images.stimulus.ts
@@ -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();
+ }
+}
diff --git a/src/back/jdd-components/components.ts b/src/back/jdd-components/components.ts
index 647173c..3d8516d 100644
--- a/src/back/jdd-components/components.ts
+++ b/src/back/jdd-components/components.ts
@@ -1,16 +1,19 @@
// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-components'
import { Registry } from "@sealcode/jdd";
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";
registry.add("image-demo", ImageDemo);
import { MapWithPins } from "./map-with-pins/map-with-pins.jdd.js";
registry.add("map-with-pins", MapWithPins);
import { NiceBox } from "./nice-box/nice-box.jdd.js";
registry.add("nice-box", NiceBox);
import { Table } from "./table/table.jdd.js";
registry.add("table", Table);
diff --git a/src/back/routes/components.css b/src/back/routes/components.css
index c19070c..d6389de 100644
--- a/src/back/routes/components.css
+++ b/src/back/routes/components.css
@@ -1,92 +1,93 @@
.title--components {
body {
max-width: none;
}
.two-column {
display: grid;
grid-template-columns: 1fr 15px min-content;
}
.resize-gutter {
background-color: gray;
cursor: ew-resize;
height: 100%;
}
.resizable {
overflow-x: auto;
}
transition: transform 200ms, opacity 200ms;
&.restarting {
transform: scale(0.99);
opacity: 0.6;
}
.component-preview {
width: var(--resizable-column-width);
& > fieldset {
min-width: 0; /* default is min-content and that causes overflow*/
max-height: calc(100vh - 75px);
overflow-x: auto;
}
}
.component-arguments {
max-height: calc(100vh - 80px);
overflow-y: auto;
}
}
.component-preview-parameters {
fieldset {
background-color: #80808024;
table {
td,
th {
outline: 0.5px solid #0000006b;
&.subdued > * {
opacity: 50%;
}
&.subdued:hover > * {
opacity: 100%;
}
&.sticky {
position: sticky;
background-color: #ececec;
}
&.sticky--left {
left: 0;
box-shadow: 5px 0px 10px -4px #00000047;
}
&.sticky--top {
top: 0;
box-shadow: 0px 5px 10px -4px #00000047;
}
}
}
}
}
.component-preview {
* {
transition: all 150ms;
}
+ container-type: inline-size;
}
@media (scripting: none) {
body {
min-width: max-content;
}
}
.component-preview-size-select {
margin-left: 10px;
}
diff --git a/src/front/controllers.ts b/src/front/controllers.ts
index 900e884..07343c1 100644
--- a/src/front/controllers.ts
+++ b/src/front/controllers.ts
@@ -1,22 +1,25 @@
// DO NOT EDIT! This file is generated automaticaly with 'npm run generate-stimulus'
import * as Turbo from "@hotwired/turbo";
import { Application } from "stimulus";
const application = Application.start();
import { default as RefreshOnTsChanges } from "./../back/html-controllers/refresh-on-ts-changes.stimulus.js";
application.register("refresh-on-ts-changes", RefreshOnTsChanges);
import { default as RefreshStyles } from "./../back/html-controllers/refresh-styles.stimulus.js";
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";
application.register("map-with-pins", MapWithPins);
import { default as ComponentDebugger } from "./../back/routes/component-preview/component-debugger.stimulus.js";
application.register("component-debugger", ComponentDebugger);
import { default as InputImagePreview } from "./../back/routes/component-preview/input-image-preview.stimulus.js";
application.register("input-image-preview", InputImagePreview);
export { Turbo };
diff --git a/src/includes.css b/src/includes.css
index 711a57f..f1031be 100644
--- a/src/includes.css
+++ b/src/includes.css
@@ -1,11 +1,12 @@
/* DO NOT EDIT! This file is generated automaticaly with npx sealgen generate-css-includes */
@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/map-with-pins/map-with-pins.css";
@import "back/jdd-components/nice-box/nice-box.css";
@import "back/jdd-components/table/table.css";
@import "back/routes/common/ui/input.css";
@import "back/routes/components.css";
@import "colors.css";
@import "tables.css";
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 14:49 (5 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1032837
Default Alt Text
(19 KB)
Attached To
Mode
rPLAY Sealious playground
Attached
Detach File
Event Timeline
Log In to Comment