Jump to content
Search Community

OSUblake's awesome Sortable List With Draggable, five years later

roman999 test
Moderator Tag

Go to solution Solved by roman999,

Recommended Posts

 

I am attempting to use the code from

 

https://greensock.com/forums/topic/14575-how-to-create-a-sortable-list-with-draggable/

 

while converting to GSAP 3, es6, and Typescript. The old code works fine and I updated it to GSAP 3 syntax, still no problem. However, when I use the code in a Typescript application with imports in the *.ts file rather than script tags in the HTML file the event handlers do not get added. Developer Tools shows "_emptyFunc" (screenshot attached as comment). This use case is incompatible with CodePen, at least without payment, but here is part of my code. 

import {gsap} from 'gsap';
import {Draggable} from "gsap/dist/Draggable";
...
	addBehavior():void {
		const rowSize = 100; // => container height / number of items
		const container = document.querySelector(".container");
		const listItems = Array.from(document.querySelectorAll(".list-item")); // Array of elements
		const sortables = listItems.map(Sortable); // Array of sortables
		const total = sortables.length;
		gsap.to(container, { duration: 0.5, autoAlpha: 1 });
		function Sortable(element, index) {
			const animation = gsap.to(element, { duration: 0.3,
				boxShadow: "rgba(0,0,0,0.2) 0px 16px 32px 0px",
				force3D: true,
				scale: 1.1,
				paused: true
			});
			const dragger = new Draggable(element, {
				onDragStart: downAction,
				onRelease: upAction,
				onDrag: dragAction,
				cursor: "inherit",
				type: "y"
			});
			// Public properties and methods
			const sortable = {
				dragger: new Draggable(element, {
					onDragStart: downAction,
					onRelease: upAction,
					onDrag: dragAction,
					cursor: "inherit",
					type: "y"
				}),
				element: element,
				index: index,
				setIndex: setIndex
			};
			// gsap.set(element, { y: index * rowSize });
			function setIndex(index) {
				sortable.index = index;
				// Don't layout if you're dragging
				if (!dragger.isDragging) layout();
			}
			function downAction() {
				animation.play();
				this.update();
			}
			function dragAction() {
				// Calculate the current index based on element's position
				const index = clamp(Math.round(this.y / rowSize), 0, total - 1);
				if (index !== sortable.index) changeIndex(sortable, index);
			}
			function upAction() {
				animation.reverse();
				layout();
			}
			function layout() {
				gsap.to(element, { duration: 0.3, y: sortable.index * rowSize });
			}
			return sortable;
		}
		function changeIndex(item, to) {
			// Change position in array
			arrayMove(sortables, item.index, to);
			// Set index for each sortable
			const order = [];
			sortables.forEach((sortable, index) => {
				sortable.setIndex(index);
				const content = sortable.element.innerText;
				order.push(content);
			});
			document.getElementById("selection").innerHTML=order.toString();
		}
		// Changes an elements's position in array
		function arrayMove(array, from, to) {
			array.splice(to, 0, array.splice(from, 1)[0]);
		}
		// Clamps a value to a min/max
		function clamp(value, a, b) {
			return value < a ? a : value > b ? b : value;
		}
	}

 

Link to comment
Share on other sites

18 minutes ago, OSUblake said:

You are inspecting an element, which has similarly named event handlers, but those aren't the same events as Draggable. You should be inspecting what Draggable returns, i.e. dragger. The event handlers will be in the vars object.

 

 

I attached three screen shots:

  1. The es6 vars object, as you requested.
  2. The js vars object, for comparison. It appears to be identical.
  3. The js version of the screenshot posted earlier, showing the onDragStart event of element. It shows a function there. Since GSAP ultimately must set events on dom objects, element in this case, the screenshot I posted earlier may show a problem.

 

es6vars.jpg

jsvars.png

jsOnDragStart.jpg

Link to comment
Share on other sites

14 hours ago, OSUblake said:

Are you registering the plugin? If you're having issues, can make a demo CodeSandbox, which allows for imports and TypeScript?

I did register the plugin, I neglected to add that line in the code above. I worked on CodeSandbox:

https://codesandbox.io/s/little-star-jvkox

I don't know if the *.ts file is actually being called or if the sandbox is publicly viewable. The behavior is the same as I see locally, but I had to make some minor adjustments to get it to display the HTML and show the *.ts file without errors. There's still two issues involving this but I think they won't prevent compilation.

 

Contact me through codesandbox and I'll make you an editor if you're willing to explore this more. I suspect a problem exists with gsap's Draggable when used as a module.

Link to comment
Share on other sites

Hey @roman999

https://codesandbox.io/s/headless-fire-cvbtm?file=/src/index.ts

There's no real JS/GSAP issues here that I can see, just some setup errors. 

  • The 'src' directory was specified as root in your tsconfig but didn't follow that folder structure.
  • The ts file needed to be actually be called.
  • No need to import from 'dist' with ES Modules. Just import { Draggable } from "gsap/Draggable";
  • You'd also got rid of the container element and position absolute in the HTML and CSS which were kinda important. 


All seems to be working now!

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, Cassie said:

Hey @roman999

https://codesandbox.io/s/headless-fire-cvbtm?file=/src/index.ts

  • No need to import from 'dist' with ES Modules. Just import { Draggable } from "gsap/Draggable";

Thanks Cassie! I used the dist directory to dispose of this Jest error that occurs when using the import as you showed. Any idea what is going on? 

 

    /home/kev/control/programming/ts/quiz/node_modules/gsap/Draggable.js:16
    import { getGlobalMatrix, Matrix2D } from "./utils/matrix.js";
    ^^^^^^

    SyntaxError: Cannot use import statement outside a module

       5 | import {sort} from '../jsonOut';
       6 | import {gsap} from 'gsap';
    >  7 | import {Draggable} from "gsap/Draggable";
         | ^
       8 | export class Sort implements Exercise {
       9 |      inst="";
      10 |      ans=[];

      at Runtime.createScriptFromCode (node_modules/jest-runtime/build/index.js:1350:14)
      at Object.<anonymous> (src/ts/main/exercise/sort.ts:7:1)

 

Link to comment
Share on other sites

56 minutes ago, Cassie said:

Really not sure what you mean by 'the event listeners are not attaching'

Are you sure the JS is actually running? Can you console log through and work out where it's going wrong?

You can see what I mean in the screenshots I posted earlier in this thread. When I ran the sorter outside of my code, the ondragstart method on one of the list-items shows a proper value. When run inside my code, it shows _emptyFunc. If the sort code wasn't running, ondragstart would be null but I confirmed the code is running via browser Developer Tools's debugger. Draggable doesn't seem to be setting the list-itemsevent handlers properly in a certain context but I'm not sure what context to change. Hopefully a minimal demo will clarify things but it's going to be some work.

 

Cassie, you're being a great help, I really appreciate it.

Link to comment
Share on other sites

I'm afraid I don't understand how to debug the way you are - I still just use good old trusty console logs so the screenshots don't mean much to me. 😂

 

Your JS wasn't running at all in the example you popped over - I added a script tag to your HTML and also moved your files into a src folder. So maybe that's the issue. I'll try to help when you have a demo though. We'll get to the bottom of it!

Link to comment
Share on other sites

Yeah, I'm totally confused here as the demo Cassie provided works as expected. And again, you shouldn't be inspecting the element for events. Draggable's onDragStart is the not the same as the native one. GSAP overrides ondragstart with an empty function because we don't want to initiate native drag behavior.

https://developer.mozilla.org/en-US/docs/Web/API/Document/dragstart_event

 

 

  • Like 1
Link to comment
Share on other sites

  • Solution

The problem was a beginner's mistake I made to add another widget:

document.body.innerHTML = document.body.innerHTML + ...;

That apparently reset gsap's event handling code. Adding the proper way, document.createElement(...)  with document.body.append(...)  made the problem disappear.

 

I still don't understand why the DOM DnD event handlers were added in particular situations instead of emptyFunc() (which I think cannot be seen by logging since the DOM lacks the necessary tooling) but my immediate problem is resolved. @OSUblake and @Cassie, I appreciate your help.

  • Like 2
Link to comment
Share on other sites

1 hour ago, roman999 said:

The problem was a beginner's mistake I made to add another widget:

document.body.innerHTML = document.body.innerHTML + ...;

That apparently reset gsap's event handling code.

 

Oh yeah. Using innerHTML will create a brand new set elements, so all references to those elements will be lost. Instead of using innerHTML, you can use insertAdjacentHTML for the same effect, but it won't create new elements.

https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML

 

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...