Drag & Drop, Resize in React App

QueryPie Development #5: Implementing Query Tabs and a Result Panel

Thomas Jang
QueryPie
9 min readFeb 7, 2019

--

한국어- https://medium.com/p/fd6a61c80d89/

The second sprint is finished.

We had several days off during this Sprint because it fell in the timeline of Christmas and New Years. But having only 6 working days during the two-week Sprint period meant there were relatively few tasks to address. And since the number of tasks was small, it was less burdensome. It was also very comforting to think that if I didn’t have enough time to finish a task, I could address it over the holidays!

Sadly, these comforting circumstances didn’t last long.

The main Goal of this Sprint:

  1. Manage multiple tabs in the query panel
  2. Run queries in the Query Panel Editor to show results

Modeling tabs for the query panel with ReactDOM

We set key goals and started working on them. The issue at hand was how to manage the query panel with multiple tabs, which we needed to model prior to beginning development.

image 1. Switch-based DOM Structure

The structure shown above shows the status of the active tab by placing a tab bar at the top (or bottom) and labeling the tab. The inactive tabs are prepared and grayed out below, so the user can easily change the status of which tab is active by clicking on the label.

This is efficient when the contents of components is not in the same format because each tab can be selected and displayed individually. On the other hand, a major disadvantage is that as the number of tabs increases, WebView has to cope with the increasing number of DOM elements. These areas need to be carefully conceived since they might not be problems while developing but can become big issues later on.

image 2. Injection-based DOM Structure

Another way to think about it is to configure the Tab content panel as shown in Image 2 and inject the content of the tab as the active tab changes. It is more suitable when the contents of the tab doesn’t change much and can be predicted beforehand because it needs to be injected into a given structure. But a great advantage of the DOM Element is that it doesn’t grow much in file size.

Out of the two approaches, we eventually took the approach shown in Image 2. We thought the approach where the tab content was fixed and the number of tabs could increase significantly was more appropriate with QueryPie.

Add / Delete / Align Tab Labels

After setting up the DOM configuration, specifically Adding, Deleting, and Aligning of tab labels began to be implemented. Adding tabs allows the user to create a query panel model on QueryPanelStore and add it to the QueryPanelStore.panels.

QueryPie Panel Creation Code

However it was not that simple. I wanted to add a query panel named Untitled-{n} if an added query panel is not saved, similar to VSCode.

We needed to know the number of panels that were not saved in the query panel. So, we broke down the code writing process like this:

  1. Add a new Panel and at the same time label it ‘UntitleSeq 1’
  2. When the Panel with the ’n’ value equal to the storage value of ‘UntitleSeq’ is deleted, reduce the value of ‘UntitleSeq’ by 1
  3. When adding a new Panel, add 1 to the UntitleSeq value if the Panel with the same n value as UnitleSeq has not been deleted

The contents of the panel have been changed to check the state of the panel that needs to be saved and the contents of the panel can be saved to a file on your computer and opened as a .sql or .txt file.

There were so many issues that needed to be sorted out.

📌Considerations for Managing Tab Panels

  • Process unsaved panel counts when adding panels
  • Manage the status to see if the contents of the panel have changed
  • Save panel contents
  • Open file (not just import; check file content encoding type when loading file)
  • Scroll to the position of the active tab when the panel is added and the number of panels is high
  • Reorder panels by dragging them
  • Prompt user to save file if changes are made when closing a panel
Query Panel Tab Implementation View

Eventually, all of the above issues were resolved. The above figure is a screenshot of the developed tab panels. It looks so simple when taking a screen capture like this because the output of developed application is already out in the world. But when I think about how we started and how we struggled, I am so happy with the result and proud of every stroke of code. This was the satisfying feeling of developing.

I’ll skip over how we dealt with the other considerations because there are tons of solutions on the internet by talented people on how to address them. I’d rather focus on the UI issues which is my favorite area.

Drag & Drop Tab Panel:

I had a fear of working with drag & drop because I had little experience creating web applications with React. When I was developing with jQuery, I really hated the experience because I thought I had to go through it piece by piece, and working with the fragmented browser was torture. That’s why instead of using drag & drop, we usually used mousedown, mousemove and mouseup.

But this time, we only needed Chrome support for our WebView so drag & drop was a good option for us. More importantly, my development skills have risen in recent years so I wasn’t as fearful as before.

Tab JSX

It was very simple to write the code. I just gave the draggable attribute to JSX and added onDragStart, end, and over. Over and start were only used to identify the indexes in which the dragging started and the indexes in progresses. The most complex code in practice was dragEnd. Although it might seem complicated, it’s rather simple to implement.

I created a sortPanel action on the queryPanelStore and implemented a code that inserts the dragTabIndex into position by sending the dragTabIndex and the dragoverTabIndex at the end of the drag.

I used the splice API to send fromIndex items over to toIndex. There is a way to combine slices twice, but I think the splice API is a little cleaner.

In the code that changed the order of the array, we could write a single line of code by ‘returning’ the item that the splice deleted without creating a variable called movePanel. But our project was using mobx and MST, so we needed two more lines of code.

Drag and Drop in Object Panel

To output Query Execution results:

Once we solved the issue of managing the query panel, we had more important execution outputs: if the query execution result was recording data, the data had to be output into the data grid.

Below is an example of a multi-query run on SQLGate. Where most other query execution tools output only one result at a time, SQLGate provides the advantage of showing the results of multiples queries run in the same panel.

Result of multiple SQL queries run in SQLGate

In fact, Koreans like to see everything at a glance.

So even before development began, we decided that users should be able to stack multiple SQL results either side-by-side or above-below each other to view them comfortably.

Users may not know how much time developers spend or how much trouble they go through to accommodate these types of features. When we started implementing this everything at a glance method, unexpected issues began emerging again.

Issue 1. Unexpectedly Complex Data Structure ( 💀💀💀)

It‘’s simple to deliver query statements written in the Query Editor to the API.

Because each change in the status of the query editor resulted in a change to the queryPanel model, everything was done just by executing the following command when running the query:

queryPanelStore.activePanel.execute();

But the return result was beyond expectations.

The QueryPie back-end and middle-ware development team informed me of a very complex form of data structure, taking into account all the worst situations that could occur when the query is executed.

So parsing was used to analyze the sentence and make it into several sentences before query execution. Each request result data-sets was parallelized so that it could be processed several times before the query was executed.

As a seasoned developer, I don’t hate well-organized data structures. But when I came back to being a naive front-end developer for QueryPie, I couldn’t understand why I was giving myself such a challenging task.

Issue 2. Resize Query Results Panel ( 💀💀💀💀💀 )

I thought that I could easily show the results of the query executions, but adjusting the size of each query panel ended up being very bothersome.

  1. If the user does not resize the panel, resize the entire screen, or resize the ambient panel to change the size of the result set, the size must be automatically set to 1/n
  2. If a user adjusts one of the multiple results panels, the size of that panel should be maintained even if the other result sets are resized
  3. Components inside the result panel must be able to determine the size of the result panel (because the data grid components must be attached inside the component)
  4. Adjust the size of the result panel at the end of the result set so that the scroll position is appropriate

As we developed QueryPie, resizing other parts was developed in such a way that the position of the Resizer is determined by using the height and width value of the object to be resized.

Editor and Result Panel Code

So you can pass the value of resizerTop to the style.top property of the resizer. When developing this method, it is necessary to detect when a mousedown event occurs in a resizer, when to bind the mousemove and mouse down event to the window/document, and when to unbind it at the time when the Resizer movement ends.

In this case, the code for event binding/unbinding management and mouse position becomes dirty. So recently we have created a function called mouseEventSubscribe.

The mouseEventSubscribe is a simple function that runs the callBack function when mouse movement occurs. It has the advantage of being able to easily compare the location before and after the start of the EventSubscribe.

As mentioned earlier, it is easy to approach resizing with width or height values. After writing the development log, callback function processed throttle in the mouseEventSubscribe function to improve UI performances.

Resizing the Result Panel

Let’s go back to the issue of resizing the query result panel and look at the size of the result panel. Because it is necessary to resize vertically or horizontally stacked panels, if the Resizer is expressed as an absolute value, it is necessary to recalculate the position of each Resizer.

It is convenient for the Resizer to position the size of the panel to be changed without creating such unnecessary code. So the panel and the Resizer are printed out using the css flex so that the position is not set separately, and the relative changes from the position of the Resizer are measured once it’s moved.

Result Panel Code

Finally, when the Resizer moves out of the Scroll Container, scrolling is possible.

Resizing and Scrolling in Result Panel

Wrap-up:

At the end of the twists and turns, the second Sprint is over and the third Spring is underway. Every member of the QueryPie team is working hard every day, and all the CHEQUER crew including the design team, planning team, and marketing team. I am also doing my best to develop QueryPie and make it into something great.

In this development blog, I discussed the subject of drag and resizing. I apologize for not including more examples and reference materials in my article, but I’ve been documenting as I develop so I didn’t have time to look up references. The contents that I discussed today are being posted as a record of the ups and downs of our QueryPie development journey.

Until next time~

--

--