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

The second sprint is finished.

We had several days off during this Sprint because it fell in the timeline of Christmas and New Year. But having only six 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 job, 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 was how to manage the query panel with multiple tabs, which we needed to model before 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 it. 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 are not in the same format because each tab can be selected and displayed individually. On the other hand, a significant 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 tab's content as the active tab changes. It is more suitable when the tab's contents don't change much and can be predicted 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 method 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 status 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 .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 active tab position 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 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 wouldn't say I liked the experience because I thought I had to go through it piece by piece, and working with the fragmented browser was torture. 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.


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

Koreans like to see everything at a glance.

So even before development began, we decided that users should 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 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 query editor's status resulted in a change to the queryPanel model, everything was done just by executing the following command when running the query:


But the return result was beyond expectations.

The QueryPie back-end and middle-ware development team informed me of a 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 were 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 result panel size at the end of the result set so that the scroll position is appropriate

As we developed QueryPie, resizing other parts was developed so 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 required 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


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 our QueryPie development journey's ups and downs.

Until next time~