From c261aacef34a8b893152938247a07846752191ff Mon Sep 17 00:00:00 2001 From: jakub-w Date: Sat, 13 Sep 2025 15:40:46 +0200 Subject: [PATCH] Updated Sealious-Playground to use the external version of sortable Summary: Ref T2984 Test Plan: Remove the node_modules directory, run npm i and check http://localhost:8080/demos/sortable/ for testing Reviewers: #testers, kuba-orlik Reviewed By: #testers, kuba-orlik Subscribers: jenkins-user Maniphest Tasks: T2984 Differential Revision: https://hub.sealcode.org/D1623 --- package-lock.json | 20 +++ package.json | 7 +- src/back/routes/common/sortable/sortable.css | 80 ---------- .../common/sortable/sortable.stimulus.ts | 139 ------------------ src/back/routes/common/sortable/sortable.tsx | 42 ------ src/back/routes/demos/sortable.page.tsx | 2 +- src/front/controllers.ts | 6 +- 7 files changed, 29 insertions(+), 267 deletions(-) delete mode 100644 src/back/routes/common/sortable/sortable.css delete mode 100644 src/back/routes/common/sortable/sortable.stimulus.ts delete mode 100644 src/back/routes/common/sortable/sortable.tsx diff --git a/package-lock.json b/package-lock.json index 9248bb8..00890a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@sealcode/jdd-editor": "^0.2.1", "@sealcode/sealgen": "^0.18.10", "@sealcode/simplemde": "^1.12.1", + "@sealcode/sortable": "^0.0.1", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", @@ -1530,6 +1531,16 @@ "turndown-plugin-gfm": "^1.0.2" } }, + "node_modules/@sealcode/sortable": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@sealcode/sortable/-/sortable-0.0.1.tgz", + "integrity": "sha512-I9dFsXcweQ91ly4if/7EoTga76TKQEbpA4UEvDTnRPNRhTt1jn657mA2yA7G/3lSWBvY5NixDgkdp0vIXzZ+DQ==", + "hasInstallScript": true, + "dependencies": { + "@hotwired/stimulus": "^3.2.2", + "tempstream": "^0.4.6" + } + }, "node_modules/@sealcode/ts-predicates": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/@sealcode/ts-predicates/-/ts-predicates-0.6.2.tgz", @@ -16072,6 +16083,15 @@ "turndown-plugin-gfm": "^1.0.2" } }, + "@sealcode/sortable": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@sealcode/sortable/-/sortable-0.0.1.tgz", + "integrity": "sha512-I9dFsXcweQ91ly4if/7EoTga76TKQEbpA4UEvDTnRPNRhTt1jn657mA2yA7G/3lSWBvY5NixDgkdp0vIXzZ+DQ==", + "requires": { + "@hotwired/stimulus": "^3.2.2", + "tempstream": "^0.4.6" + } + }, "@sealcode/ts-predicates": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/@sealcode/ts-predicates/-/ts-predicates-0.6.2.tgz", diff --git a/package.json b/package.json index 41223cf..036620e 100644 --- a/package.json +++ b/package.json @@ -40,10 +40,12 @@ }, "sealgen": { "styleDirs": [ - "node_modules/@sealcode/jdd-editor/assets" + "node_modules/@sealcode/jdd-editor/assets", + "node_modules/@sealcode/sortable/src/stimulus-styles" ], "controllerDirs": [ - "node_modules/@sealcode/jdd-editor/src/controllers" + "node_modules/@sealcode/jdd-editor/src/controllers", + "node_modules/@sealcode/sortable/src/controllers" ], "copyToPublic": [ { @@ -85,6 +87,7 @@ "@sealcode/jdd-editor": "^0.2.1", "@sealcode/sealgen": "^0.18.10", "@sealcode/simplemde": "^1.12.1", + "@sealcode/sortable": "^0.0.1", "@sealcode/ts-predicates": "^0.6.2", "@types/kill-port": "^2.0.0", "@types/leaflet": "^1.9.8", diff --git a/src/back/routes/common/sortable/sortable.css b/src/back/routes/common/sortable/sortable.css deleted file mode 100644 index ef4a4e5..0000000 --- a/src/back/routes/common/sortable/sortable.css +++ /dev/null @@ -1,80 +0,0 @@ -@keyframes sortable-enter { - from { - transform: scale(0); - } - - to { - transform: scale(1); - } -} - -.sortable-wrapper { - .edge-detector { - height: 30px; - position: fixed; - left: 0; - width: 100%; - display: none; - z-index: 2; - } - - &:has(.is-dragged) { - .edge-detector { - display: block; - } - } - - .sortable { - display: grid; - grid-template-columns: 1fr; - --element-height: 50px; - --gap: 8px; - - .sortable__element { - height: var(--element-height); - grid-column: 1/2; - grid-row: calc((var(--index) + 1) * 3 + 1) / calc((var(--index) + 1) * 3 + 3); - - background-color: white; - box-sizing: border-box; - min-width: 400px; - font-size: 20px; - border: 1px solid black; - display: flex; - align-items: center; - justify-content: center; - cursor: move; - user-select: none; - - animation: sortable-enter 100ms; - - &.is-dragged { - opacity: 20%; - } - } - - .sortable__spacer { - grid-column: 1/2; - grid-row: calc((var(--index) + 1) * 3 + 3) / calc((var(--index) + 1) * 3 + 4); - transition: all 200ms; - height: 8px; - } - - .sortable__hole { - grid-column: 1/2; - z-index: 1; - pointer-events: none; - grid-row: calc((var(--index) + 1) * 3 + 2) / calc((var(--index) + 1) * 3 + 5); - - &.ready-to-drop + .sortable__spacer { - height: var(--element-height) !important; - } - } - - &:has(.is-dragged) { - .sortable__hole { - pointer-events: all; - } - } - } -} diff --git a/src/back/routes/common/sortable/sortable.stimulus.ts b/src/back/routes/common/sortable/sortable.stimulus.ts deleted file mode 100644 index aed8dd2..0000000 --- a/src/back/routes/common/sortable/sortable.stimulus.ts +++ /dev/null @@ -1,139 +0,0 @@ -/* eslint-disable @typescript-eslint/no-for-in-array */ -/* eslint-disable @typescript-eslint/consistent-type-assertions */ -import { Controller } from "stimulus"; - -async function sleep(time: number) { - return new Promise((resolve) => { - setTimeout(resolve, time); - }); -} - -export default class Sortable extends Controller { - dragged_element: HTMLDivElement | null = null; - - clearDoubleHoles() { - this.element - .querySelectorAll(".ready-to-drop") - .forEach((e: HTMLDivElement) => e.classList.remove("ready-to-drop")); - } - - getNthElement(n: number): HTMLDivElement | null { - return this.element.querySelector(`.sortable__element:nth-child(${n})`); - } - - setIndex(node: HTMLDivElement, index: number) { - node.setAttribute("data-index", String(index)); - node.style.setProperty("--index", String(index)); - } - - setupHoleListeners(hole: HTMLDivElement) { - hole.addEventListener("dragenter", (event) => { - event.dataTransfer.dropEffect = "move"; - if (!this.dragged_element) { - return; - } - (event.target as HTMLDivElement).classList.add("ready-to-drop"); - event.preventDefault(); - }); - - hole.addEventListener("dragover", (event) => { - event.dataTransfer.dropEffect = "move"; - if (!this.dragged_element) { - return; - } - event.preventDefault(); - }); - - hole.addEventListener("dragleave", (event) => { - (event.target as HTMLDivElement).classList.remove("ready-to-drop"); - event.preventDefault(); - }); - - hole.addEventListener("drop", (event) => { - const target = event.target as HTMLDivElement; - target.classList.remove("ready-to-drop"); - const index_of_dropped_element = parseInt( - this.dragged_element.getAttribute("data-index") - ); - const index_of_drop_target = parseInt(target.getAttribute("data-index")); - - const nodes_of_dropped_element_index = this.element.querySelectorAll( - `[data-index="${index_of_dropped_element}"]` - ); - const nodes_of_target_index = this.element.querySelectorAll( - `[data-index="${index_of_drop_target}"]` - ); - - let last_node_of_target_index = - nodes_of_target_index[nodes_of_target_index.length - 1]; - - for (const node of Array.from(nodes_of_dropped_element_index)) { - last_node_of_target_index.after(node); - this.setIndex(node as HTMLDivElement, index_of_drop_target); - last_node_of_target_index = node; - } - const next_to_correct = nodes_of_dropped_element_index[0].previousSibling; - const children = Array.from(next_to_correct.parentNode.childNodes); - const children_to_correct = children.slice(2); - for (const dom_index in children_to_correct) { - const index = Math.max(Math.floor(parseInt(dom_index) / 3), 0); - this.setIndex(children_to_correct[dom_index] as HTMLDivElement, index); - } - - event.preventDefault(); - }); - } - - connect() { - this.element.querySelectorAll(".sortable__element").forEach((element) => { - element.addEventListener("dragstart", (e: DragEvent) => { - e.dataTransfer.effectAllowed = "move"; - const target = e.target as HTMLDivElement; - this.dragged_element = target; - setTimeout(() => { - // https://stackoverflow.com/a/20733870/1467284 - target.classList.add("is-dragged"); - }, 0); - }); - - element.addEventListener("dragend", (e: DragEvent) => { - const target = e.target as HTMLDivElement; - this.dragged_element = null; - target.classList.remove("is-dragged"); - }); - }); - this.element - .querySelectorAll(".edge-detector") - .forEach((detector: HTMLDivElement) => { - let is_hovered = false; - detector.addEventListener("dragenter", (e) => { - e.preventDefault(); - const target = e.target as HTMLDivElement; - const step = parseInt(target.getAttribute("data-step")); - is_hovered = true; - void (async () => { - while (is_hovered && this.dragged_element) { - window.scrollTo(window.scrollX, window.scrollY + step); - // eslint-disable-next-line no-await-in-loop - await sleep(16); - } - })(); - }); - - detector.addEventListener("dragover ", (e) => { - // necessary for drag events; - e.preventDefault(); - }); - - detector.addEventListener("dragleave", (e) => { - e.preventDefault(); - is_hovered = false; - }); - }); - this.element - .querySelectorAll(".sortable__hole") - .forEach((dropElement: HTMLDivElement) => - this.setupHoleListeners(dropElement) - ); - } -} diff --git a/src/back/routes/common/sortable/sortable.tsx b/src/back/routes/common/sortable/sortable.tsx deleted file mode 100644 index 5c1ee4d..0000000 --- a/src/back/routes/common/sortable/sortable.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { TempstreamJSX } from "tempstream"; - -export function sortable({ items }: { items: JSX.Element[] }) { - return ( -
-
-
-
-
-
- {items.map((item, index) => ( - <> -
- {item} -
-
-
- - ))} -
-
- ); -} diff --git a/src/back/routes/demos/sortable.page.tsx b/src/back/routes/demos/sortable.page.tsx index 6d82129..ec392d2 100644 --- a/src/back/routes/demos/sortable.page.tsx +++ b/src/back/routes/demos/sortable.page.tsx @@ -2,7 +2,7 @@ import type { Context } from "koa"; import { TempstreamJSX } from "tempstream"; import { Page } from "@sealcode/sealgen"; import html from "../../html.js"; -import { sortable } from "../common/sortable/sortable.js"; +import { sortable } from "@sealcode/sortable"; export const actionName = "SortableDemo"; diff --git a/src/front/controllers.ts b/src/front/controllers.ts index 6c67d0b..19c4083 100644 --- a/src/front/controllers.ts +++ b/src/front/controllers.ts @@ -16,9 +16,6 @@ application.register("map-with-pins", MapWithPins); import { default as HorizontalScroller } from "./../back/routes/common/horizontal-scroller/horizontal-scroller.stimulus.js"; application.register("horizontal-scroller", HorizontalScroller); -import { default as Sortable } from "./../back/routes/common/sortable/sortable.stimulus.js"; -application.register("sortable", Sortable); - import { default as AutogrowTextarea } from "./../../node_modules/@sealcode/jdd-editor/src/controllers/autogrow-textarea.stimulus.js"; application.register("autogrow-textarea", AutogrowTextarea); @@ -52,4 +49,7 @@ application.register("submit-on-input", SubmitOnInput); import { default as Toast } from "./../../node_modules/@sealcode/jdd-editor/src/controllers/toast.stimulus.js"; application.register("toast", Toast); +import { default as Sortable } from "./../../node_modules/@sealcode/sortable/src/controllers/sortable.stimulus.js"; +application.register("sortable", Sortable); + export { Turbo };