Posts tagged with 'Trello'
3 years ago

Wednesday CODE: Trello Remake, part 2, drag'n'drop


Continuing our series, we will now essentially write TIScipt code to make our Trello board interactive with drag'n'drop support! If you missed part 1, check it here.

Download source-code

Test DEMO with this link: (how to run it)

Drag-me to OmniFiddler and Run

Drag'n'drop in Sciter

Well, it's easier to make than to explain. I won't dive too deep in the implementation, instead, better to explain the logic behind it so you might be able to tweak and even reuse my drag'n'drop implementation.

I decided to code drag'n'drop from scratch. Sciter's SDK samples has a drag'n'drop library which you may want to resort as a more flexible and reusable approach. Check it at sciter-sdk-3/samples/drag-n-drop-manager/.

However, our Trello board drag'n'drop has so specific requirements that probably that library would not suffice or would make it a harder implementation. I recommend you to check ddm.tis/ddm2.tis and the samples as an option to your drag'n'drop scenario.

Check Trello2.html where is our final HTML code, here at GitHub.

And drag-n-drop.tis where I put the main script code, here at GitHub.

TIScript explained: element script prototype

Before I talk about drag'n'drop, you must understand Sciter's protoype mechanism that our demo uses to link script behavior to our HTML structure (also, you get a sense of development practices right out of the box).

Note that drag-n-drop.tis contains two behaviors: DndListBehavior handles drag'n'drop between lists (columns), and DndCardBehavior handles drag'n'drop between cards (tasks).

Well, behaviors are just a more organized way to set the code/logic-layer behind an element because you are declaring in CSS what element gets attached to what behavior, so you have all CSS cascading/priority rules applying.

Docs about behavios are here.

In CSS there are 3 mechanism that you can use to relate/attach a behavior to an element:

  • aspect: call a TIScript function with element set to 'this'
  • prototype: instantiate and attachs a TIScript class to the element
  • behavior: instantiate and attachs a native (C#, C++, Python, ..) behavior to the element

So yes, you can have a native behavior when you need the logic to be in the native side.

In our demo I am only using the prototype mechanism, see lines 81 and 140.

TIScript explained: drag'n'drop algorithm

For creating any kind of drag'n'drop support in Sciter, you will essentially resort to a standard sequence of handling user events and mouse capture. What is mouse capture?

When an object captures the mouse, all mouse related events are treated as if the object with mouse capture perform the event, even if the mouse pointer is over another object.

Simply put: when you capture the mouse, the element capturing it will receive all mouse events, even if cursor is outside the element box/area or even the application window. This is very usefull for drag'n'drop.

The basic sequence for any custom drag'n'drop implementation in Sciter is:

  • this.capture(#strict) and captured = true
  • Event.MOUSE_MOVE if captured==true
  • Event.MOUSE_UP if captured==true
  • this.capture(false) and captured = false

I'll explain each step and point the corresponding line of code:

  • Event.MOUSE_DRAG_REQUEST line 57: logic to detect when to start a drag'n'drop operation; this event happens when user clicks and holds mouse hover our element, and move it a little bit (which does not happens in a simple click event)

  • this.capture(#strict) line 69 and captured = true line 70: to start our drag'n'drop, we capture the mouse making all mouse messages/events to be delivered to 'this' element; also we need to set a global variable so we know that a drag'n'drop is in progress and we should handle MOUSE_MOVE events

  • Event.MOUSE_MOVE if captured==true line 25: here we put all the logic of what happens while the object is beeing dragged

  • Event.MOUSE_UP if captured==true line 19 here we put all the logic of what happens when user releases the mouse button / drops the object

  • captured = false line 21 and this.capture(false) line 23: with our drag'n'drop operation finished, we must manually release mouse capture (else mouse interaction behaves very oddly) and unset our global variable

Next time

Our next installment is exciting. I'll cover Sciter C# native implementation. Stay tunned.

3 years ago

Wednesday CODE: Trello Remake, part 1


Trello is a kanban-like task management tool, the one which I personally use. In this Wednesday CODE installment we will be remaking its UI.

I won't be making another TODO list system, the world is already full of then.

Instead, I will focus on what I think are the most clever UI details of this tool. To me, it is distinctive because of the minimalist layout, and how you can nicely drag'n'drop the tasks between the columns. If you want then, you might easily extend it to add a complete TODO list support.

The main goal is to achieve a functional UI with drag'n'drop where you can rearrange the columns, and also the tasks inside each column. So it is all about frontend implementation in Sciter. I will divide it in 2 posts:

  1. This one deals with implementing every visual aspect required, that is, we will write all HTML and CSS code necessary, and any script required to dynamically construct HTML.
  2. In the second part we make the UI interactive by adding the drag'n'drop support through scripting.

Download source-code / GitHub

Test DEMO with this link: (how to run it)

Drag-me to OmniFiddler and Run

HTML explained: templates

The file Trello.html contains all our HTML code, you should check it at GitHub.

Our Trello board has lists (columns) and cards/tasks (itens inside each column). Lists are arranged horizontally, cards vertically.

In the HTML approach I am using, I declare two HTML 'templates', #tpl-list and #tpl-card, one for each of these kind of item. I use this HTML template as the basis to create every new list/card in order to populate the page with lists and cards:

<div #tpl-list style="display: none">
		<div .list-header>
			<span />
			<i .ft.ft-dot-3 .dark-hover />

		<ul />

		<div .list-newcard.dark-hover>Add a card...</div>

<div #tpl-card style="display: none">
		<div .content>
			<img .card-cover />
			<div .card-labels />
			<div .card-title />
			<div .badges />
		<div .card-edit>
			<i .ft.ft-pencil.dark-hover />

The parent div is hidden so our template does not appears. What really matters is the child section/li element which is actually our template, which we will clone() in script in order to create every new item. I'll cover the code behind below.

HTML explained: HTML/CSS subtleties

Sciter's HTML/CSS support has some subtleties which are not supported by standard browsers. It is standard HTML, until you notice some subtleties. Notice how we declared the <button> element:

<button #btn-add-list.std-btn>

Instead of declaring the element ID with an id="btn-add-list" attribute, as in <button id="btn-add-list" />, we can simply say #btn-add-list, as you would write an ID selector in CSS.

You can do the same for the class attribute, so <button class="btn btn-colored" /> can be written as <button .btn.btn-colored />. Pretty clever method for writing the ID or classes of an element by using the same syntax from CSS.

Sciter supports more HTML shortcuts, see here.

CSS explained: 'flow' layout

Besides basic CSS code for making our Trello UI, for achieving our Trello drag'n'drop goal, we must set a proper flow to our layout.

For that, it is essential to understand the 'flow' CSS property. It is THE major method for construting layouts in Sciter. Essentially it is the equivalent of CSS3 Flexbox layouting, but it has different naming and behavior (docs here).

The 'flow' property dictates how children of the element will be positioned inside it. For our demo, we only use the following 'flow' modes:

  • horizontal: position elements horizontally, from left-to-right (the reverse in RTL layouts)
  • vertical: position elements vertically, from top-to-bottom
  • stack: position elements one above the other

The 'vertical' value we use says to layout element vertically, one bellow the other, in rows, no matter if its inline or a block element. The 'horizontal' value, of course, place elements horizontally, one beside the other. You use it, for example, to create toolbars.

One aspect of the 'horizontal' flow is that you can reverse the standard left-to-right direction to right-to-left:

	flow: horizontal;
	direction: rtl;

So 'vertical' and 'horizontal' are just the standard method to lay elements linearly (a linear layout).

What's the point with flow: stack?

The arrangement achieved with flow: stack is simply a stack of layers, as we have with visual layers in Photoshop.

It is an alternative and better approach than using position: absolute as it allows to set the x/y alignment of its childs by using horizontal-align and vertical-align, which is much better than using CSS hacks.

TIScript: Populating the page with JSON data

Data to fill our board will definitely come from a dynamic/database source, so we need to resort to scripting.

As a start, I hard-coded the JSON data in script. The data variable holds and array with an object for every column of our board, and each of these object holds a cards field with an array of the cards inside it:

var data = [
		board-name: "Basics",
		cards: [
			{ t: "Welcome to Trello!" },
			{ t: "This is a card." },
			{ t: "Click on a card to see what's behind it." },
			{ t: "You can attach pictures and files...", a: "taco.png" },
			{ t: "... any kind of hyperlink ..." },
			{ t: "... or checklists." }

Then I simply put the code that populates our page from this JSON data. It is simply dynamically creating the HTML that displays it to user:

function Setup()
	var el_main = $(main);
	for(var s in data)
		var el_section = self#tpl-list[0].clone();
		el_main.insert(el_section, el_main.length - 2);
		el_section.$(.list-header span).text = s#board-name;

		for(var c in
			var el_card = self#tpl-card[0].clone();
			el_card.$(.card-title).text = c.t;
				el_card.$(.card-cover).@#src = c.a;

TIScript: horizontall scrolling

The main area is supposed to overflow horizontally, that is, scrollbars must appear when content can't fit the window's width. In CSS we declare:

html { overflow: hidden; }
body { overflow-x: auto; }

And for beeing clever, we let the user use the mousel wheel to horizontally scroll it:

$(body).subscribe(function(evt) {
	this.scrollTo(this.scroll(#left)-10*evt.wheelDelta, 0);
}, Event.MOUSE, Event.MOUSE_WHEEL);

By default, Sciter only supports vertically scrolling the page with the wheel.

Next time

Next time we’ll look at the drag'n'drop implementation:

  • Lists drag'n'drop
  • Cards drag'n'drop

The best way to learn is by playing. If you have some Sciter sample and would like it to be showcased here, be my guest!