diff --git a/web/package.json b/web/package.json
index d708b05e4..f2ac16703 100644
--- a/web/package.json
+++ b/web/package.json
@@ -116,7 +116,6 @@
     "closest": "^0.0.1",
     "codemirror": "^5.59.2",
     "context-menu": "^2.0.0",
-    "copy-to-clipboard": "^3.3.1",
     "css-loader": "^5.0.1",
     "cssnano": "^5.0.2",
     "dagre": "^0.8.4",
@@ -153,8 +152,10 @@
     "react": "^17.0.1",
     "react-aspen": "^1.1.0",
     "react-checkbox-tree": "^1.7.2",
+    "react-data-grid": "^7.0.0-beta.11",
     "react-dom": "^17.0.1",
     "react-draggable": "^4.4.4",
+    "react-leaflet": "^3.2.2",
     "react-rnd": "^10.3.5",
     "react-router-dom": "^6.2.2",
     "react-select": "^4.2.1",
diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py
index 517232210..4a7e18eb0 100644
--- a/web/pgadmin/browser/__init__.py
+++ b/web/pgadmin/browser/__init__.py
@@ -54,7 +54,8 @@ from pgadmin.utils.master_password import validate_master_password, \
     set_crypt_key, process_masterpass_disabled
 from pgadmin.model import User
 from pgadmin.utils.constants import MIMETYPE_APP_JS, PGADMIN_NODE,\
-    INTERNAL, KERBEROS, LDAP, QT_DEFAULT_PLACEHOLDER, OAUTH2, WEBSERVER
+    INTERNAL, KERBEROS, LDAP, QT_DEFAULT_PLACEHOLDER, OAUTH2, WEBSERVER,\
+    VW_EDT_DEFAULT_PLACEHOLDER
 from pgadmin.authenticate import AuthSourceManager
 
 try:
@@ -854,6 +855,7 @@ def utils():
             logout_url=get_logout_url(),
             platform=sys.platform,
             qt_default_placeholder=QT_DEFAULT_PLACEHOLDER,
+            vw_edt_default_placeholder=VW_EDT_DEFAULT_PLACEHOLDER,
             enable_psql=config.ENABLE_PSQL
         ),
         200, {'Content-Type': MIMETYPE_APP_JS})
diff --git a/web/pgadmin/browser/register_browser_preferences.py b/web/pgadmin/browser/register_browser_preferences.py
index 0b0224b3d..5d3482be4 100644
--- a/web/pgadmin/browser/register_browser_preferences.py
+++ b/web/pgadmin/browser/register_browser_preferences.py
@@ -10,7 +10,7 @@ import sys
 from flask_babel import gettext
 from pgadmin.utils.constants import PREF_LABEL_DISPLAY,\
     PREF_LABEL_KEYBOARD_SHORTCUTS, PREF_LABEL_TABS_SETTINGS, \
-    PREF_LABEL_OPTIONS, QT_DEFAULT_PLACEHOLDER
+    PREF_LABEL_OPTIONS, QT_DEFAULT_PLACEHOLDER, VW_EDT_DEFAULT_PLACEHOLDER
 from flask_security import current_user
 import config
 
@@ -483,7 +483,7 @@ def register_browser_preferences(self):
     self.ve_edt_tab_title = self.preference.register(
         'tab_settings', 'vw_edt_tab_title_placeholder',
         gettext("View/Edit data tab title"),
-        'text', '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%',
+        'text', VW_EDT_DEFAULT_PLACEHOLDER,
         category_label=PREF_LABEL_DISPLAY,
         help_str=gettext(
             'Supported placeholders are %SCHEMA%, %TABLE%, %DATABASE%, '
diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py
index 509f6cf92..a1cc7e495 100644
--- a/web/pgadmin/browser/server_groups/servers/__init__.py
+++ b/web/pgadmin/browser/server_groups/servers/__init__.py
@@ -1284,7 +1284,7 @@ class ServerNode(PGChildNodeView):
             }
         )
 
-    def connect(self, gid, sid, user_name=None):
+    def connect(self, gid, sid, user_name=None, resp_json=False):
         """
         Connect the Server and return the connection object.
         Verification Process before Connection:
@@ -1331,12 +1331,15 @@ class ServerNode(PGChildNodeView):
         else:
             return unauthorized(gettext(UNAUTH_REQ))
 
-        data = {}
+        data = None
         if request.form:
             data = request.form
         elif request.data:
             data = json.loads(request.data, encoding='utf-8')
 
+        if data is None:
+            data = {}
+
         password = None
         passfile = None
         tunnel_password = None
@@ -1408,7 +1411,7 @@ class ServerNode(PGChildNodeView):
         if prompt_password or prompt_tunnel_password:
             return self.get_response_for_password(server, 428, prompt_password,
                                                   prompt_tunnel_password,
-                                                  user=user_name)
+                                                  user=user_name, resp_json=resp_json)
 
         status = True
         try:
@@ -1423,7 +1426,8 @@ class ServerNode(PGChildNodeView):
         except Exception as e:
             current_app.logger.exception(e)
             return self.get_response_for_password(
-                server, 401, True, True, getattr(e, 'message', str(e)))
+                server, 401, True, True, getattr(e, 'message', str(e)),
+                resp_json=resp_json)
 
         if not status:
 
@@ -1435,7 +1439,7 @@ class ServerNode(PGChildNodeView):
                 return internal_server_error(errmsg)
 
             return self.get_response_for_password(server, 401, True,
-                                                  True, errmsg)
+                                                  True, errmsg, resp_json=resp_json)
         else:
             if save_password and config.ALLOW_SAVE_PASSWORD:
                 try:
@@ -1859,47 +1863,54 @@ class ServerNode(PGChildNodeView):
 
     def get_response_for_password(self, server, status, prompt_password=False,
                                   prompt_tunnel_password=False, errmsg=None,
-                                  user=None):
+                                  user=None, resp_json=False):
 
         if server.use_ssh_tunnel:
+            data = {
+                "server_label": server.name,
+                "username": server.username,
+                "tunnel_username": server.tunnel_username,
+                "tunnel_host": server.tunnel_host,
+                "tunnel_identity_file": server.tunnel_identity_file,
+                "errmsg": errmsg,
+                "service": server.service,
+                "prompt_tunnel_password": prompt_tunnel_password,
+                "prompt_password": prompt_password,
+                "allow_save_password": True if
+                    config.ALLOW_SAVE_PASSWORD and
+                    session['allow_save_password'] else False,
+                "allow_save_tunnel_password": True if
+                    config.ALLOW_SAVE_TUNNEL_PASSWORD and
+                    session['allow_save_password'] else False
+            }
             return make_json_response(
                 success=0,
                 status=status,
                 result=render_template(
                     'servers/tunnel_password.html',
-                    server_label=server.name,
-                    username=server.username,
-                    tunnel_username=server.tunnel_username,
-                    tunnel_host=server.tunnel_host,
-                    tunnel_identity_file=server.tunnel_identity_file,
-                    errmsg=errmsg,
                     _=gettext,
-                    service=server.service,
-                    prompt_tunnel_password=prompt_tunnel_password,
-                    prompt_password=prompt_password,
-                    allow_save_password=True if
-                    config.ALLOW_SAVE_PASSWORD and
-                    session['allow_save_password'] else False,
-                    allow_save_tunnel_password=True if
-                    config.ALLOW_SAVE_TUNNEL_PASSWORD and
-                    session['allow_save_password'] else False
-                )
+                    **data,
+                ) if not resp_json else data
             )
         else:
+            data = {
+                "server_label": server.name,
+                "username": server.username,
+                "errmsg": errmsg,
+                "service": server.service,
+                "prompt_password": True,
+                "allow_save_password": True if
+                    config.ALLOW_SAVE_PASSWORD and
+                    session['allow_save_password'] else False,
+            }
             return make_json_response(
                 success=0,
                 status=status,
                 result=render_template(
                     'servers/password.html',
-                    server_label=server.name,
-                    username=user if user else server.username,
-                    errmsg=errmsg,
-                    service=server.service,
                     _=gettext,
-                    allow_save_password=True if
-                    config.ALLOW_SAVE_PASSWORD and
-                    session['allow_save_password'] else False,
-                )
+                    **data
+                ) if not resp_json else data
             )
 
     def clear_saved_password(self, gid, sid):
diff --git a/web/pgadmin/browser/static/js/ConnectServerContent.jsx b/web/pgadmin/browser/static/js/ConnectServerContent.jsx
new file mode 100644
index 000000000..8bd952f4c
--- /dev/null
+++ b/web/pgadmin/browser/static/js/ConnectServerContent.jsx
@@ -0,0 +1,103 @@
+import React, { useState } from 'react';
+import gettext from 'sources/gettext';
+import { Box } from '@material-ui/core';
+import { DefaultButton, PrimaryButton } from '../../../static/js/components/Buttons';
+import CloseIcon from '@material-ui/icons/CloseRounded';
+import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
+import PropTypes from 'prop-types';
+import { useModalStyles } from '../../../static/js/helpers/ModalProvider';
+import { FormFooterMessage, InputCheckbox, InputText, MESSAGE_TYPE } from '../../../static/js/components/FormComponents';
+
+export default function ConnectServerContent({closeModal, data, onOK}) {
+  const classes = useModalStyles();
+  const [formData, setFormData] = useState({
+    tunnel_password: '',
+    save_tunnel_password: false,
+    password: '',
+    save_password: false,
+  });
+
+  const onTextChange = (e, id) => {
+    let val = e;
+    if(e && e.target) {
+      val = e.target.value;
+    }
+    setFormData((prev)=>({...prev, [id]: val}));
+  };
+
+  if(!data) {
+    return <>No data</>;
+  }
+
+  return (
+    <Box display="flex" flexDirection="column" height="100%">
+      <Box flexGrow="1" p={2}>
+        {data.prompt_tunnel_password && <>
+          <Box>
+            <span style={{fontWeight: 'bold'}}>
+              {data.tunnel_identity_file ?
+                gettext('Please enter the SSH Tunnel password for the identity file \'%s\' to connect the server "%s"', data.tunnel_identity_file, data.tunnel_host)
+                : gettext('Please enter the SSH Tunnel password for the user \'%s\' to connect the server "%s"', data.tunnel_username, data.tunnel_host)
+              }
+            </span>
+          </Box>
+          <Box marginTop='12px'>
+            <InputText type="password" value={formData['tunnel_password']} maxLength={null}
+              onChange={(e)=>onTextChange(e, 'tunnel_password')} autoFocus />
+          </Box>
+          <Box marginTop='12px'>
+            <InputCheckbox controlProps={{label: gettext('Save Password')}} value={formData['save_tunnel_password']}
+              onChange={(e)=>onTextChange(e.target.checked, 'save_tunnel_password')} disabled={!data.allow_save_tunnel_password} />
+          </Box>
+        </>}
+        {data.prompt_password && <>
+          <Box>
+            <span style={{fontWeight: 'bold'}}>
+              {data.username ?
+                gettext('Please enter the password for the user \'%s\' to connect the server - "%s"', data.username, data.server_label)
+                : gettext('Please enter the password for the user to connect the server - "%s"', data.server_label)
+              }
+            </span>
+          </Box>
+          <Box marginTop='12px'>
+            <InputText type="password" value={formData['password']} maxLength={null}
+              onChange={(e)=>onTextChange(e, 'password')} autoFocus />
+          </Box>
+          <Box marginTop='12px'>
+            <InputCheckbox controlProps={{label: gettext('Save Password')}} value={formData['save_password']}
+              onChange={(e)=>onTextChange(e.target.checked, 'save_password')} disabled={!data.allow_save_password} />
+          </Box>
+        </>}
+        <FormFooterMessage type={MESSAGE_TYPE.ERROR} message={data.errmsg} closable={false} style={{
+          position: 'unset', padding: '12px 0px 0px'
+        }}/>
+      </Box>
+      <Box className={classes.footer}>
+        <DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
+          closeModal();
+        }} >{gettext('Cancel')}</DefaultButton>
+        <PrimaryButton data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
+          let postFormData = new FormData();
+          if(data.prompt_tunnel_password) {
+            postFormData.append('tunnel_password', formData.tunnel_password);
+            formData.save_tunnel_password &&
+              postFormData.append('save_tunnel_password', formData.save_tunnel_password);
+          }
+          if(data.prompt_password) {
+            postFormData.append('password', formData.password);
+            formData.save_password &&
+              postFormData.append('save_password', formData.save_password);
+          }
+          onOK?.(postFormData);
+          closeModal();
+        }} >{gettext('OK')}</PrimaryButton>
+      </Box>
+    </Box>
+  );
+}
+
+ConnectServerContent.propTypes = {
+  closeModal: PropTypes.func,
+  data: PropTypes.object,
+  onOK: PropTypes.func
+};
diff --git a/web/pgadmin/browser/static/js/collection.js b/web/pgadmin/browser/static/js/collection.js
index 78760632d..9b894c9b6 100644
--- a/web/pgadmin/browser/static/js/collection.js
+++ b/web/pgadmin/browser/static/js/collection.js
@@ -119,8 +119,8 @@ define([
         );
       },
       show_query_tool: function() {
-        if(pgAdmin.DataGrid) {
-          pgAdmin.DataGrid.show_query_tool('', pgAdmin.Browser.tree.selected());
+        if(pgAdmin.Tools.SQLEditor) {
+          pgAdmin.Tools.SQLEditor.showQueryTool('', pgAdmin.Browser.tree.selected());
         }
       },
       show_search_objects: function() {
diff --git a/web/pgadmin/browser/static/js/keyboard.js b/web/pgadmin/browser/static/js/keyboard.js
index 989f8bb5b..1aa88c231 100644
--- a/web/pgadmin/browser/static/js/keyboard.js
+++ b/web/pgadmin/browser/static/js/keyboard.js
@@ -253,7 +253,7 @@ _.extend(pgBrowser.keyboardNavigation, {
       return;
 
     // Call data grid method to render query tool
-    pgAdmin.DataGrid.show_query_tool('', tree.i);
+    pgAdmin.Tools.SQLEditor.showQueryTool('', tree.i);
   },
   bindSubMenuViewData: function() {
     const tree = this.getTreeDetails();
@@ -262,7 +262,7 @@ _.extend(pgBrowser.keyboardNavigation, {
       return;
 
     // Call data grid method to render view data
-    pgAdmin.DataGrid.show_data_grid({'mnuid': 1}, tree.i);
+    pgAdmin.Tools.SQLEditor.showViewData({'mnuid': 1}, tree.i);
   },
   bindSubMenuSearchObjects: function() {
     const tree = this.getTreeDetails();
diff --git a/web/pgadmin/browser/static/js/node.js b/web/pgadmin/browser/static/js/node.js
index 9e5244b7b..4b28eeb98 100644
--- a/web/pgadmin/browser/static/js/node.js
+++ b/web/pgadmin/browser/static/js/node.js
@@ -975,7 +975,7 @@ define('pgadmin.browser.node', [
           sql_url = 'sql';
         }
         // Open data grid & pass the URL for fetching
-        pgAdmin.DataGrid.show_query_tool(
+        pgAdmin.Tools.SQLEditor.showQueryTool(
           obj.generate_url(i, sql_url, d, true),
           i, scriptType
         );
@@ -1001,7 +1001,7 @@ define('pgadmin.browser.node', [
           };
           pgBrowser.Node.callbacks.show_script(data);
         }else{
-          pgAdmin.DataGrid.show_query_tool('', i);
+          pgAdmin.Tools.SQLEditor.showQueryTool('', i);
         }
       },
 
diff --git a/web/pgadmin/browser/static/js/preferences.js b/web/pgadmin/browser/static/js/preferences.js
index 600027ac4..3f1aab16b 100644
--- a/web/pgadmin/browser/static/js/preferences.js
+++ b/web/pgadmin/browser/static/js/preferences.js
@@ -112,6 +112,10 @@ _.extend(pgBrowser, {
     }, 500);
   },
 
+  triggerPreferencesChange: function(moduleChanged) {
+    $.event.trigger('prefchange:'+moduleChanged);
+  },
+
   reflectPreferences: function(module) {
     let obj = this;
 
@@ -130,7 +134,13 @@ _.extend(pgBrowser, {
   },
 
   onPreferencesChange: function(module, eventHandler) {
-    $(pgWindow).on('prefchange:'+module, function(event) {
+    let eventWindow = pgWindow;
+    if (window.location === window.parent?.location ) {
+      // The page is in a new tab
+      eventWindow = window;
+    }
+
+    $(eventWindow).on('prefchange:'+module, function(event) {
       eventHandler(event);
     });
   },
diff --git a/web/pgadmin/browser/static/js/toolbar.js b/web/pgadmin/browser/static/js/toolbar.js
index b97165f2d..fc915026e 100644
--- a/web/pgadmin/browser/static/js/toolbar.js
+++ b/web/pgadmin/browser/static/js/toolbar.js
@@ -112,11 +112,11 @@ export function initializeToolbar(panel, wcDocker) {
   // Listen on button click event.
   panel.on(wcDocker.EVENT.BUTTON, function(data) {
     if ('name' in data && data.name === gettext('Query Tool'))
-      pgAdmin.DataGrid.show_query_tool('', pgAdmin.Browser.tree.selected());
+      pgAdmin.Tools.SQLEditor.showQueryTool('', pgAdmin.Browser.tree.selected());
     else if ('name' in data && data.name === gettext('View Data'))
-      pgAdmin.DataGrid.show_data_grid({mnuid: 3}, pgAdmin.Browser.tree.selected());
+      pgAdmin.Tools.SQLEditor.showViewData({mnuid: 3}, pgAdmin.Browser.tree.selected());
     else if ('name' in data && data.name === gettext('Filtered Rows'))
-      pgAdmin.DataGrid.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected());
+      pgAdmin.Tools.SQLEditor.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected());
     else if ('name' in data && data.name === gettext('Search objects'))
       pgAdmin.SearchObjects.show_search_objects('', pgAdmin.Browser.tree.selected());
     else if ('name' in data && data.name === gettext('PSQL Tool')){
diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js
index 6ce87bec0..6c971e7c6 100644
--- a/web/pgadmin/browser/templates/browser/js/utils.js
+++ b/web/pgadmin/browser/templates/browser/js/utils.js
@@ -56,6 +56,7 @@ define('pgadmin.browser.utils',
   pgAdmin['enable_psql'] =  '{{enable_psql}}' == 'True';
   pgAdmin['platform'] =  '{{platform}}';
   pgAdmin['qt_default_placeholder'] = '{{qt_default_placeholder}}'
+  pgAdmin['vw_edt_default_placeholder'] = '{{vw_edt_default_placeholder}}'
 
   /* GET Binary Path Browse config */
   pgAdmin['enable_binary_path_browsing'] = '{{ current_app.config.get('ENABLE_BINARY_PATH_BROWSING') }}' == 'True';
diff --git a/web/pgadmin/misc/static/explain/js/svg_downloader.js b/web/pgadmin/misc/static/explain/js/svg_downloader.js
index 10a3b0d4d..bf1728a84 100644
--- a/web/pgadmin/misc/static/explain/js/svg_downloader.js
+++ b/web/pgadmin/misc/static/explain/js/svg_downloader.js
@@ -14,7 +14,7 @@ let svgDownloader = {
   },
 
   downloadSVG: function(content, fileName)  {
-    // Safari xlink NS issue fix
+    // Safari xlink NS issue fixblobURL
     content = content.replace(/NS\d+:href/gi, 'xlink:href');
 
     var svgURL = this.blobURL(content, 'image/svg+xml');
diff --git a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx
index e3c912a1a..4e1383aed 100644
--- a/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx
+++ b/web/pgadmin/preferences/static/js/components/PreferencesComponent.jsx
@@ -26,7 +26,7 @@ import pgAdmin from 'sources/pgadmin';
 import { DefaultButton, PgIconButton, PrimaryButton } from '../../../../static/js/components/Buttons';
 import BaseUISchema from 'sources/SchemaView/base_schema.ui';
 import { getBinaryPathSchema } from '../../../../browser/server_groups/servers/static/js/binary_path.ui';
-import { _set_dynamic_tab } from '../../../../tools/datagrid/static/js/show_query_tool';
+import { _set_dynamic_tab } from '../../../../tools/sqleditor/static/js/show_query_tool';
 
 class PreferencesSchema extends BaseUISchema {
   constructor(initValues = {}, schemaFields = []) {
@@ -289,7 +289,7 @@ export default function PreferencesComponent({ ...props }) {
         if(_el.name == 'column_data_auto_resize') {
           size_control_id = _el.id;
         }
-        
+
       });
       element.disabled = (state) => {
         return state[size_control_id] != 'by_data';
@@ -343,7 +343,7 @@ export default function PreferencesComponent({ ...props }) {
     }
 
     if (note && note.length > 0) {
-      //Add Note for Nodes 
+      //Add Note for Nodes
       preferencesData.push(
         {
           id: _.uniqueId('note') + subNode.id,
diff --git a/web/pgadmin/settings/__init__.py b/web/pgadmin/settings/__init__.py
index 371b951f8..f83412806 100644
--- a/web/pgadmin/settings/__init__.py
+++ b/web/pgadmin/settings/__init__.py
@@ -122,7 +122,7 @@ def store(setting=None, value=None):
             store_setting(setting, value)
     except Exception as e:
         success = 0
-        errormsg = e.message
+        errormsg = str(e)
 
     try:
         info = traceback.format_exc()
@@ -141,12 +141,21 @@ def reset_layout():
     """Reset configuration setting"""
 
     try:
-        db.session.query(Setting) \
-            .filter(Setting.user_id == current_user.id)\
-            .filter((Setting.setting == 'Browser/Layout') |
-                    (Setting.setting == 'SQLEditor/Layout') |
-                    (Setting.setting == 'Debugger/Layout'))\
-            .delete()
+        if request.params['setting'] in [
+                'Browser/Layout', 'SQLEditor/Layout', 'Debugger/Layout']:
+            db.session.query(Setting) \
+                .filter(Setting.user_id == current_user.id) \
+                .filter((Setting.setting == 'Browser/Layout') |
+                        (Setting.setting == 'SQLEditor/Layout') |
+                        (Setting.setting == 'Debugger/Layout')) \
+                .delete()
+        else:
+            db.session.query(Setting) \
+                .filter(Setting.user_id == current_user.id)\
+                .filter((Setting.setting == 'Browser/Layout') |
+                        (Setting.setting == 'SQLEditor/Layout') |
+                        (Setting.setting == 'Debugger/Layout'))\
+                .delete()
 
         db.session.commit()
     except Exception as e:
diff --git a/web/pgadmin/static/bundle/app.js b/web/pgadmin/static/bundle/app.js
index fc35da653..cbf89aee4 100644
--- a/web/pgadmin/static/bundle/app.js
+++ b/web/pgadmin/static/bundle/app.js
@@ -8,7 +8,7 @@
 //////////////////////////////////////////////////////////////
 
 define('app', [
-  'sources/pgadmin', 'bundled_browser', 'pgadmin.datagrid',
+  'sources/pgadmin', 'bundled_browser',
 ], function(pgAdmin) {
   var initializeModules = function(Object) {
     for (var key in Object) {
diff --git a/web/pgadmin/static/js/Explain/Analysis.jsx b/web/pgadmin/static/js/Explain/Analysis.jsx
new file mode 100644
index 000000000..fa822b156
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/Analysis.jsx
@@ -0,0 +1,215 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/styles';
+import React from 'react';
+import clsx from 'clsx';
+import _ from 'lodash';
+import ArrowRightAltIcon from '@material-ui/icons/ArrowRightAlt';
+import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
+import HTMLReactParse from 'html-react-parser';
+import { commonTableStyles } from '../Theme';
+import PropTypes from 'prop-types';
+
+const useStyles = makeStyles((theme)=>({
+  collapsible: {
+    cursor: 'pointer',
+  },
+  collapseParent: {
+    borderBottom: '2px dashed '+theme.palette.primary.main,
+  },
+  level2: {
+    backgroundColor: '#fff',
+    color: '#000',
+  },
+  level3: {
+    backgroundColor: '#fff',
+    color: '#000',
+  },
+  level4: {
+    backgroundColor: '#fff',
+    color: '#000',
+  },
+}));
+
+function getRowClassname(data, collapseParent) {
+  const classes = useStyles();
+  let className = [];
+  if(data['Plans']?.length > 0) {
+    className.push(classes.collapsible);
+  }
+  if(!_.isEmpty(data['exclusive_flag'])) {
+    className.push(classes['level'+data['exclusive_flag']]);
+  }
+  if(!_.isEmpty(data['inclusive_flag'])) {
+    className.push(classes['level'+data['inclusive_flag']]);
+  }
+  if(!_.isEmpty(data['rowsx_flag'])) {
+    className.push(classes['level'+data['rowsx_flag']]);
+  }
+  if(collapseParent) {
+    className.push(classes.collapseParent);
+  }
+  return className;
+}
+
+function NodeText({displayText, extraInfo}) {
+  return (
+    <>
+      <ArrowRightAltIcon fontSize="small" /> {displayText}
+      {extraInfo?.length > 0 && <ul>
+        {extraInfo.map((item, i)=>{
+          return <li key={i}>{HTMLReactParse(item)}</li>;
+        })}
+      </ul>}
+    </>);
+}
+NodeText.propTypes = {
+  displayText: PropTypes.string,
+  extraInfo: PropTypes.array,
+};
+
+function ExplainRow({row, show, activeExId, setActiveExId, collapsedExId, toggleCollapseExId}) {
+  let data = row['data'];
+  const exId = `pga_ex_${data['level'].join('_')}`;
+  const parentExId = `pga_ex_${data['parent_node']}`;
+  const collapsed = collapsedExId.findIndex((v)=>parentExId.startsWith(v)) > -1;
+  const className = getRowClassname(data, collapsedExId.indexOf(exId) > -1);
+  let onRowClick = (e)=>{
+    toggleCollapseExId(e.currentTarget.getAttribute('data-ex-id'), data['Plans']?.length);
+  };
+
+  return (
+    <tr onMouseEnter={(e)=>{setActiveExId(e.currentTarget.getAttribute('data-ex-id'));}}
+      onMouseLeave={()=>{setActiveExId(null);}}
+      className={clsx(className)} data-parent={parentExId}
+      data-ex-id={`pga_ex_${data['level'].join('_')}`}
+      style={collapsed ? {display: 'none'} : {}}
+      onClick={onRowClick}>
+      <td>
+        <FiberManualRecordIcon fontSize="small" style={{visibility: activeExId==parentExId ? 'visible' : 'hidden'}} />
+      </td>
+      <td>{data['_serial']}.</td>
+      <td style={{paddingLeft: data['level'].length*30+'px'}} title={row['tooltip_text']}>
+        <NodeText displayText={row['display_text']} extraInfo={row['node_extra_info']} />
+      </td>
+      <td style={show.show_timings ? {} : {display: 'none'}}>
+        {data['exclusive'] && (data['exclusive']+' ms')}
+      </td>
+      <td style={show.show_timings ? {} : {display: 'none'}}>
+        {data['inclusive'] && (data['inclusive']+' ms')}
+      </td>
+      <td style={{display: 'none'}}>{!_.isUndefined(data['rowsx_flag'])
+        && (data['rowsx_direction'] == 'positive' ? '&uarr;' : '&darr;')
+      }</td>
+      <td style={show.show_rowsx ? {} : {display: 'none'}}>
+        {data['rowsx']}
+      </td>
+      <td style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
+        {data['Actual Rows']}
+      </td>
+      <td style={(show.show_rowsx || show.show_plan_rows) ? {} : {display: 'none'}}>
+        {data['Plan Rows']}
+      </td>
+      <td style={(show.show_rowsx || show.show_rows) ? {} : {display: 'none'}}>
+        {data['Actual Loops']}
+      </td>
+    </tr>
+  );
+}
+ExplainRow.propTypes = {
+  row: PropTypes.shape({
+    data: PropTypes.shape({
+      Plans: PropTypes.array,
+      level: PropTypes.number,
+      _serial: PropTypes.number,
+      parent_node: PropTypes.number,
+      exclusive: PropTypes.number,
+      inclusive: PropTypes.number,
+      rowsx_direction: PropTypes.string,
+      rowsx: PropTypes.number,
+      rowsx_flag: PropTypes.number,
+      'Actual Rows': PropTypes.number,
+      'Plan Rows': PropTypes.number,
+      'Actual Loops': PropTypes.number,
+    }),
+    node_extra_info: PropTypes.array,
+    display_text: PropTypes.string,
+    tooltip_text: PropTypes.string,
+  }),
+  show: PropTypes.shape({
+    show_timings: PropTypes.bool,
+    show_rowsx: PropTypes.bool,
+    show_rows: PropTypes.bool,
+    show_plan_rows: PropTypes.bool,
+  }),
+  activeExId: PropTypes.string,
+  setActiveExId: PropTypes.string,
+  collapsedExId: PropTypes.string,
+  toggleCollapseExId: PropTypes.func,
+};
+
+export default function Analysis({explainTable}) {
+  const tableClasses = commonTableStyles();
+  const [activeExId, setActiveExId] = React.useState();
+  const [collapsedExId, setCollapsedExId] = React.useState([]);
+
+  const toggleCollapseExId = (exId, hasPlans=true)=>{
+    if(hasPlans) {
+      setCollapsedExId((prev)=>{
+        if(prev.indexOf(exId) > -1) {
+          return prev.filter((v)=>v!=exId);
+        }
+        return [...prev, exId];
+      });
+    }
+  };
+  return <table className={clsx(tableClasses.table, tableClasses.noBorder, tableClasses.borderBottom)}>
+    <thead>
+      <tr>
+        <th rowSpan="2" style={{width: '30px'}}></th>
+        <th rowSpan="2"><button disabled="">#</button></th>
+        <th rowSpan="2"><button disabled="">Node</button></th>
+        <th colSpan="2" style={explainTable.show_timings ? {} : {display: 'none'}}>
+          <button disabled="">Timings</button>
+        </th>
+        <th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} colSpan="3">
+          <button disabled="">Rows</button>
+        </th>
+        <th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} rowSpan="2">
+          <button disabled="">Loops</button>
+        </th>
+      </tr>
+      <tr>
+        <th style={explainTable.show_timings ? {} : {display: 'none'}}>
+          <button disabled="">Exclusive</button>
+        </th>
+        <th style={explainTable.show_timings ? {} : {display: 'none'}}>
+          <button disabled="">Inclusive</button>
+        </th>
+        <th style={explainTable.show_rowsx ? {} : {display: 'none'}}><button disabled="">Rows X</button></th>
+        <th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}}><button disabled="">Actual</button></th>
+        <th style={(explainTable.show_rowsx || explainTable.plan_rows) ? {} : {display: 'none'}}><button disabled="">Plan</button></th>
+      </tr>
+    </thead>
+    <tbody>
+      {_.sortBy(explainTable.rows,(r)=>r['data']['_serial']).map((row, i)=>{
+        return <ExplainRow key={i} row={row} show={{
+          show_timings: explainTable.show_timings,
+          show_rowsx: explainTable.show_rowsx,
+          show_rows: explainTable.show_rows,
+          show_plan_rows: explainTable.show_plan_rows,
+        }} activeExId={activeExId} setActiveExId={setActiveExId} collapsedExId={collapsedExId} toggleCollapseExId={toggleCollapseExId} />;
+      })}
+    </tbody>
+  </table>;
+}
+
+Analysis.propTypes = {
+  explainTable: PropTypes.object,
+};
diff --git a/web/pgadmin/static/js/Explain/ExplainStatistics.jsx b/web/pgadmin/static/js/Explain/ExplainStatistics.jsx
new file mode 100644
index 000000000..c9d1ce670
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/ExplainStatistics.jsx
@@ -0,0 +1,143 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React from 'react';
+import { Box, Grid, makeStyles } from '@material-ui/core';
+import gettext from 'sources/gettext';
+import { commonTableStyles } from '../Theme';
+import clsx from 'clsx';
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+
+const useStyles = makeStyles((theme)=>({
+  title: {
+    fontWeight: 'bold',
+    padding: '4px',
+    backgroundColor: theme.otherVars.cardHeaderBg,
+    borderTopLeftRadius: theme.shape.borderRadius,
+    borderTopRightRadius: theme.shape.borderRadius,
+  },
+  tableRow: {
+    backgroundColor: theme.palette.grey[200]
+  },
+  tableName:{
+    fontWeight: 'bold',
+  },
+  nodeName: {
+    paddingLeft: '30px',
+  },
+}));
+
+export default function ExplainStatistics({explainTable}) {
+  // _renderStatisticsTable
+  const classes = useStyles();
+  const tableClasses = commonTableStyles();
+  return (
+    <Box p={1}>
+      <Grid container spacing={1}>
+        <Grid item lg={6} md={12}>
+          <div className={classes.title}>{gettext('Statistics per Node Type')}</div>
+          <table className={clsx(tableClasses.table)}>
+            <thead>
+              <tr>
+                <th>{gettext('Node type')}</th>
+                <th>{gettext('Count')}</th>
+                {explainTable.show_timings && <>
+                  <th>{gettext('Time spent')}</th>
+                  <th>{'% '+gettext('of query')}</th>
+                </>}
+              </tr>
+            </thead>
+            <tbody>
+              {_.sortBy(Object.keys(explainTable.statistics.nodes)).map((key, i)=>{
+                let node = explainTable.statistics.nodes[key];
+                return <tr key={i}>
+                  <td>{node.name}</td>
+                  <td>{node.count}</td>
+                  {explainTable.show_timings && <>
+                    <td>{Math.ceil10(node.sum_of_times, -3) + ' ms'}</td>
+                    <td>{Math.ceil10(((node.sum_of_times||0)/(explainTable.total_time||1)) * 100, -2)+ '%'}</td>
+                  </>}
+                </tr>;
+              })}
+            </tbody>
+          </table>
+        </Grid>
+        <Grid item lg={6} md={12}>
+          <div className={classes.title}>{gettext('Statistics per Relation')}</div>
+          <table className={clsx(tableClasses.table)}>
+            <thead>
+              <tr>
+                <th>{gettext('Relation name')}</th>
+                <th>{gettext('Scan count')}</th>
+                {explainTable.show_timings && <>
+                  <th>{gettext('Total time')}</th>
+                  <th>{'% '+gettext('of query')}</th>
+                </>}
+              </tr>
+              <tr>
+                <th>{gettext('Node type')}</th>
+                <th>{gettext('Count')}</th>
+                {explainTable.show_timings && <>
+                  <th>{gettext('Sum of times')}</th>
+                  <th>{'% '+gettext('of relation')}</th>
+                </>}
+              </tr>
+            </thead>
+            <tbody>
+              {_.sortBy(Object.keys(explainTable.statistics.tables)).map((key, i)=>{
+                let table = explainTable.statistics.tables[key];
+                table.sum_of_times = _.sumBy(table.nodes, 'sum_of_times');
+                return <React.Fragment key={i}>
+                  <tr className={classes.tableRow}>
+                    <td className={classes.tableName}>{table.name}</td>
+                    <td>{table.count}</td>
+                    {explainTable.show_timings && <>
+                      <td>{Math.ceil10(table.sum_of_times, -3) + ' ms'}</td>
+                      <td>{Math.ceil10(((table.sum_of_times||0)/(explainTable.total_time||1)) * 100, -2)+ '%'}</td>
+                    </>}
+                  </tr>
+                  {_.sortBy(Object.keys(table.nodes)).map((nodeKey, j)=>{
+                    let node = table.nodes[nodeKey];
+                    return <tr key={j}>
+                      <td><div className={classes.nodeName}>{node.name}</div></td>
+                      <td>{node.count}</td>
+                      {explainTable.show_timings && <>
+                        <td>{Math.ceil10(node.sum_of_times, -3) + ' ms'}</td>
+                        <td>{Math.ceil10(((node.sum_of_times||0)/(table.sum_of_times||1)) * 100, -2)+ '%'}</td>
+                      </>}
+                    </tr>;
+                  })}
+                </React.Fragment>;
+              })}
+            </tbody>
+          </table>
+        </Grid>
+      </Grid>
+    </Box>
+  );
+}
+
+const NodeType = {
+  name: PropTypes.string,
+  count: PropTypes.number,
+  sum_of_times: PropTypes.number,
+};
+ExplainStatistics.propTypes = {
+  explainTable: PropTypes.shape({
+    show_timings: PropTypes.bool,
+    statistics: PropTypes.shape({
+      nodes: PropTypes.arrayOf(NodeType),
+      tables: PropTypes.arrayOf({
+        ...NodeType,
+        nodes: PropTypes.arrayOf(NodeType),
+      }),
+    }),
+    total_time: PropTypes.number,
+  }),
+};
diff --git a/web/pgadmin/static/js/Explain/Graphical.jsx b/web/pgadmin/static/js/Explain/Graphical.jsx
new file mode 100644
index 000000000..2c7ba2101
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/Graphical.jsx
@@ -0,0 +1,435 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { Box, Card, CardContent, CardHeader, makeStyles, useTheme } from '@material-ui/core';
+import React, {useEffect} from 'react';
+import _ from 'lodash';
+import { PgButtonGroup, PgIconButton } from '../components/Buttons';
+import ZoomInIcon from '@material-ui/icons/ZoomIn';
+import ZoomOutIcon from '@material-ui/icons/ZoomOut';
+import ZoomOutMapIcon from '@material-ui/icons/ZoomOutMap';
+import SaveAltIcon from '@material-ui/icons/SaveAlt';
+import gettext from 'sources/gettext';
+import ReactDOMServer from 'react-dom/server';
+import url_for from 'sources/url_for';
+import { downloadSvg } from './svg_download';
+import CloseIcon from '@material-ui/icons/CloseRounded';
+import { commonTableStyles } from '../Theme';
+import clsx from 'clsx';
+import PropTypes from 'prop-types';
+
+// Some predefined constants used to calculate image location and its border
+var pWIDTH = 100.;
+var pHEIGHT = 100.;
+var IMAGE_WIDTH = 50;
+var IMAGE_HEIGHT = 50;
+var ARROW_WIDTH = 10,
+  ARROW_HEIGHT = 10;
+var TXT_ALIGN = 5,
+  TXT_SIZE = '15px';
+var xMargin = 25,
+  yMargin = 25;
+var MIN_ZOOM_FACTOR = 0.3,
+  MAX_ZOOM_FACTOR = 2,
+  INIT_ZOOM_FACTOR = 1;
+var ZOOM_RATIO = 0.05;
+
+const AUXILIARY_KEYS = ['image', 'Plans', 'level', 'image_text', 'xpos', 'ypos', 'width', 'height', 'total_time', 'parent_node', '_serial', 'arr_id'];
+
+function PolyLine({startx, starty, endx, endy, opts, arrowOpts}) {
+  // Calculate end point of first starting straight line (startx1, starty1)
+  // Calculate start point of 2nd straight line (endx1, endy1)
+  let midX1 = startx + ((endx - startx) / 3),
+    midX2 = startx + (2 * ((endx - startx) / 3));
+  return (
+    <>
+      <line x1={startx} x2={midX1} y1={starty} y2={starty} {...opts}></line>
+      <line x1={midX1-1} x2={midX2} y1={starty} y2={endy} {...opts}></line>
+      <line x1={midX2} x2={endx} y1={endy} y2={endy} {...opts} {...arrowOpts}></line>
+    </>
+  );
+}
+PolyLine.propTypes = {
+  startx: PropTypes.number,
+  starty: PropTypes.number,
+  endx: PropTypes.number,
+  endy: PropTypes.number,
+  opts: PropTypes.object,
+  arrowOpts: PropTypes.object,
+};
+
+function Multitext({currentXpos, currentYpos, label, maxWidth}) {
+  const theme = useTheme();
+  let abc = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+  var xmlns = 'http://www.w3.org/2000/svg';
+  var svgElem = document.createElementNS(xmlns, 'svg');
+  svgElem.setAttributeNS(xmlns, 'height', '100%');
+  svgElem.setAttributeNS(xmlns, 'width', '100%');
+  var text = document.createElementNS(xmlns, 'text');
+  text.innerHTML = abc;
+  text.setAttributeNS(xmlns, 'x', 0);
+  text.setAttributeNS(xmlns, 'y', 0);
+  let attributes={
+    'font-size': TXT_SIZE,
+    'text-anchor': 'middle',
+    'fill': theme.palette.text.primary,
+  };
+  Object.keys(attributes).forEach((key)=>{
+    text.setAttributeNS(xmlns, key, attributes[key]);
+  });
+  svgElem.appendChild(text);
+  document.body.appendChild(svgElem);
+  /*
+  * Find letter width in pixels and
+  * index from where the text should be broken
+  */
+  var letterWidth = text.getBBox().width / abc.length,
+    wordBreakIndex = Math.round((maxWidth / letterWidth)) - 1;
+  svgElem.remove();
+
+  var words = label.split(' '),
+    widthSoFar = 0,
+    lines = [],
+    currLine = '',
+    /*
+    * Function to divide string into multiple lines
+    * and store them in an array if it size crosses
+    * the max-width boundary.
+    */
+    splitTextInMultiLine = function(leading, so_far, line) {
+      var l = line.length,
+        res = [];
+
+      if (l == 0)
+        return res;
+
+      if (so_far && (so_far + (l * letterWidth) > maxWidth)) {
+        res.push(leading);
+        res = res.concat(splitTextInMultiLine('', 0, line));
+      } else if (so_far) {
+        res.push(leading + ' ' + line);
+      } else {
+        if (leading)
+          res.push(leading);
+        if (line.length > wordBreakIndex + 1)
+          res.push(line.slice(0, wordBreakIndex) + '-');
+        else
+          res.push(line);
+        res = res.concat(splitTextInMultiLine('', 0, line.slice(wordBreakIndex)));
+      }
+
+      return res;
+    };
+
+  for (var i = 0; i < words.length; i++) {
+    var tmpArr = splitTextInMultiLine(
+      currLine, widthSoFar, words[i]
+    );
+
+    if (currLine) {
+      lines = lines.slice(0, lines.length - 1);
+    }
+    lines = lines.concat(tmpArr);
+    currLine = lines[lines.length - 1];
+    widthSoFar = (currLine.length * letterWidth);
+  }
+
+  return (
+    <text x={currentXpos} y={currentYpos} fill={theme.palette.text.primary} style={{fontSize: TXT_SIZE, textAnchor: 'middle'}}>
+      {lines.map((line, i)=>{
+        if(i > 0) {
+          return <tspan key={i} dy="1.2em" x={currentXpos}>{line}</tspan>;
+        }
+        return <tspan key={i}>{line}</tspan>;
+      })}
+    </text>
+  );
+}
+
+Multitext.propTypes = {
+  currentXpos: PropTypes.number,
+  currentYpos: PropTypes.number,
+  label: PropTypes.string,
+  maxWidth: PropTypes.number,
+};
+function Image({plan, label, currentXpos, currentYpos, content, download, onNodeClick}) {
+  return (
+    <>
+      <image href={content}
+        preserveAspectRatio="none"
+        x={currentXpos + (pWIDTH - IMAGE_WIDTH) / 2}
+        y={currentYpos + (pHEIGHT - IMAGE_HEIGHT) / 2}
+        width={IMAGE_WIDTH} height={IMAGE_HEIGHT}
+        style={{cursor: 'pointer'}} onClick={onNodeClick}>
+        {download &&
+          <title>
+            <NodeDetails plan={plan} download={true} />
+          </title>
+        }
+      </image>
+      <Multitext currentXpos={currentXpos + (pWIDTH / 2) + TXT_ALIGN} currentYpos={currentYpos + pHEIGHT - TXT_ALIGN} label={label} maxWidth={150} />
+    </>
+  );
+}
+
+Image.propTypes = {
+  plan: PropTypes.object,
+  label: PropTypes.string,
+  currentXpos: PropTypes.number,
+  currentYpos: PropTypes.number,
+  content: PropTypes.string,
+  download: PropTypes.bool,
+  onNodeClick: PropTypes.func,
+};
+function NodeDetails({plan, download=false}) {
+  return <>
+    {Object.keys(plan).map((key)=>{
+      if(AUXILIARY_KEYS.indexOf(key) != -1) {
+        return <></>;
+      }
+      if(download) {
+        return `${key}: ${plan[key]}\n`;
+      } else {
+        return (<tr key={key}>
+          <td>{_.escape(key)}</td>
+          <td>{_.escape(plan[key])}</td>
+        </tr>);
+      }
+    })}
+  </>;
+}
+NodeDetails.propTypes = {
+  plan: PropTypes.object,
+  download: PropTypes.bool,
+};
+
+function PlanContent({plan, pXpos, pYpos, ...props}) {
+  const theme = useTheme();
+  let currentXpos = props.xpos + plan.xpos,
+    currentYpos = props.ypos + plan.ypos,
+    isSubPlan = (plan['Parent Relationship'] === 'SubPlan');
+  const nodeLabel = plan.Schema == undefined ?
+    plan.image_text : plan.Schema + '.' + plan.image_text;
+
+  let polylineProps = null;
+  if(!_.isUndefined(pYpos)) {
+    let arrowSize = props.ctx.arrows[plan['arr_id']];
+    polylineProps = {
+      startx: currentXpos + pWIDTH,
+      starty: currentYpos + (pHEIGHT / 2),
+      endx: pXpos - ARROW_WIDTH,
+      endy: pYpos + (pHEIGHT / 2),
+      arr_id: plan['arr_id'],
+    };
+    polylineProps.opts = {
+      stroke: theme.palette.text.primary,
+      strokeWidth: arrowSize + 2,
+    };
+    polylineProps.arrowOpts = {
+      style: {
+        markerEnd: `url("#${plan['arr_id']}")`,
+      }
+    };
+  }
+  return (
+    <>
+      <g>
+        {isSubPlan &&
+      <>
+        <rect x={currentXpos - plan.width + pWIDTH + xMargin}
+          y={currentYpos - plan.height + pHEIGHT + yMargin - TXT_ALIGN}
+          width={plan.width - xMargin}
+          height={plan.height + (currentYpos - yMargin)}
+          rx={5}
+          stroke="#444444"
+          strokeWidth={1.2}
+          fill="gray"
+          fillOpacity={0.2}
+        />
+        <tspan x={currentXpos + pWIDTH - (plan.width / 2) - xMargin}
+          y={currentYpos + pHEIGHT - (plan.height / 2) - yMargin}
+          fontSize={TXT_SIZE}
+          textAnchor="start"
+          fill="red"
+        >{plan['Subplan Name']}</tspan>
+      </>}
+        <Image
+          label={nodeLabel}
+          content={url_for('misc.index') + 'static/explain/img/' + plan.image}
+          currentXpos={currentXpos}
+          currentYpos={currentYpos}
+          plan={plan}
+          download={props.download}
+          onNodeClick={()=>props.onNodeClick(nodeLabel, plan)}
+        />
+        {polylineProps && <PolyLine {...polylineProps} />}
+      </g>
+      {plan['Plans'].map((p, i)=>(
+        <PlanContent key={i} plan={p} pXpos={currentXpos} pYpos={currentYpos} {...props}/>
+      ))}
+    </>
+  );
+}
+
+PlanContent.propTypes = {
+  plan: PropTypes.object,
+  pXpos: PropTypes.number,
+  pYpos: PropTypes.number,
+  xpos: PropTypes.number,
+  ypos: PropTypes.number,
+  ctx: PropTypes.object,
+  download: PropTypes.bool,
+  onNodeClick: PropTypes.func,
+};
+
+function PlanSVG({planData, zoomFactor, fitZoomFactor, ...props}) {
+  const theme = useTheme();
+  useEffect(()=>{
+    fitZoomFactor && fitZoomFactor(planData.width);
+  }, [planData.width]);
+
+  return (
+    <svg height={planData.height*zoomFactor} width={planData.width*zoomFactor} version="1.1" xmlns="http://www.w3.org/2000/svg">
+      <defs>
+        {Object.keys(props.ctx.arrows).map((arr_id, i)=>{
+          let arrowPoints = [
+            0, 0,
+            0, (ARROW_WIDTH / 2),
+            ARROW_HEIGHT, (ARROW_WIDTH / 4),
+            0, 0
+          ].join(',');
+          let viewBox = [0, 0, 2 * ARROW_WIDTH, 2 * ARROW_HEIGHT].join(' ');
+          return(
+            <marker key={i} viewBox={viewBox} markerWidth={ARROW_WIDTH} markerHeight={ARROW_HEIGHT} orient="auto" refX="0" refY={ARROW_WIDTH / 4} id={arr_id} fill={theme.palette.text.primary}>
+              <polygon points={arrowPoints}></polygon>
+            </marker>
+          );
+        })}
+      </defs>
+      <g transform={`matrix(${zoomFactor},0,0,${zoomFactor},0,0)`}>
+        <rect x="0" y="0" width={planData.width} height={planData.height} rx="5" ry="5" fill={theme.palette.background.default}></rect>
+        <PlanContent plan={planData['Plan']} xpos={planData.width - xMargin} ypos={yMargin} {...props}/>
+      </g>
+    </svg>
+  );
+}
+
+PlanSVG.propTypes = {
+  planData: PropTypes.object,
+  zoomFactor: PropTypes.number,
+  fitZoomFactor: PropTypes.number,
+  ctx: PropTypes.object,
+};
+
+
+const useStyles = makeStyles((theme)=>({
+  explainDetails: {
+    minWidth: '200px',
+    maxWidth: '300px',
+    position: 'absolute',
+    top: '0.25rem',
+    bottom: '0.25rem',
+    right: '0.25rem',
+    borderColor: theme.otherVars.borderColor,
+    // box-shadow: 0 0.125rem 0.5rem rgb(132 142 160 / 28%);
+    wordBreak: 'break-all',
+    display: 'flex',
+    flexDirection: 'column',
+    zIndex: 99,
+  },
+  explainContent: {
+    height: '100%',
+    overflow: 'auto',
+  }
+}));
+
+export default function Graphical({planData, ctx}) {
+  const tableStyles = commonTableStyles();
+  const classes = useStyles();
+  const graphContainerRef = React.useRef();
+  const [zoomFactor, setZoomFactor] = React.useState(INIT_ZOOM_FACTOR);
+  const [[explainPlanTitle, explainPlanDetails], setExplainPlanDetails] = React.useState([null, null]);
+
+  const onCmdClick = (cmd)=>{
+    if(cmd == 'in') {
+      setZoomFactor((prev)=>{
+        if(prev >= MAX_ZOOM_FACTOR) return prev;
+        return prev+ZOOM_RATIO;
+      });
+    } else if(cmd == 'out') {
+      setZoomFactor((prev)=>{
+        if(prev <= MIN_ZOOM_FACTOR) return prev;
+        return prev-ZOOM_RATIO;
+      });
+    } else {
+      setZoomFactor(INIT_ZOOM_FACTOR);
+    }
+  };
+
+  const fitZoomFactor = React.useCallback((svgWidth)=>{
+    /*
+    * Scale graph in case its width is bigger than panel width
+    * in which the graph is displayed
+    */
+    if(graphContainerRef.current.offsetWidth && svgWidth) {
+      let zoomFactor = graphContainerRef.current.offsetWidth/svgWidth;
+      zoomFactor = zoomFactor < MIN_ZOOM_FACTOR ? MIN_ZOOM_FACTOR : zoomFactor;
+      zoomFactor = zoomFactor > INIT_ZOOM_FACTOR ? INIT_ZOOM_FACTOR : zoomFactor;
+      setZoomFactor(zoomFactor);
+    }
+  }, []);
+
+  const onDownloadClick = ()=>{
+    downloadSvg(ReactDOMServer.renderToStaticMarkup(
+      <PlanSVG planData={planData} download={true} ctx={ctx} zoomFactor={INIT_ZOOM_FACTOR} onNodeClick={()=>{}}/>
+    ), 'explain_plan_' + (new Date()).getTime() + '.svg');
+  };
+
+  const onNodeClick = React.useCallback((title, details)=>{
+    setExplainPlanDetails([title, details]);
+  }, []);
+
+  return (
+    <Box ref={graphContainerRef} height="100%" width="100%" overflow="auto">
+      <Box position="absolute" top="4px" left="4px" gridGap="4px" display="flex">
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Zoom in')} icon={<ZoomInIcon />} onClick={()=>onCmdClick('in')}/>
+          <PgIconButton title={gettext('Zoom to original')} icon={<ZoomOutMapIcon />} onClick={()=>onCmdClick()}/>
+          <PgIconButton title={gettext('Zoom out')} icon={<ZoomOutIcon />} onClick={()=>onCmdClick('out')}/>
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Download')} icon={<SaveAltIcon />} onClick={onDownloadClick}/>
+        </PgButtonGroup>
+      </Box>
+      <PlanSVG planData={planData} ctx={ctx} zoomFactor={zoomFactor} fitZoomFactor={fitZoomFactor}
+        onNodeClick={onNodeClick}
+      />
+      {Boolean(explainPlanDetails) &&
+      <Card className={classes.explainDetails}>
+        <CardHeader title={<Box display="flex">
+          {explainPlanTitle}
+          <Box marginLeft="auto">
+            <PgIconButton title={gettext('Close')} icon={<CloseIcon />} size="xs" noBorder onClick={()=>setExplainPlanDetails([null, null])}/>
+          </Box>
+        </Box>} />
+        <CardContent className={classes.explainContent}>
+          <table className={clsx(tableStyles.table, tableStyles.borderBottom)}>
+            <tbody>
+              <NodeDetails download={false} plan={explainPlanDetails} />
+            </tbody>
+          </table>
+        </CardContent>
+      </Card>}
+    </Box>
+  );
+}
+
+Graphical.propTypes = {
+  planData: PropTypes.object,
+  ctx: PropTypes.object,
+};
diff --git a/web/pgadmin/static/js/Explain/ImageMapper.js b/web/pgadmin/static/js/Explain/ImageMapper.js
new file mode 100644
index 000000000..02827de76
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/ImageMapper.js
@@ -0,0 +1,300 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+
+/*
+ * A map which is used to fetch the image to be drawn and
+ * text which will appear below it
+ */
+
+const ImageMapper = {
+  'Aggregate': {
+    'image': 'ex_aggregate.svg',
+    'image_text': 'Aggregate',
+  },
+  'Append': {
+    'image': 'ex_append.svg',
+    'image_text': 'Append',
+  },
+  'Bitmap Index Scan': function(data) {
+    return {
+      'image': 'ex_bmp_index.svg',
+      'image_text': data['Index Name'],
+    };
+  },
+  'Bitmap Heap Scan': function(data) {
+    return {
+      'image': 'ex_bmp_heap.svg',
+      'image_text': data['Relation Name'],
+    };
+  },
+  'BitmapAnd': {
+    'image': 'ex_bmp_and.svg',
+    'image_text': 'Bitmap AND',
+  },
+  'BitmapOr': {
+    'image': 'ex_bmp_or.svg',
+    'image_text': 'Bitmap OR',
+  },
+  'CTE Scan': {
+    'image': 'ex_cte_scan.svg',
+    'image_text': 'CTE Scan',
+  },
+  'Function Scan': {
+    'image': 'ex_result.svg',
+    'image_text': 'Function Scan',
+  },
+  'Foreign Scan': {
+    'image': 'ex_foreign_scan.svg',
+    'image_text': 'Foreign Scan',
+  },
+  'Gather': {
+    'image': 'ex_gather_motion.svg',
+    'image_text': 'Gather',
+  },
+  'Gather Merge': {
+    'image': 'ex_gather_merge.svg',
+    'image_text': 'Gather Merge',
+  },
+  'Group': {
+    'image': 'ex_group.svg',
+    'image_text': 'Group',
+  },
+  'GroupAggregate': {
+    'image': 'ex_aggregate.svg',
+    'image_text': 'Group Aggregate',
+  },
+  'Hash': {
+    'image': 'ex_hash.svg',
+    'image_text': 'Hash',
+  },
+  'Hash Join': function(data) {
+    if (!data['Join Type']) return {
+      'image': 'ex_join.svg',
+      'image_text': 'Join',
+    };
+    switch (data['Join Type']) {
+    case 'Anti':
+      return {
+        'image': 'ex_hash_anti_join.svg',
+        'image_text': 'Hash Anti Join',
+      };
+    case 'Semi':
+      return {
+        'image': 'ex_hash_semi_join.svg',
+        'image_text': 'Hash Semi Join',
+      };
+    default:
+      return {
+        'image': 'ex_hash.svg',
+        'image_text': String('Hash ' + data['Join Type'] + ' Join'),
+      };
+    }
+  },
+  'HashAggregate': {
+    'image': 'ex_aggregate.svg',
+    'image_text': 'Hash Aggregate',
+  },
+  'Index Only Scan': function(data) {
+    return {
+      'image': 'ex_index_only_scan.svg',
+      'image_text': data['Index Name'],
+    };
+  },
+  'Index Scan': function(data) {
+    return {
+      'image': 'ex_index_scan.svg',
+      'image_text': data['Index Name'],
+    };
+  },
+  'Index Scan Backword': {
+    'image': 'ex_index_scan.svg',
+    'image_text': 'Index Backward Scan',
+  },
+  'Limit': {
+    'image': 'ex_limit.svg',
+    'image_text': 'Limit',
+  },
+  'LockRows': {
+    'image': 'ex_lock_rows.svg',
+    'image_text': 'Lock Rows',
+  },
+  'Materialize': {
+    'image': 'ex_materialize.svg',
+    'image_text': 'Materialize',
+  },
+  'Merge Append': {
+    'image': 'ex_merge_append.svg',
+    'image_text': 'Merge Append',
+  },
+  'Merge Join': function(data) {
+    switch (data['Join Type']) {
+    case 'Anti':
+      return {
+        'image': 'ex_merge_anti_join.svg',
+        'image_text': 'Merge Anti Join',
+      };
+    case 'Semi':
+      return {
+        'image': 'ex_merge_semi_join.svg',
+        'image_text': 'Merge Semi Join',
+      };
+    default:
+      return {
+        'image': 'ex_merge.svg',
+        'image_text': String('Merge ' + data['Join Type'] + ' Join'),
+      };
+    }
+  },
+  'ModifyTable': function(data) {
+    switch (data['Operation']) {
+    case 'Insert':
+      return {
+        'image': 'ex_insert.svg',
+        'image_text': 'Insert',
+      };
+    case 'Update':
+      return {
+        'image': 'ex_update.svg',
+        'image_text': 'Update',
+      };
+    case 'Delete':
+      return {
+        'image': 'ex_delete.svg',
+        'image_text': 'Delete',
+      };
+    }
+  },
+  'Named Tuplestore Scan': {
+    'image': 'ex_named_tuplestore_scan.svg',
+    'image_text': 'Named Tuplestore Scan',
+  },
+  'Nested Loop': function(data) {
+    switch (data['Join Type']) {
+    case 'Anti':
+      return {
+        'image': 'ex_nested_loop_anti_join.svg',
+        'image_text': 'Nested Loop Anti Join',
+      };
+    case 'Semi':
+      return {
+        'image': 'ex_nested_loop_semi_join.svg',
+        'image_text': 'Nested Loop Semi Join',
+      };
+    default:
+      return {
+        'image': 'ex_nested.svg',
+        'image_text': 'Nested Loop ' + data['Join Type'] + ' Join',
+      };
+    }
+  },
+  'ProjectSet': {
+    'image': 'ex_projectset.svg',
+    'image_text': 'ProjectSet',
+  },
+  'Recursive Union': {
+    'image': 'ex_recursive_union.svg',
+    'image_text': 'Recursive Union',
+  },
+  'Result': {
+    'image': 'ex_result.svg',
+    'image_text': 'Result',
+  },
+  'Sample Scan': {
+    'image': 'ex_scan.svg',
+    'image_text': 'Sample Scan',
+  },
+  'Scan': {
+    'image': 'ex_scan.svg',
+    'image_text': 'Scan',
+  },
+  'Seek': {
+    'image': 'ex_seek.svg',
+    'image_text': 'Seek',
+  },
+  'SetOp': function(data) {
+    let strategy = data['Strategy'],
+      command = data['Command'];
+
+    if (strategy == 'Hashed') {
+      if (command.startsWith('Intersect')) {
+        if (command == 'Intersect All')
+          return {
+            'image': 'ex_hash_setop_intersect_all.svg',
+            'image_text': 'Hashed Intersect All',
+          };
+        return {
+          'image': 'ex_hash_setop_intersect.svg',
+          'image_text': 'Hashed Intersect',
+        };
+      } else if (command.startsWith('Except')) {
+        if (command == 'Except All')
+          return {
+            'image': 'ex_hash_setop_except_all.svg',
+            'image_text': 'Hashed Except All',
+          };
+        return {
+          'image': 'ex_hash_setop_except.svg',
+          'image_text': 'Hash Except',
+        };
+      }
+      return {
+        'image': 'ex_hash_setop_unknown.svg',
+        'image_text': 'Hashed SetOp Unknown',
+      };
+    }
+    return {
+      'image': 'ex_setop.svg',
+      'image_text': 'SetOp',
+    };
+  },
+  'Seq Scan': function(data) {
+    return {
+      'image': 'ex_scan.svg',
+      'image_text': data['Relation Name'],
+    };
+  },
+  'Subquery Scan': {
+    'image': 'ex_subplan.svg',
+    'image_text': 'SubQuery Scan',
+  },
+  'Sort': {
+    'image': 'ex_sort.svg',
+    'image_text': 'Sort',
+  },
+  'Tid Scan': {
+    'image': 'ex_tid_scan.svg',
+    'image_text': 'Tid Scan',
+  },
+  'Table Function Scan': {
+    'image': 'ex_table_func_scan.svg',
+    'image_text': 'Table Function Scan',
+  },
+  'Unique': {
+    'image': 'ex_unique.svg',
+    'image_text': 'Unique',
+  },
+  'Values Scan': {
+    'image': 'ex_values_scan.svg',
+    'image_text': 'Values Scan',
+  },
+  'WindowAgg': {
+    'image': 'ex_window_aggregate.svg',
+    'image_text': 'Window Aggregate',
+  },
+  'WorkTable Scan': {
+    'image': 'ex_worktable_scan.svg',
+    'image_text': 'WorkTable Scan',
+  },
+  'Undefined': {
+    'image': 'ex_unknown.svg',
+    'image_text': 'Undefined',
+  },
+};
+
+export default ImageMapper;
diff --git a/web/pgadmin/static/js/Explain/css/explain.css b/web/pgadmin/static/js/Explain/css/explain.css
new file mode 100644
index 000000000..d1f8bbb27
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/css/explain.css
@@ -0,0 +1,23 @@
+.explain-tooltip {
+  display: table-cell;
+  text-align: left;
+  white-space: nowrap;
+  padding: 2px !important;
+  font-size: small;
+}
+
+td.explain-tooltip-val {
+  display: table-cell;
+  text-align: left;
+  white-space: pre-wrap;
+  padding: 2px !important;
+  font-size: small;
+  font-weight: normal;
+}
+
+.pgadmin-tooltip-table {
+  border-collapse: collapse;
+  border-spacing: 1px;
+  top: auto;
+  left: auto;
+}
diff --git a/web/pgadmin/static/js/Explain/img/ex_aggregate.svg b/web/pgadmin/static/js/Explain/img/ex_aggregate.svg
new file mode 100644
index 000000000..f6342e50d
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_aggregate.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3,.cls-8{fill:#c8ffac;}.cls-4{fill:#48d601;}.cls-5{fill:#d3d4f3;}.cls-6{fill:#666bd1;}.cls-11,.cls-7,.cls-9{fill:none;}.cls-7{stroke:#666bd1;}.cls-10,.cls-11,.cls-7,.cls-9{stroke-linejoin:round;}.cls-7,.cls-9{opacity:0.8;}.cls-8{stroke:#48d601;stroke-miterlimit:10;opacity:0.7;}.cls-9{stroke:#c18f35;}.cls-10{fill:#34495e;}.cls-10,.cls-11{stroke:#34495e;}.cls-11{stroke-linecap:round;}</style></defs><title>ex_aggregate</title><g id="ex_broadcast_motion"><rect class="cls-1" x="38.42" y="27.25" width="8.28" height="9"/><path class="cls-2" d="M46.2,27.75v8H38.92v-8H46.2m1-1H37.92v10H47.2v-10Z"/><rect class="cls-1" x="46.72" y="27.25" width="8.28" height="9"/><path class="cls-2" d="M54.5,27.75v8H47.22v-8H54.5m1-1H46.22v10H55.5v-10Z"/><path class="cls-3" d="M39.92,45.23c-1.37,0-1.5-.13-1.5-1.5v-7.5H46.7v9Z"/><path class="cls-4" d="M46.2,36.73v8H39.92a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-7H46.2m1-1H37.92v8c0,1.65.35,2,2,2H47.2v-10Z"/><path class="cls-3" d="M46.72,45.23v-9H55v7.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M54.5,36.73v7a3.29,3.29,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.22v-8H54.5m1-1H46.22v10H53.5c1.65,0,2-.35,2-2v-8Z"/><path class="cls-5" d="M38.42,27.27v-7.5c0-1.37.13-1.5,1.5-1.5H46.7v9Z"/><path class="cls-6" d="M46.2,18.77v8H38.92v-7a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H46.2m1-1H39.92c-1.65,0-2,.35-2,2v8H47.2v-10Z"/><path class="cls-5" d="M46.72,27.27v-9H53.5c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-6" d="M53.5,18.77a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H47.22v-8H53.5m0-1H46.22v10H55.5v-8c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M9,25.43V13.93c0-1.37.13-1.5,1.5-1.5h5.78v13Z"/><path class="cls-6" d="M15.81,12.93v12H9.53v-11a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H10.53c-1.65,0-2,.35-2,2v12h8.28v-14Z"/><path class="cls-5" d="M16.32,25.43v-13H22.1c1.37,0,1.5.13,1.5,1.5v11.5Z"/><path class="cls-6" d="M22.1,12.93A3.25,3.25,0,0,1,23,13h0a2.18,2.18,0,0,1,.1.92v11H16.82v-12H22.1m0-1H15.82v14H24.1v-12c0-1.65-.35-2-2-2Z"/><line class="cls-7" x1="9.06" y1="18.77" x2="15.8" y2="18.77"/><line class="cls-7" x1="16.08" y1="18.77" x2="23.57" y2="18.77"/><path class="cls-3" d="M10.53,51.45C9.16,51.45,9,51.33,9,50V38.45h7.28v13Z"/><path class="cls-4" d="M15.81,39V51H10.53a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V39h6.28m1-1H8.53V50c0,1.65.35,2,2,2h6.28V38Z"/><path class="cls-3" d="M16.32,51.45v-13H23.6V50c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M23.1,39V50a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H16.82V39H23.1m1-1H15.82V52H22.1c1.65,0,2-.35,2-2V38Z"/><line class="cls-8" x1="16.08" y1="44.8" x2="23.57" y2="44.8"/><line class="cls-8" x1="9.06" y1="44.8" x2="15.8" y2="44.8"/><rect class="cls-1" x="16.32" y="25.43" width="7.28" height="13"/><path class="cls-2" d="M23.1,25.93v12H16.82v-12H23.1m1-1H15.82v14H24.1v-14Z"/><line class="cls-9" x1="16.08" y1="31.78" x2="23.57" y2="31.78"/><rect class="cls-1" x="9.03" y="25.43" width="7.28" height="13"/><path class="cls-2" d="M15.81,25.93v12H9.53v-12h6.28m1-1H8.53v14h8.28v-14Z"/><line class="cls-9" x1="9.06" y1="31.78" x2="15.8" y2="31.78"/><polygon class="cls-10" points="37.01 23.11 33.87 24.12 35.16 20.39 37.01 23.11"/><line class="cls-11" x1="34.91" y1="22.57" x2="25" y2="19.04"/><polygon class="cls-10" points="37.01 40.87 33.87 39.87 35.16 43.6 37.01 40.87"/><line class="cls-11" x1="34.91" y1="41.42" x2="25" y2="44.95"/><polygon class="cls-10" points="37.07 31.78 34.43 33.76 34.43 29.8 37.07 31.78"/><line class="cls-11" x1="34.89" y1="31.88" x2="25" y2="31.92"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_append.svg b/web/pgadmin/static/js/Explain/img/ex_append.svg
new file mode 100644
index 000000000..c43597c84
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_append.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#528d19;}.cls-1,.cls-2{stroke:#528d19;}.cls-1,.cls-11,.cls-2,.cls-8{stroke-linejoin:round;}.cls-11,.cls-2,.cls-8{fill:none;}.cls-2{stroke-linecap:round;stroke-width:2px;}.cls-3{fill:#f0ecb6;}.cls-4{fill:#3588c1;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7{opacity:0.7;}.cls-8{stroke:#c18f35;}.cls-9{fill:#d3d4f3;}.cls-10{fill:#666bd1;}.cls-11{stroke:#666bd1;}</style></defs><title>ex_append</title><g id="ex_append"><polygon class="cls-1" points="23.04 38.11 25.57 41.49 20.51 41.49 23.04 38.11"/><line class="cls-2" x1="23.04" y1="41.49" x2="23.04" y2="44.47"/><polygon class="cls-1" points="40.98 38.11 43.5 41.49 38.45 41.49 40.98 38.11"/><line class="cls-2" x1="40.97" y1="41.49" x2="40.97" y2="44.47"/><path class="cls-3" d="M8,34.5c-1.37,0-1.5-.13-1.5-1.5V12.5h51V33c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M57,13V33a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,34H8a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,7,33V13H57m1-1H6V33c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V12Z"/><path class="cls-5" d="M46.38,12.5v-6H56c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M56,7a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,8v4H46.88V7H56m0-1H45.88v7H58V8c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M6.52,12.5V8c0-1.37.13-1.5,1.5-1.5H17.6v6Z"/><path class="cls-6" d="M17.1,7v5H7V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,8,7H17.1m1-1H8C6.37,6,6,6.35,6,8v5H18.1V6Z"/><rect class="cls-5" x="36.82" y="6.5" width="9.56" height="6"/><path class="cls-6" d="M45.88,7v5H37.31V7h8.56m1-1H36.32v7H46.88V6Z"/><rect class="cls-5" x="27.25" y="6.5" width="9.56" height="6"/><path class="cls-6" d="M36.32,7v5H27.75V7h8.56m1-1H26.76v7H37.32V6Z"/><rect class="cls-5" x="17.62" y="6.5" width="9.62" height="6"/><path class="cls-6" d="M26.74,7v5H18.12V7h8.61m1-1H17.13v7H27.74V6Z"/><g class="cls-7"><line class="cls-8" x1="17.61" y1="34" x2="17.61" y2="13"/><line class="cls-8" x1="36.8" y1="34" x2="36.8" y2="13"/><line class="cls-8" x1="27.21" y1="34" x2="27.21" y2="13"/><line class="cls-8" x1="46.39" y1="34" x2="46.39" y2="13"/><line class="cls-8" x1="7" y1="19.85" x2="57.25" y2="19.85"/><line class="cls-8" x1="7" y1="27.23" x2="57.25" y2="27.23"/></g><rect class="cls-9" x="6.52" y="48.5" width="50.98" height="9" rx="1.5" ry="1.5"/><path class="cls-10" d="M56,49a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,50v6a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,57H8a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,7,56V50a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,8,49H56m0-1H8c-1.65,0-2,.35-2,2v6c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V50c0-1.65-.35-2-2-2Z"/><g class="cls-7"><line class="cls-11" x1="17.6" y1="57.06" x2="17.6" y2="48.83"/><line class="cls-11" x1="27.2" y1="57.06" x2="27.2" y2="48.83"/><line class="cls-11" x1="36.8" y1="57.06" x2="36.8" y2="48.83"/><line class="cls-11" x1="46.4" y1="57.06" x2="46.4" y2="48.83"/></g></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_bmp_and.svg b/web/pgadmin/static/js/Explain/img/ex_bmp_and.svg
new file mode 100644
index 000000000..4393178eb
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_bmp_and.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-8{fill:#34495e;}.cls-2{fill:#d3d4f3;}.cls-3{fill:#666bd1;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{opacity:0.7;}.cls-11,.cls-7{fill:none;stroke-linejoin:round;}.cls-7{stroke:#666bd1;}.cls-8{font-size:6px;font-family:Helvetica-Bold, Helvetica;font-weight:700;}.cls-9{fill:#f0ecb6;}.cls-10{fill:#c18f35;}.cls-11{stroke:#c18f35;}</style></defs><title>ex_bmp_and</title><g id="ex_bmp_and"><path class="cls-1" d="M26,28.89a5.2,5.2,0,0,1-.46-2.07,4.62,4.62,0,0,1,1.4-3.45A5.14,5.14,0,0,1,30.69,22a4.68,4.68,0,0,1,3.49,1.27,4.19,4.19,0,0,1,1.26,3,5.47,5.47,0,0,1-1.3,3.62A12.24,12.24,0,0,1,31.6,32l3.91,4.69Q35.9,35.56,36,35t.31-1.62h2.5a16,16,0,0,1-1,4q-.74,1.9-.74,1.53l3.81,4.65H37.55l-2-2.46a10.55,10.55,0,0,1-2.19,1.9,7.55,7.55,0,0,1-4,1.05Q26,44,24.49,42.19A6.18,6.18,0,0,1,23,38.11,5.89,5.89,0,0,1,24.45,34a16.16,16.16,0,0,1,3.37-2.49A11.06,11.06,0,0,1,26,28.89Zm6.33,12a6.09,6.09,0,0,0,1.79-1.67l-4.85-5.93a14.74,14.74,0,0,0-2.68,2.11,3.93,3.93,0,0,0-1,2.68,3.08,3.08,0,0,0,1.25,2.64,4.39,4.39,0,0,0,2.66.92A5.05,5.05,0,0,0,32.33,40.93Zm-.21-12.2A3.31,3.31,0,0,0,33,26.53a2.35,2.35,0,0,0-.63-1.64,2.17,2.17,0,0,0-1.69-.69,2.26,2.26,0,0,0-2.56,2.3,3.27,3.27,0,0,0,.49,1.71,15.83,15.83,0,0,0,1.64,2.12A11.08,11.08,0,0,0,32.12,28.74Z"/><rect class="cls-2" x="6.54" y="7.5" width="11.98" height="49" rx="1.5" ry="1.5"/><path class="cls-3" d="M17,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,18,9V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,17,56H8a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,7,55V9a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,8,8h9m0-1H8C6.39,7,6,7.35,6,9V55c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V9c0-1.65-.35-2-2-2Z"/><path class="cls-4" d="M6.54,15.5V9c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-5" d="M17,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,18,9v6H7V9a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,8,8h9m0-1H8C6.39,7,6,7.35,6,9v7H19V9c0-1.65-.35-2-2-2Z"/><g class="cls-6"><line class="cls-7" x1="7.05" y1="23.74" x2="18.02" y2="23.74"/><line class="cls-7" x1="7.05" y1="31.49" x2="18.02" y2="31.49"/><line class="cls-7" x1="7.05" y1="39.23" x2="18.02" y2="39.23"/><line class="cls-7" x1="7.05" y1="46.97" x2="18.02" y2="46.97"/></g><text class="cls-8" transform="translate(10.87 21.83)">1</text><text class="cls-8" transform="translate(10.87 29.76)">0</text><text class="cls-8" transform="translate(10.87 45.24)">1</text><text class="cls-8" transform="translate(10.87 37.62)">0</text><text class="cls-8" transform="translate(10.87 53.33)">0</text><rect class="cls-9" x="45.47" y="7.5" width="11.98" height="49" rx="1.5" ry="1.5"/><path class="cls-10" d="M56,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,9V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,56H47a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,46,55V9A3.28,3.28,0,0,1,46,8.1,2.18,2.18,0,0,1,47,8h9m0-1H47c-1.65,0-2,.35-2,2V55c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V9c0-1.65-.35-2-2-2Z"/><g class="cls-6"><line class="cls-11" x1="45.98" y1="23.74" x2="56.95" y2="23.74"/><line class="cls-11" x1="45.98" y1="31.49" x2="56.95" y2="31.49"/><line class="cls-11" x1="45.98" y1="39.23" x2="56.95" y2="39.23"/><line class="cls-11" x1="45.98" y1="46.97" x2="56.95" y2="46.97"/></g><text class="cls-8" transform="translate(49.8 21.83)">0</text><text class="cls-8" transform="translate(49.8 29.76)">0</text><text class="cls-8" transform="translate(49.8 45.24)">1</text><text class="cls-8" transform="translate(49.8 37.62)">1</text><text class="cls-8" transform="translate(49.8 53.33)">1</text><path class="cls-4" d="M45.47,15.5V9c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-5" d="M56,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,9v6H46V9A3.28,3.28,0,0,1,46,8.1,2.18,2.18,0,0,1,47,8h9m0-1H47c-1.65,0-2,.35-2,2v7H58V9c0-1.65-.35-2-2-2Z"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_bmp_heap.svg b/web/pgadmin/static/js/Explain/img/ex_bmp_heap.svg
new file mode 100644
index 000000000..8eed51610
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_bmp_heap.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{opacity:0.7;}.cls-10,.cls-13,.cls-15,.cls-4{fill:none;}.cls-4{stroke:#c18f35;}.cls-10,.cls-12,.cls-13,.cls-14,.cls-15,.cls-4{stroke-linejoin:round;}.cls-5{fill:#addff3;}.cls-14,.cls-6{fill:#2980b9;}.cls-11,.cls-7{fill:#34495e;}.cls-8{fill:#d3d4f3;}.cls-9{fill:#666bd1;}.cls-10{stroke:#666bd1;}.cls-11{font-size:6px;font-family:Helvetica-Bold, Helvetica;font-weight:700;}.cls-12{fill:#528d19;}.cls-12,.cls-13{stroke:#528d19;}.cls-13,.cls-15{stroke-linecap:round;stroke-width:2px;}.cls-14,.cls-15{stroke:#2980b9;}</style></defs><title>ex_bmp_heap</title><g id="ex_bmp_heap"><path class="cls-1" d="M37,56.5c-1.37,0-1.5-.13-1.5-1.5V9c0-.9.43-1.5,2.5-1.5H55c2.07,0,2.5.6,2.5,1.5V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M55,8c2,0,2,.55,2,1V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,56H37a3.25,3.25,0,0,1-.9-.08h0A2.17,2.17,0,0,1,36,55V9c0-.45,0-1,2-1H55m0-1H38c-1.65,0-3,.35-3,2V55c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V9c0-1.65-1.35-2-3-2Z"/><g class="cls-3"><line class="cls-4" x1="35.98" y1="23.74" x2="56.94" y2="23.74"/><line class="cls-4" x1="35.98" y1="31.49" x2="56.94" y2="31.49"/><line class="cls-4" x1="35.98" y1="39.23" x2="56.94" y2="39.23"/><line class="cls-4" x1="35.98" y1="46.97" x2="56.94" y2="46.97"/></g><g class="cls-3"><line class="cls-4" x1="50.04" y1="16" x2="50.04" y2="56.11"/><line class="cls-4" x1="43.07" y1="16" x2="43.07" y2="56.11"/></g><path class="cls-5" d="M35.47,15.5V9c0-1.37.13-1.5,1.5-1.5H56c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-6" d="M56,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,9v6H36V9A3.28,3.28,0,0,1,36,8.1,2.18,2.18,0,0,1,37,8H56m0-1H37c-1.65,0-2,.35-2,2v7H58V9c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M36.71,25l5.12.08v5.39l-5.12-.08Z"/><path class="cls-7" d="M51.07,40.48l5.12.08V46l-5.12-.08Z"/><path class="cls-8" d="M8,56.5c-1.37,0-1.5-.13-1.5-1.5V10c0-2.07.6-2.5,1.5-2.5h9c.9,0,1.5.43,1.5,2.5V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-9" d="M17,8c.45,0,1,0,1,2V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,17,56H8a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,7,55V10c0-2,.55-2,1-2h9m0-1H8C6.35,7,6,8.35,6,10V55c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V10c0-1.65-.35-3-2-3Z"/><path class="cls-5" d="M6.5,15.5V9c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-6" d="M17,8a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,18,9v6H7V9a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,8,8h9m0-1H8C6.35,7,6,7.35,6,9v7H19V9c0-1.65-.35-2-2-2Z"/><g class="cls-3"><line class="cls-10" x1="7.01" y1="23.74" x2="17.97" y2="23.74"/><line class="cls-10" x1="7.01" y1="31.49" x2="17.97" y2="31.49"/><line class="cls-10" x1="7.01" y1="39.23" x2="17.97" y2="39.23"/><line class="cls-10" x1="7.01" y1="46.97" x2="17.97" y2="46.97"/></g><text class="cls-11" transform="translate(10.82 21.83)">1</text><text class="cls-11" transform="translate(10.82 29.76)">0</text><text class="cls-11" transform="translate(10.82 45.24)">1</text><text class="cls-11" transform="translate(10.82 37.62)">0</text><text class="cls-11" transform="translate(10.82 53.33)">0</text><polygon class="cls-12" points="17.81 27.49 21.19 24.96 21.19 30.02 17.81 27.49"/><line class="cls-13" x1="21.19" y1="27.49" x2="36.16" y2="27.49"/><polygon class="cls-12" points="17.81 43.01 21.19 40.48 21.19 45.54 17.81 43.01"/><line class="cls-13" x1="21.19" y1="43.01" x2="50.04" y2="43.01"/><polygon class="cls-14" points="30.47 51.68 27.94 48.3 33 48.3 30.47 51.68"/><line class="cls-15" x1="30.47" y1="48.3" x2="30.47" y2="17.32"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_bmp_index.svg b/web/pgadmin/static/js/Explain/img/ex_bmp_index.svg
new file mode 100644
index 000000000..8e78f5c0a
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_bmp_index.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{opacity:0.7;}.cls-14,.cls-4,.cls-9{fill:none;}.cls-4{stroke:#c18f35;}.cls-13,.cls-14,.cls-4,.cls-8,.cls-9{stroke-linejoin:round;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-15,.cls-7,.cls-8{fill:#34495e;}.cls-8,.cls-9{stroke:#34495e;}.cls-14,.cls-9{stroke-linecap:round;stroke-width:2px;}.cls-10,.cls-12{fill:#d3d4f3;}.cls-11{fill:#666bd1;}.cls-12{stroke:#666bd1;stroke-miterlimit:10;}.cls-13{fill:#528d19;}.cls-13,.cls-14{stroke:#528d19;}.cls-15{font-size:6px;font-family:Helvetica-Bold, Helvetica;font-weight:700;}</style></defs><title>ex_bmp_index</title><g id="ex_bmp_index"><path class="cls-1" d="M37,54.5c-1.37,0-1.5-.13-1.5-1.5V16.5h22V53c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M57,17V53a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,54H37a3.25,3.25,0,0,1-.9-.08h0A2.17,2.17,0,0,1,36,53V17H57m1-1H35V53c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V16Z"/><g class="cls-3"><line class="cls-4" x1="36.03" y1="23.74" x2="56.99" y2="23.74"/><line class="cls-4" x1="36.03" y1="31.49" x2="56.99" y2="31.49"/><line class="cls-4" x1="36.03" y1="39.23" x2="56.99" y2="39.23"/><line class="cls-4" x1="36.03" y1="46.97" x2="56.99" y2="46.97"/></g><g class="cls-3"><line class="cls-4" x1="50.08" y1="17" x2="50.08" y2="54.11"/><line class="cls-4" x1="43.12" y1="17" x2="43.12" y2="54.11"/></g><path class="cls-5" d="M35.52,16.5V10c0-.9.43-1.5,2.5-1.5H56c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-6" d="M56,9a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,10v6H36V10c0-.45,0-1,2-1H56m0-1H38c-1.65,0-3,.35-3,2v7H58V10c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M36.76,25l5.12.08v5.39l-5.12-.08Z"/><path class="cls-7" d="M36.76,40.48l5.12.08V46l-5.12-.08Z"/><polygon class="cls-8" points="30.2 51.68 27.68 48.3 32.73 48.3 30.2 51.68"/><line class="cls-9" x1="30.2" y1="48.3" x2="30.2" y2="17.32"/><path class="cls-10" d="M10,54.5c-1.37,0-1.5-.13-1.5-1.5V16.5h12V53c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-11" d="M20,17V53a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,19,54H10a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,9,53V17H20m1-1H8V53c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V16Z"/><path class="cls-5" d="M8.5,16.5V10c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-6" d="M19,9a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,20,10v6H9V10a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,10,9h9m0-1H10c-1.65,0-2,.35-2,2v7H21V10c0-1.65-.35-2-2-2Z"/><line class="cls-12" x1="9" y1="23.74" x2="19.99" y2="23.74"/><line class="cls-12" x1="9" y1="31.49" x2="19.99" y2="31.49"/><line class="cls-12" x1="9" y1="39.23" x2="19.99" y2="39.23"/><line class="cls-12" x1="9" y1="46.98" x2="19.99" y2="46.98"/><polygon class="cls-13" points="17.81 27.49 21.19 24.96 21.19 30.02 17.81 27.49"/><line class="cls-14" x1="21.19" y1="27.49" x2="36.16" y2="27.49"/><polygon class="cls-13" points="17.81 43.22 21.19 40.69 21.19 45.75 17.81 43.22"/><line class="cls-14" x1="21.19" y1="43.22" x2="36.16" y2="43.22"/><text class="cls-15" transform="translate(12.82 22.45)">0</text><text class="cls-15" transform="translate(12.82 30.02)">1</text><text class="cls-15" transform="translate(12.82 45.24)">1</text><text class="cls-15" transform="translate(12.82 37.33)">0</text><text class="cls-15" transform="translate(12.82 52.73)">0</text></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_bmp_or.svg b/web/pgadmin/static/js/Explain/img/ex_bmp_or.svg
new file mode 100644
index 000000000..47bb86296
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_bmp_or.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-11{fill:none;stroke-linejoin:round;}.cls-1{stroke:#34495e;stroke-linecap:round;stroke-width:4px;}.cls-2,.cls-4{fill:#d3d4f3;}.cls-3{fill:#666bd1;}.cls-4{stroke:#666bd1;stroke-miterlimit:10;}.cls-5{font-size:6px;fill:#34495e;font-family:Helvetica-Bold, Helvetica;font-weight:700;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}.cls-8{fill:#f0ecb6;}.cls-9{fill:#c18f35;}.cls-10{opacity:0.7;}.cls-11{stroke:#c18f35;}</style></defs><title>ex_bmp_or</title><g id="ex_bmp_or"><line class="cls-1" x1="32" y1="15.84" x2="32" y2="48.16"/><path class="cls-2" d="M10,56.5c-1.37,0-1.5-.13-1.5-1.5V15.46h12V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M20,16V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,19,56H10a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,9,55V16H20m1-1H8V55c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V15Z"/><line class="cls-4" x1="9.01" y1="23.74" x2="19.97" y2="23.74"/><line class="cls-4" x1="9.01" y1="31.49" x2="19.97" y2="31.49"/><line class="cls-4" x1="9.01" y1="39.23" x2="19.97" y2="39.23"/><line class="cls-4" x1="9.01" y1="46.97" x2="19.97" y2="46.97"/><text class="cls-5" transform="translate(12.82 21.83)">1</text><text class="cls-5" transform="translate(12.82 29.76)">0</text><text class="cls-5" transform="translate(12.82 45.24)">1</text><text class="cls-5" transform="translate(12.82 37.62)">0</text><text class="cls-5" transform="translate(12.82 53.33)">0</text><path class="cls-6" d="M8.5,15.5V9c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-7" d="M19,8a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,20,9v6H9V9a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,10,8h9m0-1H10C8.35,7,8,7.35,8,9v7H21V9c0-1.65-.35-2-2-2Z"/><path class="cls-8" d="M45,56.5c-1.37,0-1.5-.13-1.5-1.5V15.46h12V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-9" d="M55,16V55a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,54,56H45a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,44,55V16H55m1-1H43V55c0,1.65.35,2,2,2h9c1.65,0,2-.35,2-2V15Z"/><g class="cls-10"><line class="cls-11" x1="44.03" y1="23.74" x2="54.99" y2="23.74"/><line class="cls-11" x1="44.03" y1="31.49" x2="54.99" y2="31.49"/><line class="cls-11" x1="44.03" y1="39.23" x2="54.99" y2="39.23"/><line class="cls-11" x1="44.03" y1="46.97" x2="54.99" y2="46.97"/></g><text class="cls-5" transform="translate(47.84 21.83)">0</text><text class="cls-5" transform="translate(47.84 29.76)">0</text><text class="cls-5" transform="translate(47.84 45.24)">1</text><text class="cls-5" transform="translate(47.84 37.62)">1</text><text class="cls-5" transform="translate(47.84 53.33)">1</text><path class="cls-6" d="M43.52,15.5V9c0-1.37.13-1.5,1.5-1.5h9c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-7" d="M54,8a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,55,9v6H44V9a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,45,8h9m0-1H45c-1.65,0-2,.35-2,2v7H56V9c0-1.65-.35-2-2-2Z"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_broadcast_motion.svg b/web/pgadmin/static/js/Explain/img/ex_broadcast_motion.svg
new file mode 100644
index 000000000..b34ba1a8a
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_broadcast_motion.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{opacity:0.7;}.cls-4,.cls-6{fill:none;}.cls-4{stroke:#c18f35;}.cls-4,.cls-5,.cls-6{stroke-linejoin:round;}.cls-5{fill:#34495e;}.cls-5,.cls-6{stroke:#34495e;}.cls-6{stroke-linecap:round;}</style></defs><title>ex_broadcast_motion</title><g id="ex_broadcast_motion"><path class="cls-1" d="M21.56,51.69c-1.34,0-1.49-.13-1.5-1.41V14.15c0-1.28.15-1.41,1.5-1.41h9c1.34,0,1.48.13,1.5,1.41V50.28c0,1.28-.15,1.41-1.5,1.41Z"/><path class="cls-2" d="M21.56,13.24h9a3.09,3.09,0,0,1,.9.08h0a2,2,0,0,1,.1.84V50.27a3,3,0,0,1-.08.81,2.15,2.15,0,0,1-.92.1h-9a3.09,3.09,0,0,1-.9-.08h0a2,2,0,0,1-.1-.84V14.16a3,3,0,0,1,.08-.81,2.15,2.15,0,0,1,.92-.1m0-1c-1.61,0-2,.34-2,1.9h0V50.28h0c0,1.56.38,1.9,2,1.9h9c1.61,0,2-.34,2-1.9h0V14.15h0c0-1.56-.38-1.9-2-1.9Z"/><g class="cls-3"><line class="cls-4" x1="20.57" y1="19.93" x2="31.54" y2="19.93"/><line class="cls-4" x1="20.57" y1="27.67" x2="31.54" y2="27.67"/><line class="cls-4" x1="20.57" y1="35.42" x2="31.54" y2="35.42"/><line class="cls-4" x1="20.57" y1="43.16" x2="31.54" y2="43.16"/></g><polygon class="cls-5" points="43.3 54.68 42.27 51.55 40.01 54.79 43.3 54.68"/><line class="cls-6" x1="41.57" y1="53.35" x2="33.5" y2="47.64"/><polygon class="cls-5" points="43.3 9.32 42.27 12.45 40.01 9.21 43.3 9.32"/><line class="cls-6" x1="41.57" y1="10.65" x2="33.5" y2="16.36"/><polygon class="cls-5" points="45.44 31.63 42.8 29.65 42.8 33.6 45.44 31.63"/><line class="cls-6" x1="43.25" y1="31.53" x2="33.36" y2="31.48"/><polygon class="cls-5" points="45.04 18.95 41.88 18 43.23 21.71 45.04 18.95"/><line class="cls-6" x1="42.95" y1="19.61" x2="33.64" y2="22.95"/><polygon class="cls-5" points="45.04 43.48 41.88 44.44 43.23 40.72 45.04 43.48"/><line class="cls-6" x1="42.95" y1="42.83" x2="33.64" y2="39.49"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_cte_scan.svg b/web/pgadmin/static/js/Explain/img/ex_cte_scan.svg
new file mode 100644
index 000000000..ac1861067
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_cte_scan.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-9{fill:#2980b9;}.cls-1,.cls-2{stroke:#2980b9;}.cls-1,.cls-2,.cls-6,.cls-7{stroke-linejoin:round;}.cls-2{fill:#6c6c6c;stroke-linecap:round;stroke-width:2px;}.cls-3{fill:#caf0b7;}.cls-4{fill:#36c13d;}.cls-5,.cls-6{opacity:0.7;}.cls-6,.cls-7{fill:none;stroke:#36c13d;}.cls-8{fill:#addff3;}.cls-10{font-size:12px;fill:#34495e;font-family:HelveticaNeue-Bold, Helvetica Neue;font-weight:700;letter-spacing:0.1em;}</style></defs><title>ex_cte_scan</title><g id="ex_cte_scan"><polygon class="cls-1" points="9.97 50.75 6.49 46.1 13.45 46.1 9.97 50.75"/><line class="cls-2" x1="9.97" y1="46.74" x2="9.97" y2="15.76"/><path class="cls-3" d="M18,57.44c-1.37,0-1.5-.13-1.5-1.5V15.5h41V55.94c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M57,16V55.94a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H18a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16H57m1-1H16V55.94c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V15Z"/><g class="cls-5"><line class="cls-6" x1="24.7" y1="56.94" x2="24.7" y2="15.94"/><line class="cls-6" x1="41.1" y1="56.94" x2="41.1" y2="15.94"/><line class="cls-6" x1="32.9" y1="56.94" x2="32.9" y2="15.94"/><line class="cls-6" x1="49.3" y1="56.94" x2="49.3" y2="15.94"/><g class="cls-5"><line class="cls-7" x1="17" y1="23.94" x2="56.97" y2="23.94"/><line class="cls-7" x1="17" y1="32.31" x2="56.97" y2="32.31"/><line class="cls-7" x1="17" y1="40.68" x2="56.97" y2="40.68"/><line class="cls-7" x1="17" y1="49.05" x2="56.97" y2="49.05"/></g></g><path class="cls-8" d="M49.31,15.49v-9H56c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-9" d="M56,7a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,57,8v7H49.81V7H56m0-1H48.81V16H58V8c0-1.65-.35-2-2-2Z"/><path class="cls-8" d="M16.51,15.49V8c0-1.37.13-1.5,1.5-1.5H24.7v9Z"/><path class="cls-9" d="M24.2,7v8H17V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,18,7H24.2m1-1H18c-1.65,0-2,.35-2,2v8H25.2V6Z"/><rect class="cls-8" x="41.1" y="6.49" width="8.19" height="9"/><path class="cls-9" d="M48.79,7v8H41.6V7h7.18m1-1H40.61V16h9.19V6Z"/><rect class="cls-8" x="32.91" y="6.49" width="8.19" height="9"/><path class="cls-9" d="M40.6,7v8H33.41V7H40.6m1-1H32.42V16H41.6V6Z"/><rect class="cls-8" x="24.72" y="6.49" width="8.17" height="9"/><path class="cls-9" d="M32.4,7v8H25.22V7H32.4m1-1H24.22V16H33.4V6Z"/><text class="cls-10" transform="translate(23.35 38.4)">CTE</text></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_delete.svg b/web/pgadmin/static/js/Explain/img/ex_delete.svg
new file mode 100644
index 000000000..89d96805b
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_delete.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3,.cls-4{opacity:0.7;}.cls-4,.cls-5{fill:none;stroke:#c18f36;stroke-linejoin:round;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}.cls-8{fill:#f64d12;}</style></defs><title>ex_delete</title><g id="ex_delete"><path class="cls-1" d="M18,57.44c-1.37,0-1.5-.13-1.5-1.5V15.5h41V55.94c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M57,16V55.94a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H18a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16H57m1-1H16V55.94c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V15Z"/><g class="cls-3"><line class="cls-4" x1="24.7" y1="56.94" x2="24.7" y2="15.94"/><line class="cls-4" x1="41.1" y1="56.94" x2="41.1" y2="15.94"/><line class="cls-4" x1="32.9" y1="56.94" x2="32.9" y2="15.94"/><line class="cls-4" x1="49.3" y1="56.94" x2="49.3" y2="15.94"/><g class="cls-3"><line class="cls-5" x1="17" y1="23.94" x2="56.97" y2="23.94"/><line class="cls-5" x1="17" y1="32.31" x2="56.97" y2="32.31"/><line class="cls-5" x1="17" y1="40.68" x2="56.97" y2="40.68"/><line class="cls-5" x1="17" y1="49.05" x2="56.97" y2="49.05"/></g></g><path class="cls-6" d="M49.31,15.49v-9H56c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-7" d="M56,7a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,57,8v7H49.81V7H56m0-1H48.81V16H58V8c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M16.51,15.49V8c0-1.37.13-1.5,1.5-1.5H24.7v9Z"/><path class="cls-7" d="M24.2,7v8H17V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,18,7H24.2m1-1H18c-1.65,0-2,.35-2,2v8H25.2V6Z"/><rect class="cls-6" x="41.1" y="6.49" width="8.19" height="9"/><path class="cls-7" d="M48.79,7v8H41.6V7h7.18m1-1H40.61V16h9.19V6Z"/><rect class="cls-6" x="32.91" y="6.49" width="8.19" height="9"/><path class="cls-7" d="M40.6,7v8H33.41V7H40.6m1-1H32.42V16H41.6V6Z"/><rect class="cls-6" x="24.72" y="6.49" width="8.17" height="9"/><path class="cls-7" d="M32.4,7v8H25.22V7H32.4m1-1H24.22V16H33.4V6Z"/><path class="cls-8" d="M17.5,24.94h6.2v6.37H17.5Z"/><path class="cls-8" d="M25.71,41.72h6.2v6.37h-6.2Z"/><path class="cls-8" d="M33.94,41.72h6.2v6.37h-6.2Z"/><path class="cls-2" d="M6,29.22a3.37,3.37,0,0,1,5.71-2.43h0l.13.13.05.05L23.45,38.52a.4.4,0,0,1,.18.2l1.63,5.82v.06h0a.46.46,0,0,1,0,.12.41.41,0,0,1,0,.12s0,0,0,0h0a.4.4,0,0,1-.06.1l0,0,0,0-.1.06h0l-.12,0-.12,0h-.07l-5.82-1.63a.4.4,0,0,1-.2-.18L6.95,31.58a.4.4,0,0,1-.08-.12A3.35,3.35,0,0,1,6,29.22ZM18.9,42.38l1-2h0L10,30.41,8.46,31.93Zm5,.55-.84.84,1.17.33Zm-1-3.48-2.26,1.09L19.6,42.8l2.57.72L23.66,42Zm-.42-.7L12.08,28.31l-1.52,1.52,9.93,9.93h0ZM6.83,29.25a2.55,2.55,0,0,0,.83,1.88h0l.22.22,3.62-3.62-.22-.22h0a2.56,2.56,0,0,0-4.45,1.73Z"/><polygon class="cls-1" points="18.9 42.38 19.9 40.36 19.9 40.35 9.97 30.42 8.45 31.94 18.9 42.38"/><polygon class="cls-1" points="12.07 28.32 10.55 29.84 20.48 39.77 20.49 39.76 22.52 38.76 12.07 28.32"/><path class="cls-1" d="M6.85,29.21a2.55,2.55,0,0,0,.83,1.88h0l.22.22,3.62-3.62-.22-.22h0a2.56,2.56,0,0,0-4.45,1.73Z"/><polygon class="cls-1" points="22.94 39.45 20.69 40.54 19.6 42.8 22.17 43.52 23.66 42.03 22.94 39.45"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_foreign_scan.svg b/web/pgadmin/static/js/Explain/img/ex_foreign_scan.svg
new file mode 100644
index 000000000..a9f1c3d2b
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_foreign_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-9{fill:#34495e;}.cls-1,.cls-2{stroke:#34495e;}.cls-1,.cls-2,.cls-6{stroke-linejoin:round;}.cls-2,.cls-6{fill:none;}.cls-2{stroke-linecap:round;stroke-width:2px;}.cls-3{fill:#dae7e7;}.cls-4{fill:#83b8bd;}.cls-5{opacity:0.8;}.cls-6{stroke:#83b8bd;}.cls-7{fill:#addff3;}.cls-8{fill:#2980b9;}.cls-9{font-size:12px;font-family:HelveticaNeue-Bold, Helvetica Neue;font-weight:700;letter-spacing:0.1em;}</style></defs><title>ex_foreign_scan</title><polygon class="cls-1" points="9.97 50.75 6.49 46.1 13.45 46.1 9.97 50.75"/><line class="cls-2" x1="9.97" y1="46.74" x2="9.97" y2="15.76"/><path class="cls-3" d="M18,57.44c-1.37,0-1.5-.13-1.5-1.5V15.5h41V55.94c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M57,16V55.94a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H18a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16H57m1-1H16V55.94c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V15Z"/><g class="cls-5"><line class="cls-6" x1="24.7" y1="56.94" x2="24.7" y2="15.94"/><line class="cls-6" x1="41.1" y1="56.94" x2="41.1" y2="15.94"/><line class="cls-6" x1="32.9" y1="56.94" x2="32.9" y2="15.94"/><line class="cls-6" x1="49.3" y1="56.94" x2="49.3" y2="15.94"/><line class="cls-6" x1="17" y1="23.94" x2="56.97" y2="23.94"/><line class="cls-6" x1="17" y1="32.31" x2="56.97" y2="32.31"/><line class="cls-6" x1="17" y1="40.68" x2="56.97" y2="40.68"/><line class="cls-6" x1="17" y1="49.05" x2="56.97" y2="49.05"/></g><path class="cls-7" d="M49.31,15.49v-9H56c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-8" d="M56,7a3.25,3.25,0,0,1,.9.08h0A2.17,2.17,0,0,1,57,8v7H49.81V7H56m0-1H48.81V16H58V8c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M16.51,15.49V8c0-1.37.13-1.5,1.5-1.5H24.7v9Z"/><path class="cls-8" d="M24.2,7v8H17V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,18,7H24.2m1-1H18c-1.65,0-2,.35-2,2v8H25.2V6Z"/><rect class="cls-7" x="41.1" y="6.49" width="8.19" height="9"/><path class="cls-8" d="M48.79,7v8H41.6V7h7.18m1-1H40.61V16h9.19V6Z"/><rect class="cls-7" x="32.91" y="6.49" width="8.19" height="9"/><path class="cls-8" d="M40.6,7v8H33.41V7H40.6m1-1H32.42V16H41.6V6Z"/><rect class="cls-7" x="24.72" y="6.49" width="8.17" height="9"/><path class="cls-8" d="M32.4,7v8H25.22V7H32.4m1-1H24.22V16H33.4V6Z"/><text class="cls-9" transform="translate(23.35 38.4)">CTE</text></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_gather_merge.svg b/web/pgadmin/static/js/Explain/img/ex_gather_merge.svg
new file mode 100644
index 000000000..4c6f1dc7e
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_gather_merge.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64.03 64"><defs><style>.cls-1{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3,.cls-6{fill:#f0ecb6;}.cls-4{fill:#c18f36;}.cls-5{opacity:0.8;}.cls-6{stroke:#c18f36;stroke-miterlimit:10;}.cls-7{fill:#addff3;}.cls-8{fill:#2980b9;}.cls-10,.cls-9{fill:#528d19;stroke:#528d19;stroke-linejoin:round;}.cls-10{stroke-linecap:round;stroke-width:2px;}</style></defs><title>gather_merge</title><g id="ex_append"><path class="cls-1" d="M12.82,14.3c-1.37,0-1.5-.12-1.5-1.5v-4c0-1.38.13-1.5,1.5-1.5h4.5v7Z"/><path class="cls-2" d="M16.82,7.82v6h-4a3.54,3.54,0,0,1-.9-.07h0a2.16,2.16,0,0,1-.1-.93v-4a2.7,2.7,0,0,1,.08-.9s.19-.1.92-.1h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5v-8Z"/><path class="cls-1" d="M17.32,14.3v-7H22.1c1.38,0,1.5.12,1.5,1.5v4c0,1.38-.12,1.5-1.5,1.5Z"/><path class="cls-2" d="M22.1,7.82a3.44,3.44,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v4a3.71,3.71,0,0,1-.07.9,2.16,2.16,0,0,1-.93.1H17.82v-6H22.1m0-1H16.82v8H22.1c1.65,0,2-.35,2-2v-4c0-1.65-.35-2-2-2Z"/><path class="cls-1" d="M27.87,14.3c-1.37,0-1.5-.12-1.5-1.5v-4c0-1.38.13-1.5,1.5-1.5h4.5v7Z"/><path class="cls-2" d="M31.87,7.82v6h-4a3.64,3.64,0,0,1-.9-.07h0a2.4,2.4,0,0,1-.1-.93v-4a3.18,3.18,0,0,1,.08-.9,2.08,2.08,0,0,1,.92-.1h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5v-8Z"/><path class="cls-1" d="M32.38,14.3v-7h4.78c1.37,0,1.5.12,1.5,1.5v4c0,1.38-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M37.16,7.82a3.39,3.39,0,0,1,.89.07h0a2.4,2.4,0,0,1,.1.93v4a3.18,3.18,0,0,1-.08.9,2.08,2.08,0,0,1-.92.1H32.88v-6h4.28m0-1H31.88v8h5.28c1.65,0,2-.35,2-2v-4c0-1.65-.35-2-2-2Z"/><path class="cls-1" d="M42.93,14.3c-1.38,0-1.5-.12-1.5-1.5v-4c0-1.38.12-1.5,1.5-1.5h4.5v7Z"/><path class="cls-2" d="M46.93,7.82v6h-4a3.44,3.44,0,0,1-.9-.07h0a2.16,2.16,0,0,1-.1-.93v-4a3.71,3.71,0,0,1,.07-.9,2.16,2.16,0,0,1,.93-.1h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5v-8Z"/><path class="cls-1" d="M47.43,14.3v-7h4.78c1.37,0,1.5.12,1.5,1.5v4c0,1.38-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M52.21,7.82a3.54,3.54,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v4a2.7,2.7,0,0,1-.08.9s-.19.1-.92.1H47.93v-6h4.28m0-1H46.93v8h5.28c1.65,0,2-.35,2-2v-4c0-1.65-.35-2-2-2Z"/><path class="cls-3" d="M21.21,57.44c-1.38,0-1.5-.13-1.5-1.5V44.49h24.6V55.94c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M43.81,45V55.94a4,4,0,0,1-.07.9,2.4,2.4,0,0,1-.93.1H21.21a3.11,3.11,0,0,1-.9-.08h0a2.13,2.13,0,0,1-.1-.92V45h23.6m1-1H19.21V55.94c0,1.65.35,2,2,2h21.6c1.65,0,2-.35,2-2V44Z"/><g class="cls-5"><line class="cls-6" x1="27.91" y1="56.93" x2="27.91" y2="44.93"/><line class="cls-6" x1="36.12" y1="56.93" x2="36.12" y2="44.93"/><line class="cls-6" x1="20.2" y1="50.93" x2="43.79" y2="50.93"/></g><path class="cls-7" d="M36.13,44.49v-6h6.69c1.37,0,1.5.12,1.5,1.5v4.5Z"/><path class="cls-8" d="M42.82,39a3.54,3.54,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v4H36.63V39h6.19m0-1H35.63v7h9.19V40c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M19.71,44.49V40c0-1.38.12-1.5,1.5-1.5H27.9v6Z"/><path class="cls-8" d="M27.4,39v5H20.21V40a3.71,3.71,0,0,1,.07-.9,2.16,2.16,0,0,1,.93-.1H27.4m1-1H21.21c-1.65,0-2,.35-2,2v5H28.4V38Z"/><rect class="cls-7" x="27.92" y="38.49" width="8.19" height="6"/><path class="cls-8" d="M35.61,39v5H28.42V39h7.18m1-1H27.43v7h9.19V38Z"/><polygon class="cls-9" points="27.1 36.38 27.96 32.25 23.38 34.39 27.1 36.38"/><line class="cls-10" x1="25.55" y1="33.06" x2="18.96" y2="18.94"/><polygon class="cls-9" points="32.4 36.2 34.92 32.82 29.86 32.82 32.4 36.2"/><line class="cls-10" x1="32.39" y1="32.54" x2="32.39" y2="18.02"/><polygon class="cls-9" points="38.52 36.38 37.65 32.25 42.24 34.39 38.52 36.38"/><line class="cls-10" x1="40.06" y1="33.06" x2="46.65" y2="18.94"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_gather_motion.svg b/web/pgadmin/static/js/Explain/img/ex_gather_motion.svg
new file mode 100644
index 000000000..b3f2081ad
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_gather_motion.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#addff3;}.cls-2{fill:#2980b9;}.cls-3{fill:none;stroke:#d04465;stroke-linecap:round;stroke-linejoin:round;}.cls-4{fill:#d04465;}.cls-5{fill:#f3c7d7;}</style></defs><title>ex_gather_motion</title><g id="ex_delete"><rect class="cls-1" x="47.19" y="45.43" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M55.69,45.93v8h-8v-8h8m0-1h-8a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="7.81" y="45.43" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M16.31,45.93v8h-8v-8h8m0-1h-8a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="27.51" y="9.57" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M36,10.07v8H28v-8h8m0-1H28a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><line class="cls-3" x1="18.14" y1="44.2" x2="21.92" y2="42.02"/><path class="cls-4" d="M24,40.82a12.09,12.09,0,0,0-2,3.36l-.35-2-1.55-1.3A12.08,12.08,0,0,0,24,40.82Z"/><line class="cls-3" x1="42.06" y1="42.22" x2="45.84" y2="44.4"/><path class="cls-4" d="M40,41a12.09,12.09,0,0,1,2,3.36l.35-2,1.55-1.3A12.08,12.08,0,0,1,40,41Z"/><line class="cls-3" x1="32.09" y1="20.07" x2="32.08" y2="24.43"/><path class="cls-4" d="M32.08,26.85a12.09,12.09,0,0,0-1.9-3.42l1.9.69,1.9-.69A12.08,12.08,0,0,0,32.08,26.85Z"/><circle class="cls-5" cx="32.08" cy="35.93" r="7.5"/><path class="cls-4" d="M32.08,28.93a7,7,0,1,1-7,7,7,7,0,0,1,7-7m0-1a8,8,0,1,0,8,8,8,8,0,0,0-8-8Z"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_group.svg b/web/pgadmin/static/js/Explain/img/ex_group.svg
new file mode 100644
index 000000000..44aa97fe2
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_group.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-10,.cls-3{fill:#d3d4f3;}.cls-4{fill:#666bd1;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7{opacity:0.7;}.cls-8,.cls-9{fill:none;stroke-linejoin:round;}.cls-11,.cls-8{stroke:#c18f35;}.cls-10,.cls-12,.cls-9{stroke:#666bd1;}.cls-10,.cls-11,.cls-12{stroke-miterlimit:10;}.cls-11{fill:#34495e;}.cls-12{fill:#528d19;}</style></defs><title>ex_group</title><rect class="cls-1" x="19.03" y="16" width="35.98" height="24.96"/><path class="cls-2" d="M54.52,16.5v24h-35v-24h35m1-1h-37v26h37v-26Z"/><path class="cls-3" d="M20.53,56c-1.37,0-1.5-.13-1.5-1.5V41H55V54.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M54.52,41.5v13a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-33a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-13h35m1-1h-37v14c0,1.65.35,2,2,2h33c1.65,0,2-.35,2-2v-14Z"/><path class="cls-5" d="M46.87,16V8h6.63c1.37,0,1.5.13,1.5,1.5V16Z"/><path class="cls-6" d="M53.49,8.5a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v6H47.37v-7h6.13m0-1H46.37v9h9.13v-7c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M19,16V9.5c0-1.37.13-1.5,1.5-1.5h6.59v8Z"/><path class="cls-6" d="M26.59,8.5v7H19.51v-6a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.09m1-1H20.51c-1.65,0-2,.35-2,2v7h9.09v-9Z"/><rect class="cls-5" x="40.31" y="8" width="7.05" height="8"/><path class="cls-6" d="M46.86,8.5v7H40.8v-7h6m1-1h-8v9h8.05v-9Z"/><rect class="cls-5" x="33.74" y="8" width="6.57" height="8"/><path class="cls-6" d="M39.81,8.5v7H34.24v-7h5.56m1-1H33.25v9h7.57v-9Z"/><rect class="cls-5" x="26.62" y="8" width="7.1" height="8"/><path class="cls-6" d="M33.23,8.5v7H27.12v-7h6.1m1-1h-8.1v9h8.11v-9Z"/><g class="cls-7"><line class="cls-8" x1="26.61" y1="40.5" x2="26.61" y2="16.5"/><line class="cls-8" x1="40.29" y1="40.5" x2="40.29" y2="16.5"/><line class="cls-8" x1="33.7" y1="40.5" x2="33.7" y2="16.5"/><line class="cls-8" x1="47.37" y1="40.5" x2="47.37" y2="16.5"/><line class="cls-9" x1="26.61" y1="55.62" x2="26.61" y2="40.5"/><line class="cls-9" x1="40.29" y1="55.62" x2="40.29" y2="40.5"/><line class="cls-9" x1="33.7" y1="55.62" x2="33.7" y2="40.5"/><line class="cls-9" x1="47.37" y1="55.62" x2="47.37" y2="40.5"/><line class="cls-8" x1="19.49" y1="24.35" x2="54.46" y2="24.35"/><line class="cls-10" x1="19.52" y1="48.5" x2="54.49" y2="48.5"/><line class="cls-8" x1="19.49" y1="32.73" x2="54.46" y2="32.73"/></g><path class="cls-11" d="M14.88,38.29l-.53,0a4.34,4.34,0,0,1-.67-.11A5.39,5.39,0,0,1,13,37.9a2.9,2.9,0,0,1-.7-.41,3.31,3.31,0,0,1-.83-.9A3.65,3.65,0,0,1,11,35.52c-.09-.39-.22-5.25-.3-5.66a2.39,2.39,0,0,0-.47-1.06,2.66,2.66,0,0,0-1-.73.69.69,0,0,1,0-1.17,2.65,2.65,0,0,0,1-.73,2.37,2.37,0,0,0,.47-1.06c.08-.41.13-2.91.14-3.38A7,7,0,0,1,11,20.45a3.66,3.66,0,0,1,.43-1.07,3.34,3.34,0,0,1,.83-.9,2.89,2.89,0,0,1,.7-.41,5.12,5.12,0,0,1,.72-.23,4.11,4.11,0,0,1,.67-.11l.53,0v.44a4.47,4.47,0,0,0-2.05.51,2.82,2.82,0,0,0-1.06,1.08,3.58,3.58,0,0,0-.4,1.4c0,.5-.14,3.7-.2,4a2.77,2.77,0,0,1-.26.83,2.41,2.41,0,0,1-.44.6,2.52,2.52,0,0,1-.62.46,3.65,3.65,0,0,1-.64.27,3.52,3.52,0,0,1-.6.13c-.19,0-.19.1,0,.12a3.5,3.5,0,0,1,.6.13,4,4,0,0,1,.64.27,2.53,2.53,0,0,1,.62.46,2.37,2.37,0,0,1,.44.6,2.76,2.76,0,0,1,.26.83,2.56,2.56,0,0,1,.13.93s0,3.6.06,4.1a3.59,3.59,0,0,0,.4,1.4,2.81,2.81,0,0,0,1.06,1.08,4.42,4.42,0,0,0,2.05.51Z"/><path class="cls-12" d="M14.88,55.3l-.53,0a4.34,4.34,0,0,1-.67-.11,5.39,5.39,0,0,1-.72-.23,2.9,2.9,0,0,1-.7-.41,3.31,3.31,0,0,1-.83-.9A3.65,3.65,0,0,1,11,52.53c-.09-.39-.22-1.25-.3-1.66a2.39,2.39,0,0,0-.47-1.06,2.66,2.66,0,0,0-1-.73.69.69,0,0,1,0-1.17,2.65,2.65,0,0,0,1-.73,2.37,2.37,0,0,0,.47-1.06c.08-.41.21-1.28.3-1.66a3.66,3.66,0,0,1,.43-1.07,3.34,3.34,0,0,1,.83-.9,2.89,2.89,0,0,1,.7-.41,5.12,5.12,0,0,1,.72-.23,4.11,4.11,0,0,1,.67-.11l.53,0v.44a4.47,4.47,0,0,0-2.05.51,2.82,2.82,0,0,0-1.06,1.08,3.58,3.58,0,0,0-.4,1.4,7.66,7.66,0,0,1-.2,1,2.77,2.77,0,0,1-.26.83,2.41,2.41,0,0,1-.44.6,2.52,2.52,0,0,1-.62.46,3.65,3.65,0,0,1-.64.27,3.52,3.52,0,0,1-.6.13c-.19,0-.19.1,0,.12a3.5,3.5,0,0,1,.6.13,4,4,0,0,1,.64.27,2.53,2.53,0,0,1,.62.46,2.37,2.37,0,0,1,.44.6,2.76,2.76,0,0,1,.26.83,7.66,7.66,0,0,1,.2,1,3.59,3.59,0,0,0,.4,1.4,2.81,2.81,0,0,0,1.06,1.08,4.42,4.42,0,0,0,2.05.51Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash.svg b/web/pgadmin/static/js/Explain/img/ex_hash.svg
new file mode 100644
index 000000000..dfd8c6cb7
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-10,.cls-11{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3{fill:#addff3;}.cls-4{fill:#2980b9;}.cls-5{opacity:0.7;}.cls-12,.cls-6{fill:none;}.cls-10,.cls-11,.cls-6{stroke:#666bd1;}.cls-12,.cls-14,.cls-15,.cls-6{stroke-linejoin:round;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{fill:#72ceee;stroke:#2980b9;}.cls-15,.cls-9{stroke-linecap:round;}.cls-10,.cls-11,.cls-9{stroke-miterlimit:10;}.cls-11,.cls-12{opacity:0.8;}.cls-12{stroke:#c18f35;}.cls-13{fill:#c00;}.cls-14,.cls-15{fill:#528d19;stroke:#528d19;}.cls-15{stroke-width:2px;}</style></defs><title>ex_hash</title><path class="cls-1" d="M10.38,56.5c-1.37,0-1.5-.13-1.5-1.5V15.5h9V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M17.36,16V55a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-6a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V16h8m1-1h-10V55c0,1.65.35,2,2,2h6c1.65,0,2-.35,2-2V15Z"/><path class="cls-3" d="M8.88,15.5V9c0-1.37.13-1.5,1.5-1.5h6c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-4" d="M16.36,8a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v6h-8V9a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6m0-1h-6c-1.65,0-2,.35-2,2v7h10V9c0-1.65-.35-2-2-2Z"/><g class="cls-5"><line class="cls-6" x1="9.39" y1="23.74" x2="17.35" y2="23.74"/><line class="cls-6" x1="9.39" y1="31.49" x2="17.35" y2="31.49"/><line class="cls-6" x1="9.39" y1="39.23" x2="17.35" y2="39.23"/><line class="cls-6" x1="9.39" y1="46.97" x2="17.35" y2="46.97"/></g><path class="cls-1" d="M47.4,46.22v-28h6.22c1.37,0,1.5.13,1.5,1.5v25c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M53.62,18.72a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v25a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.9v-27h5.72m0-1H46.9v29h6.72c1.65,0,2-.35,2-2v-25c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M26.64,46.22c-1.37,0-1.5-.13-1.5-1.5V24.22H47.42v22Z"/><path class="cls-8" d="M46.92,24.72v21H26.64a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-20H46.92m1-1H24.64v21c0,1.65.35,2,2,2H47.92v-23Z"/><path class="cls-3" d="M25.14,24.22v-4.5c0-1.37.13-1.5,1.5-1.5H47.42v6Z"/><path class="cls-4" d="M46.92,18.72v5H25.64v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H46.92m1-1H26.64c-1.65,0-2,.35-2,2v5H47.92v-7Z"/><line class="cls-9" x1="32.6" y1="24.05" x2="32.6" y2="18.24"/><line class="cls-10" x1="55" y1="24.22" x2="47.92" y2="24.22"/><line class="cls-11" x1="55" y1="31.49" x2="47.92" y2="31.49"/><line class="cls-11" x1="55" y1="38.96" x2="47.92" y2="38.96"/><line class="cls-9" x1="40.35" y1="24.05" x2="40.35" y2="18.24"/><line class="cls-12" x1="32.6" y1="46.05" x2="32.6" y2="24.72"/><line class="cls-12" x1="40.35" y1="46.05" x2="40.35" y2="24.72"/><line class="cls-12" x1="25.17" y1="31.57" x2="47.42" y2="31.57"/><line class="cls-12" x1="25.17" y1="38.96" x2="47.42" y2="38.96"/><path class="cls-13" d="M10.27,32.86h6.38V38H10.27Z"/><path class="cls-13" d="M33.81,32.71h5.4v5.23h-5.4Z"/><polygon class="cls-14" points="33.43 35.53 30.05 33 30.05 38.06 33.43 35.53"/><line class="cls-15" x1="29.77" y1="35.53" x2="19.33" y2="35.53"/><polygon class="cls-14" points="36.84 7.67 34.31 11.05 39.37 11.05 36.84 7.67"/><line class="cls-15" x1="36.84" y1="11.33" x2="36.84" y2="30.77"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_anti_join.svg b/web/pgadmin/static/js/Explain/img/ex_hash_anti_join.svg
new file mode 100644
index 000000000..39a9354b8
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_anti_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-10{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3{fill:#addff3;}.cls-4{fill:#2980b9;}.cls-5{opacity:0.7;}.cls-11,.cls-16,.cls-6{fill:none;}.cls-10,.cls-6{stroke:#666bd1;}.cls-11,.cls-13,.cls-14,.cls-6{stroke-linejoin:round;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{fill:#72ceee;stroke:#2980b9;}.cls-14,.cls-9{stroke-linecap:round;}.cls-10,.cls-16,.cls-9{stroke-miterlimit:10;}.cls-11{stroke:#c18f35;}.cls-12{fill:#c00;}.cls-13,.cls-14{fill:#528d19;stroke:#528d19;}.cls-14{stroke-width:2px;}.cls-15{fill:#def4fd;}.cls-16{stroke:#c00;stroke-width:1.5px;}</style></defs><title>ex_hash_anti_join</title><rect class="cls-1" x="8.66" y="7.5" width="8.98" height="49" rx="1.5" ry="1.5"/><path class="cls-2" d="M16.14,8a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92V55a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-6a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V9a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6m0-1h-6c-1.65,0-2,.35-2,2V55c0,1.65.35,2,2,2h6c1.65,0,2-.35,2-2V9c0-1.65-.35-2-2-2Z"/><path class="cls-3" d="M8.66,15.5V9c0-1.37.13-1.5,1.5-1.5h6c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-4" d="M16.14,8a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v6h-8V9a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6m0-1h-6c-1.65,0-2,.35-2,2v7h10V9c0-1.65-.35-2-2-2Z"/><g class="cls-5"><line class="cls-6" x1="9.17" y1="23.74" x2="17.13" y2="23.74"/><line class="cls-6" x1="9.17" y1="31.49" x2="17.13" y2="31.49"/><line class="cls-6" x1="9.17" y1="39.23" x2="17.13" y2="39.23"/><line class="cls-6" x1="9.17" y1="46.97" x2="17.13" y2="46.97"/></g><path class="cls-1" d="M47.18,46.22v-28H53.4c1.37,0,1.5.13,1.5,1.5v25c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M53.4,18.72a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v25a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.68v-27H53.4m0-1H46.68v29H53.4c1.65,0,2-.35,2-2v-25c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M26.42,46.22c-1.37,0-1.5-.13-1.5-1.5V24.22H47.2v22Z"/><path class="cls-8" d="M46.7,24.72v21H26.42a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-20H46.7m1-1H24.42v21c0,1.65.35,2,2,2H47.7v-23Z"/><path class="cls-3" d="M24.92,24.22v-4.5c0-1.37.13-1.5,1.5-1.5H47.2v6Z"/><path class="cls-4" d="M46.7,18.72v5H25.42v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H46.7m1-1H26.42c-1.65,0-2,.35-2,2v5H47.7v-7Z"/><line class="cls-9" x1="32.38" y1="24.05" x2="32.38" y2="18.24"/><line class="cls-10" x1="54.78" y1="24.22" x2="47.7" y2="24.22"/><line class="cls-9" x1="40.13" y1="24.05" x2="40.13" y2="18.24"/><line class="cls-11" x1="32.38" y1="46.05" x2="32.38" y2="24.72"/><line class="cls-11" x1="40.13" y1="46.05" x2="40.13" y2="24.72"/><line class="cls-11" x1="24.95" y1="31.57" x2="47.2" y2="31.57"/><line class="cls-11" x1="24.95" y1="38.96" x2="47.2" y2="38.96"/><path class="cls-12" d="M10.05,32.86h6.37V38H10.05Z"/><path class="cls-12" d="M33.59,32.71H39v5.23h-5.4Z"/><polygon class="cls-13" points="33.21 35.53 29.83 33 29.83 38.06 33.21 35.53"/><line class="cls-14" x1="29.55" y1="35.53" x2="19.11" y2="35.53"/><polygon class="cls-13" points="36.62 7.67 34.09 11.05 39.15 11.05 36.62 7.67"/><line class="cls-14" x1="36.62" y1="11.33" x2="36.62" y2="30.77"/><circle class="cls-15" cx="50.8" cy="34.37" r="4.29"/><path class="cls-12" d="M50.8,30.83a3.54,3.54,0,1,1-3.54,3.54,3.55,3.55,0,0,1,3.54-3.54m0-1.5a5,5,0,1,0,5,5,5,5,0,0,0-5-5Z"/><line class="cls-16" x1="47.52" y1="36.64" x2="54.03" y2="31.91"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_semi_join.svg b/web/pgadmin/static/js/Explain/img/ex_hash_semi_join.svg
new file mode 100644
index 000000000..c0d9886d6
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_semi_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-10,.cls-11{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3{fill:#addff3;}.cls-4{fill:#2980b9;}.cls-5{opacity:0.7;}.cls-12,.cls-6{fill:none;}.cls-10,.cls-11,.cls-6{stroke:#666bd1;}.cls-12,.cls-14,.cls-15,.cls-6{stroke-linejoin:round;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{fill:#72ceee;stroke:#2980b9;}.cls-15,.cls-9{stroke-linecap:round;}.cls-10,.cls-11,.cls-9{stroke-miterlimit:10;}.cls-11{opacity:0.8;}.cls-12{stroke:#c18f35;}.cls-13{fill:#c00;}.cls-14,.cls-15{fill:#528d19;stroke:#528d19;}.cls-15{stroke-width:2px;}</style></defs><title>ex_hash_semi_join</title><path class="cls-1" d="M10.16,56.5c-1.37,0-1.5-.13-1.5-1.5V15.5h9V55c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M17.14,16V55a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-6a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16h8m1-1h-10V55c0,1.65.35,2,2,2h6c1.65,0,2-.35,2-2V15Z"/><path class="cls-3" d="M8.66,15.5V9c0-1.37.13-1.5,1.5-1.5h6c1.37,0,1.5.13,1.5,1.5v6.5Z"/><path class="cls-4" d="M16.14,8a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v6h-8V9a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6m0-1h-6c-1.65,0-2,.35-2,2v7h10V9c0-1.65-.35-2-2-2Z"/><g class="cls-5"><line class="cls-6" x1="9.17" y1="23.74" x2="17.13" y2="23.74"/><line class="cls-6" x1="9.17" y1="31.49" x2="17.13" y2="31.49"/><line class="cls-6" x1="9.17" y1="39.23" x2="17.13" y2="39.23"/><line class="cls-6" x1="9.17" y1="46.97" x2="17.13" y2="46.97"/></g><path class="cls-1" d="M47.18,46.22v-28H53.4c1.37,0,1.5.13,1.5,1.5v25c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M53.4,18.72a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v25a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.68v-27H53.4m0-1H46.68v29H53.4c1.65,0,2-.35,2-2v-25c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M26.42,46.22c-1.37,0-1.5-.13-1.5-1.5V24.22H47.2v22Z"/><path class="cls-8" d="M46.7,24.72v21H26.42a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-20H46.7m1-1H24.42v21c0,1.65.35,2,2,2H47.7v-23Z"/><path class="cls-3" d="M24.92,24.22v-4.5c0-1.37.13-1.5,1.5-1.5H47.2v6Z"/><path class="cls-4" d="M46.7,18.72v5H25.42v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H46.7m1-1H26.42c-1.65,0-2,.35-2,2v5H47.7v-7Z"/><line class="cls-9" x1="32.38" y1="24.05" x2="32.38" y2="18.24"/><line class="cls-10" x1="54.78" y1="24.22" x2="47.7" y2="24.22"/><line class="cls-11" x1="54.78" y1="31.49" x2="47.7" y2="31.49"/><line class="cls-11" x1="54.78" y1="38.96" x2="47.7" y2="38.96"/><line class="cls-9" x1="40.13" y1="24.05" x2="40.13" y2="18.24"/><line class="cls-12" x1="32.38" y1="46.05" x2="32.38" y2="24.72"/><line class="cls-12" x1="40.13" y1="46.05" x2="40.13" y2="24.72"/><line class="cls-12" x1="24.95" y1="31.57" x2="47.2" y2="31.57"/><line class="cls-12" x1="24.95" y1="38.96" x2="47.2" y2="38.96"/><path class="cls-13" d="M10.05,32.86h6.37V38H10.05Z"/><path class="cls-13" d="M33.59,32.71H39v5.23h-5.4Z"/><polygon class="cls-14" points="33.21 35.53 29.83 33 29.83 38.06 33.21 35.53"/><line class="cls-15" x1="29.55" y1="35.53" x2="19.11" y2="35.53"/><polygon class="cls-14" points="36.62 7.67 34.09 11.05 39.15 11.05 36.62 7.67"/><line class="cls-15" x1="36.62" y1="11.33" x2="36.62" y2="30.77"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_setop_except.svg b/web/pgadmin/static/js/Explain/img/ex_hash_setop_except.svg
new file mode 100644
index 000000000..6d8319931
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_setop_except.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-3{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-10,.cls-3{stroke:#c18f35;}.cls-3,.cls-9{stroke-miterlimit:10;}.cls-10,.cls-3,.cls-8{opacity:0.8;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{fill:#d3d4f3;}.cls-7{fill:#666bd1;}.cls-10,.cls-8{fill:none;stroke-linejoin:round;}.cls-8{stroke:#666bd1;}.cls-9{fill:#34495e;stroke:#528d19;}.cls-11{fill:#88f625;}</style></defs><title>ex_hash_setop_except</title><path class="cls-1" d="M13.16,58.21c-1.37,0-1.5-.13-1.5-1.5V39.52h7V56.71c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M18.15,40V56.71a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V40h6m1-1h-8V56.71c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39Z"/><line class="cls-3" x1="12.05" y1="45.87" x2="18.29" y2="45.87"/><line class="cls-3" x1="12.05" y1="51.79" x2="18.29" y2="51.79"/><path class="cls-4" d="M11.66,11.67V7.17c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,6.17a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M13.16,30.36c-1.37,0-1.5-.13-1.5-1.5V11.67h7V28.86c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M18.15,12.17V28.86a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V12.17h6m1-1h-8V28.86c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V11.17Z"/><line class="cls-8" x1="12.05" y1="18.02" x2="18.29" y2="18.02"/><line class="cls-8" x1="12.05" y1="23.94" x2="18.29" y2="23.94"/><path class="cls-4" d="M11.66,39.52V35c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,34a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6V35a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8V35c0-1.65-.35-2-2-2Z"/><path class="cls-9" d="M22.25,41.12a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46V37c0-.68,0-1.25,0-1.72A9.28,9.28,0,0,1,26,34.1a2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,24.3,22.9a4.47,4.47,0,0,0-2.05-.51V22l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07A7,7,0,0,1,26.28,26c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-1" d="M35.18,37.79c-1.37,0-1.5-.13-1.5-1.5V24.79H41v13Z"/><path class="cls-2" d="M40.46,25.29v12H35.18a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-11h6.28m1-1H33.18v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-6" d="M40.95,37.79v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M47.73,25.29v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H41.45v-12h6.28m1-1H40.45v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-4" d="M33.68,24.79v-4.5c0-1.37.13-1.5,1.5-1.5H41v6Z"/><path class="cls-5" d="M40.46,19.29v5H34.18v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H35.18c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M40.95,24.79v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M46.73,19.29a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H41.45v-5h5.28m0-1H40.45v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="40.71" y1="31.14" x2="48.2" y2="31.14"/><line class="cls-10" x1="33.71" y1="31.14" x2="40.45" y2="31.14"/><path class="cls-11" d="M36,50.42a8.4,8.4,0,0,1,5-7.66,8.4,8.4,0,1,0,0,15.33A8.4,8.4,0,0,1,36,50.42Z"/><path class="cls-2" d="M37.56,43.4a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><path class="cls-7" d="M44.43,43.4a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_setop_except_all.svg b/web/pgadmin/static/js/Explain/img/ex_hash_setop_except_all.svg
new file mode 100644
index 000000000..c47b1081d
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_setop_except_all.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-3{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-10,.cls-3{stroke:#c18f35;}.cls-3,.cls-9{stroke-miterlimit:10;}.cls-10,.cls-3,.cls-8{opacity:0.8;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{fill:#d3d4f3;}.cls-7{fill:#666bd1;}.cls-10,.cls-8{fill:none;stroke-linejoin:round;}.cls-8{stroke:#666bd1;}.cls-12,.cls-9{fill:#34495e;}.cls-9{stroke:#528d19;}.cls-11{fill:#88f625;}.cls-12{font-size:9px;font-family:Helvetica-Bold, Helvetica;font-weight:700;}</style></defs><title>ex_hash_setop_except_all</title><path class="cls-1" d="M13.16,58.21c-1.37,0-1.5-.13-1.5-1.5V39.52h7V56.71c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M18.15,40V56.71a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V40h6m1-1h-8V56.71c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39Z"/><line class="cls-3" x1="12.05" y1="45.87" x2="18.29" y2="45.87"/><line class="cls-3" x1="12.05" y1="51.79" x2="18.29" y2="51.79"/><path class="cls-4" d="M11.66,11.67V7.17c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,6.17a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M13.16,30.36c-1.37,0-1.5-.13-1.5-1.5V11.67h7V28.86c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M18.15,12.17V28.86a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V12.17h6m1-1h-8V28.86c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V11.17Z"/><line class="cls-8" x1="12.05" y1="18.02" x2="18.29" y2="18.02"/><line class="cls-8" x1="12.05" y1="23.94" x2="18.29" y2="23.94"/><path class="cls-4" d="M11.66,39.52V35c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,34a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6V35a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8V35c0-1.65-.35-2-2-2Z"/><path class="cls-9" d="M22.25,41.12a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46V37c0-.68,0-1.25,0-1.72A9.28,9.28,0,0,1,26,34.1a2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,24.3,22.9a4.47,4.47,0,0,0-2.05-.51V22l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07A7,7,0,0,1,26.28,26c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-1" d="M35.18,37.79c-1.37,0-1.5-.13-1.5-1.5V24.79H41v13Z"/><path class="cls-2" d="M40.46,25.29v12H35.18a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H33.18v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-6" d="M40.95,37.79v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M47.73,25.29v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H41.45v-12h6.28m1-1H40.45v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-4" d="M33.68,24.79v-4.5c0-1.37.13-1.5,1.5-1.5H41v6Z"/><path class="cls-5" d="M40.46,19.29v5H34.18v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H35.18c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M40.95,24.79v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M46.73,19.29a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4H41.45v-5h5.28m0-1H40.45v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="40.71" y1="31.14" x2="48.2" y2="31.14"/><line class="cls-10" x1="33.71" y1="31.14" x2="40.45" y2="31.14"/><path class="cls-11" d="M36,50.42a8.4,8.4,0,0,1,5-7.66,8.4,8.4,0,1,0,0,15.33A8.4,8.4,0,0,1,36,50.42Z"/><path class="cls-2" d="M37.56,43.4a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><path class="cls-7" d="M44.43,43.4a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><text class="cls-12" transform="translate(20.35 18.02)">All</text></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect.svg b/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect.svg
new file mode 100644
index 000000000..b3c4ab7af
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#88f625;}.cls-2,.cls-4{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-11,.cls-4{stroke:#c18f35;}.cls-10,.cls-4{stroke-miterlimit:10;}.cls-11,.cls-4,.cls-9{opacity:0.8;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7{fill:#d3d4f3;}.cls-8{fill:#666bd1;}.cls-11,.cls-9{fill:none;stroke-linejoin:round;}.cls-9{stroke:#666bd1;}.cls-10{fill:#34495e;stroke:#528d19;}</style></defs><title>ex_hash_setop_intersect</title><path class="cls-1" d="M41,42.7A8.4,8.4,0,0,0,41,58,8.4,8.4,0,0,0,41,42.7Z"/><path class="cls-2" d="M13.16,58.27c-1.37,0-1.5-.13-1.5-1.5V39.58h7V56.77c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M18.15,40.08V56.77a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V40.08h6m1-1h-8V56.77c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39.08Z"/><line class="cls-4" x1="12.05" y1="45.93" x2="18.29" y2="45.93"/><line class="cls-4" x1="12.05" y1="51.85" x2="18.29" y2="51.85"/><path class="cls-5" d="M11.66,11.73V7.23c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M17.15,6.23a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M13.16,30.42c-1.37,0-1.5-.13-1.5-1.5V11.73h7V28.92c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M18.15,12.23V28.92a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V12.23h6m1-1h-8V28.92c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V11.23Z"/><line class="cls-9" x1="12.05" y1="18.08" x2="18.29" y2="18.08"/><line class="cls-9" x1="12.05" y1="24" x2="18.29" y2="24"/><path class="cls-5" d="M11.66,39.58v-4.5c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M17.15,34.08a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-10" d="M22.25,41.18a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,24.3,23a4.47,4.47,0,0,0-2.05-.51V22l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07,7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-2" d="M35.18,37.85c-1.37,0-1.5-.13-1.5-1.5V24.85H41v13Z"/><path class="cls-3" d="M40.46,25.35v12H35.18a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-11h6.28m1-1H33.18v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-7" d="M40.95,37.85v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M47.73,25.35v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H41.45v-12h6.28m1-1H40.45v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-5" d="M33.68,24.85v-4.5c0-1.37.13-1.5,1.5-1.5H41v6Z"/><path class="cls-6" d="M40.46,19.35v5H34.18v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H35.18c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-5" d="M40.95,24.85v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M46.73,19.35a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H41.45v-5h5.28m0-1H40.45v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-9" x1="40.71" y1="31.2" x2="48.2" y2="31.2"/><line class="cls-11" x1="33.71" y1="31.2" x2="40.45" y2="31.2"/><path class="cls-3" d="M37.56,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><path class="cls-8" d="M44.43,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect_all.svg b/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect_all.svg
new file mode 100644
index 000000000..0ba7e4469
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_setop_intersect_all.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#88f625;}.cls-2,.cls-4{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-11,.cls-4{stroke:#c18f35;}.cls-10,.cls-4{stroke-miterlimit:10;}.cls-11,.cls-4,.cls-9{opacity:0.8;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7{fill:#d3d4f3;}.cls-8{fill:#666bd1;}.cls-11,.cls-9{fill:none;stroke-linejoin:round;}.cls-9{stroke:#666bd1;}.cls-10,.cls-12{fill:#34495e;}.cls-10{stroke:#528d19;}.cls-12{font-size:9px;font-family:Helvetica-Bold, Helvetica;font-weight:700;}</style></defs><title>ex_hash_setop_intersect_all</title><path class="cls-1" d="M41,42.7A8.4,8.4,0,0,0,41,58,8.4,8.4,0,0,0,41,42.7Z"/><path class="cls-2" d="M13.16,58.27c-1.37,0-1.5-.13-1.5-1.5V39.58h7V56.77c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M18.15,40.08V56.77a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V40.08h6m1-1h-8V56.77c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39.08Z"/><line class="cls-4" x1="12.05" y1="45.93" x2="18.29" y2="45.93"/><line class="cls-4" x1="12.05" y1="51.85" x2="18.29" y2="51.85"/><path class="cls-5" d="M11.66,11.73V7.23c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M17.15,6.23a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M13.16,30.42c-1.37,0-1.5-.13-1.5-1.5V11.73h7V28.92c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M18.15,12.23V28.92a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V12.23h6m1-1h-8V28.92c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V11.23Z"/><line class="cls-9" x1="12.05" y1="18.08" x2="18.29" y2="18.08"/><line class="cls-9" x1="12.05" y1="24" x2="18.29" y2="24"/><path class="cls-5" d="M11.66,39.58v-4.5c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M17.15,34.08a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-10" d="M22.25,41.18a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,24.3,23a4.47,4.47,0,0,0-2.05-.51V22l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07,7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-2" d="M35.18,37.85c-1.37,0-1.5-.13-1.5-1.5V24.85H41v13Z"/><path class="cls-3" d="M40.46,25.35v12H35.18a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-11h6.28m1-1H33.18v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-7" d="M40.95,37.85v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M47.73,25.35v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H41.45v-12h6.28m1-1H40.45v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-5" d="M33.68,24.85v-4.5c0-1.37.13-1.5,1.5-1.5H41v6Z"/><path class="cls-6" d="M40.46,19.35v5H34.18v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H35.18c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-5" d="M40.95,24.85v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-6" d="M46.73,19.35a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4H41.45v-5h5.28m0-1H40.45v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-9" x1="40.71" y1="31.2" x2="48.2" y2="31.2"/><line class="cls-11" x1="33.71" y1="31.2" x2="40.45" y2="31.2"/><path class="cls-3" d="M37.56,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><path class="cls-8" d="M44.43,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><text class="cls-12" transform="translate(20.98 18.36)">All</text></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_hash_setop_unknown.svg b/web/pgadmin/static/js/Explain/img/ex_hash_setop_unknown.svg
new file mode 100644
index 000000000..bfaadcff3
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_hash_setop_unknown.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-3{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-10,.cls-3{stroke:#c18f35;}.cls-3,.cls-9{stroke-miterlimit:10;}.cls-10,.cls-3,.cls-8{opacity:0.8;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{fill:#d3d4f3;}.cls-7{fill:#666bd1;}.cls-10,.cls-8{fill:none;stroke-linejoin:round;}.cls-8{stroke:#666bd1;}.cls-9{fill:#34495e;stroke:#528d19;}.cls-11{font-size:9px;fill:#c00;font-family:Helvetica-Bold, Helvetica;font-weight:700;}</style></defs><title>ex_hash_setop_unknown</title><path class="cls-1" d="M13.16,58.27c-1.37,0-1.5-.13-1.5-1.5V39.58h7V56.77c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M18.15,40.08V56.77a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V40.08h6m1-1h-8V56.77c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39.08Z"/><line class="cls-3" x1="12.05" y1="45.93" x2="18.29" y2="45.93"/><line class="cls-3" x1="12.05" y1="51.85" x2="18.29" y2="51.85"/><path class="cls-4" d="M11.66,11.73V7.23c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,6.23a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M13.16,30.42c-1.37,0-1.5-.13-1.5-1.5V11.73h7V28.92c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M18.15,12.23V28.92a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V12.23h6m1-1h-8V28.92c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V11.23Z"/><line class="cls-8" x1="12.05" y1="18.08" x2="18.29" y2="18.08"/><line class="cls-8" x1="12.05" y1="24" x2="18.29" y2="24"/><path class="cls-4" d="M11.66,39.58v-4.5c0-1.37.13-1.5,1.5-1.5h4c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M17.15,34.08a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4h-6v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1h-4c-1.65,0-2,.35-2,2v5h8v-5c0-1.65-.35-2-2-2Z"/><path class="cls-9" d="M22.25,41.18a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,24.3,23a4.47,4.47,0,0,0-2.05-.51V22l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07,7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-1" d="M35.18,37.85c-1.37,0-1.5-.13-1.5-1.5V24.85H41v13Z"/><path class="cls-2" d="M40.46,25.35v12H35.18a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-11h6.28m1-1H33.18v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-6" d="M40.95,37.85v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-7" d="M47.73,25.35v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H41.45v-12h6.28m1-1H40.45v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-4" d="M33.68,24.85v-4.5c0-1.37.13-1.5,1.5-1.5H41v6Z"/><path class="cls-5" d="M40.46,19.35v5H34.18v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H35.18c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M40.95,24.85v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M46.73,19.35a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H41.45v-5h5.28m0-1H40.45v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="40.71" y1="31.2" x2="48.2" y2="31.2"/><line class="cls-10" x1="33.71" y1="31.2" x2="40.45" y2="31.2"/><path class="cls-2" d="M37.56,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><path class="cls-7" d="M44.43,43.46a6.9,6.9,0,1,1-6.9,6.9,6.91,6.91,0,0,1,6.9-6.9m0-1.5a8.4,8.4,0,1,0,8.4,8.4,8.4,8.4,0,0,0-8.4-8.4Z"/><text class="cls-11" transform="translate(38.29 53.85)">?</text></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_index_only_scan.svg b/web/pgadmin/static/js/Explain/img/ex_index_only_scan.svg
new file mode 100644
index 000000000..e27c79924
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_index_only_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:none;stroke:#747474;stroke-width:0.75px;}.cls-1,.cls-7{stroke-linecap:round;}.cls-1,.cls-6,.cls-7{stroke-linejoin:round;}.cls-2{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#addff3;}.cls-5,.cls-6,.cls-7{fill:#2980b9;}.cls-6,.cls-7{stroke:#2980b9;}.cls-7{stroke-width:2px;}</style></defs><title>ex_index_only_scan</title><line class="cls-1" x1="36.89" y1="24.41" x2="24.9" y2="31.3"/><line class="cls-1" x1="36.89" y1="24.41" x2="48.87" y2="31.3"/><line class="cls-1" x1="24.9" y1="37.22" x2="18.92" y2="43.22"/><line class="cls-1" x1="24.9" y1="37.22" x2="30.89" y2="43.22"/><line class="cls-1" x1="48.87" y1="37.22" x2="42.55" y2="43.22"/><line class="cls-1" x1="48.87" y1="37.22" x2="54.85" y2="43.22"/><path class="cls-2" d="M33.39,23.91c-1.37,0-1.5-.13-1.5-1.5V18.22h10v4.19c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M41.38,18.72v3.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-7a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V18.72h9m1-1h-11v4.69c0,1.65.35,2,2,2h7c1.65,0,2-.35,2-2V17.72Z"/><rect class="cls-2" x="20.91" y="31.03" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M27.39,31.53a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V32.53a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V32.53c0-1.65-.35-2-2-2Z"/><rect class="cls-2" x="14.92" y="43.72" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M21.41,44.22a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V45.22a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V45.22c0-1.65-.35-2-2-2Z"/><rect class="cls-2" x="26.89" y="43.72" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M33.38,44.22a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V45.22a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V45.22c0-1.65-.35-2-2-2Z"/><rect class="cls-2" x="38.89" y="43.72" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M45.38,44.22a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V45.22a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V45.22c0-1.65-.35-2-2-2Z"/><rect class="cls-2" x="50.86" y="43.72" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M57.35,44.22a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V45.22a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V45.22c0-1.65-.35-2-2-2Z"/><rect class="cls-2" x="44.88" y="30.97" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M51.36,31.47a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-5a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V32.47a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V32.47c0-1.65-.35-2-2-2Z"/><path class="cls-4" d="M31.89,18.28V16.09c0-1.37.13-1.5,1.5-1.5h7c1.37,0,1.5.13,1.5,1.5v2.19Z"/><path class="cls-5" d="M40.38,15.09a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v1.69h-9V16.09a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h7m0-1h-7c-1.65,0-2,.35-2,2v2.69h11V16.09c0-1.65-.35-2-2-2Z"/><polygon class="cls-6" points="8.13 49.45 4.65 44.8 11.61 44.8 8.13 49.45"/><line class="cls-7" x1="8.13" y1="45.43" x2="8.13" y2="18.46"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_index_scan.svg b/web/pgadmin/static/js/Explain/img/ex_index_scan.svg
new file mode 100644
index 000000000..0b5b1b03d
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_index_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:none;stroke:#747474;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}.cls-2,.cls-8{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{fill:#c18f36;}.cls-7{opacity:0.8;}.cls-8{stroke:#c18f36;stroke-miterlimit:10;}</style></defs><title>ex_index_scan</title><line class="cls-1" x1="13.85" y1="36.23" x2="19.12" y2="44.7"/><line class="cls-1" x1="13.85" y1="36.23" x2="19.09" y2="27.92"/><line class="cls-1" x1="27.64" y1="44.33" x2="35.64" y2="50.23"/><line class="cls-1" x1="27.64" y1="44.33" x2="35.64" y2="38.92"/><rect class="cls-2" x="19.26" y="41.65" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M25.75,42.15a3.54,3.54,0,0,1,.9.07h0a2.4,2.4,0,0,1,.1.93v2.69a3.18,3.18,0,0,1-.08.9s-.19.1-.92.1h-5a3.11,3.11,0,0,1-.9-.08h0a2.25,2.25,0,0,1-.1-.92V43.15a3.18,3.18,0,0,1,.08-.9s.19-.1.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V43.15c0-1.65-.35-2-2-2Z"/><line class="cls-1" x1="27.64" y1="27.83" x2="35.64" y2="33.73"/><line class="cls-1" x1="27.64" y1="27.83" x2="35.64" y2="22.42"/><rect class="cls-2" x="19.26" y="25.15" width="7.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-3" d="M25.75,25.65a3.54,3.54,0,0,1,.9.07h0a2.4,2.4,0,0,1,.1.93v2.69a3.18,3.18,0,0,1-.08.9s-.19.1-.92.1h-5a3.11,3.11,0,0,1-.9-.08h0a2.25,2.25,0,0,1-.1-.92V26.65a3.18,3.18,0,0,1,.08-.9s.19-.1.92-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V26.65c0-1.65-.35-2-2-2Z"/><path class="cls-2" d="M6.61,40.89c-1.38,0-1.5-.13-1.5-1.5V35.2h8v4.19c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M12.59,35.7v3.69a2.7,2.7,0,0,1-.08.9s-.19.1-.92.1h-5a3,3,0,0,1-.9-.08h0a2.45,2.45,0,0,1-.09-.92V35.7h7m1-1h-9v4.69c0,1.65.35,2,2,2h5c1.65,0,2-.35,2-2V34.7Z"/><path class="cls-4" d="M5.11,35.26V33.07c0-1.38.12-1.5,1.5-1.5h5c1.37,0,1.5.12,1.5,1.5v2.19Z"/><path class="cls-5" d="M11.59,32.07a3.54,3.54,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v1.69h-7V33.07a3.71,3.71,0,0,1,.07-.9,2.16,2.16,0,0,1,.93-.1h5m0-1h-5c-1.65,0-2,.35-2,2v2.69h9V33.07c0-1.65-.35-2-2-2Z"/><path class="cls-2" d="M35.74,53.47c-1.38,0-1.5-.12-1.5-1.5V19.53H58.89V52c0,1.38-.13,1.5-1.5,1.5Z"/><path class="cls-6" d="M58.39,20V52a2.7,2.7,0,0,1-.08.9s-.19.1-.92.1H35.74a3.54,3.54,0,0,1-.9-.07h0a2.16,2.16,0,0,1-.1-.93V20H58.39m1-1H33.74V52c0,1.65.35,2,2,2H57.39c1.65,0,2-.35,2-2V19Z"/><g class="cls-7"><line class="cls-8" x1="42.44" y1="52.97" x2="42.44" y2="19.97"/><line class="cls-8" x1="50.63" y1="52.97" x2="50.63" y2="19.97"/><line class="cls-8" x1="34.73" y1="27.97" x2="58.37" y2="27.97"/><line class="cls-8" x1="34.73" y1="36.34" x2="58.37" y2="36.34"/><line class="cls-8" x1="34.73" y1="44.71" x2="58.37" y2="44.71"/></g><path class="cls-4" d="M50.7,19.53v-9h6.69c1.38,0,1.5.12,1.5,1.5v7.5Z"/><path class="cls-5" d="M57.39,11a3.44,3.44,0,0,1,.9.07h0a2.62,2.62,0,0,1,.09.93v7H51.2V11h6.19m0-1H50.2V20h9.19V12c0-1.65-.35-2-2-2Z"/><path class="cls-4" d="M34.23,19.53V12c0-1.38.13-1.5,1.5-1.5h6.7v9Z"/><path class="cls-5" d="M41.93,11v8h-7.2V12a3.18,3.18,0,0,1,.08-.9,2.08,2.08,0,0,1,.92-.1h6.2m1-1h-7.2c-1.65,0-2,.35-2,2v8h9.2V10Z"/><rect class="cls-4" x="42.45" y="10.53" width="8.17" height="9"/><path class="cls-5" d="M50.12,11v8H43V11h7.17m1-1H42V20h9.17V10Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_insert.svg b/web/pgadmin/static/js/Explain/img/ex_insert.svg
new file mode 100644
index 000000000..202beaa4e
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_insert.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-5{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{fill:#88f625;}.cls-4{opacity:0.8;}.cls-5{stroke:#c18f36;stroke-miterlimit:10;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}</style></defs><title>ex_insert</title><path class="cls-1" d="M16.73,57.47c-1.37,0-1.5-.13-1.5-1.5V15.53h41V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M55.72,16V56a3.29,3.29,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-38a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16h40m1-1h-42V56c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V15Z"/><rect class="cls-3" x="48.52" y="49.53" width="7.22" height="7.44"/><rect class="cls-3" x="40.32" y="49.53" width="7.22" height="7.44"/><rect class="cls-3" x="32.12" y="49.53" width="7.22" height="7.44"/><g class="cls-4"><line class="cls-5" x1="23.43" y1="56.97" x2="23.43" y2="15.97"/><line class="cls-5" x1="39.83" y1="56.97" x2="39.83" y2="15.97"/><line class="cls-5" x1="31.63" y1="56.97" x2="31.63" y2="15.97"/><line class="cls-5" x1="48.03" y1="56.97" x2="48.03" y2="15.97"/><line class="cls-5" x1="15.73" y1="23.97" x2="55.7" y2="23.97"/><line class="cls-5" x1="15.73" y1="32.34" x2="55.7" y2="32.34"/><line class="cls-5" x1="15.73" y1="40.71" x2="55.7" y2="40.71"/><line class="cls-5" x1="15.73" y1="49.08" x2="55.7" y2="49.08"/></g><path class="cls-6" d="M48,15.53v-9h6.69c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-7" d="M54.73,7a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H48.53V7h6.19m0-1H47.53V16h9.19V8c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M15.23,15.53V8c0-1.37.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-7" d="M22.93,7v8H15.73V8a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.19m1-1H16.73c-1.65,0-2,.35-2,2v8h9.19V6Z"/><rect class="cls-6" x="39.83" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M47.52,7v8H40.33V7h7.18m1-1H39.33V16h9.19V6Z"/><rect class="cls-6" x="31.64" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M39.33,7v8H32.14V7h7.18m1-1H31.14V16h9.19V6Z"/><rect class="cls-6" x="23.45" y="6.53" width="8.17" height="9"/><path class="cls-7" d="M31.12,7v8H23.95V7h7.17m1-1H23V16h9.18V6Z"/><path class="cls-2" d="M7.27,28.28A3.37,3.37,0,0,1,13,25.85h0l.13.13.05.05L24.72,37.58a.4.4,0,0,1,.18.2l1.63,5.82v.06h0a.46.46,0,0,1,0,.12.41.41,0,0,1,0,.12s0,0,0,0h0a.4.4,0,0,1-.06.1l0,0,0,0-.1.06h0l-.12,0-.12,0H26l-5.82-1.63a.4.4,0,0,1-.2-.18L8.22,30.64a.4.4,0,0,1-.08-.12A3.35,3.35,0,0,1,7.27,28.28Zm12.9,13.15,1-2h0l-9.93-9.93L9.73,31Zm5,.55-.84.84,1.17.33Zm-1-3.48L22,39.6l-1.09,2.26,2.57.72,1.49-1.49Zm-.42-.7L13.35,27.37l-1.52,1.52,9.93,9.93h0ZM8.1,28.31a2.55,2.55,0,0,0,.83,1.88h0l.22.22,3.62-3.62-.22-.22h0A2.56,2.56,0,0,0,8.1,28.31Z"/><polygon class="cls-1" points="20.17 41.45 21.17 39.42 21.18 39.41 11.25 29.48 9.73 31 20.17 41.45"/><polygon class="cls-1" points="13.35 27.38 11.83 28.9 21.76 38.83 21.77 38.82 23.79 37.83 13.35 27.38"/><path class="cls-1" d="M8.12,28.27A2.55,2.55,0,0,0,9,30.15H9l.22.22,3.62-3.62-.22-.22h0a2.56,2.56,0,0,0-4.45,1.73Z"/><polygon class="cls-1" points="24.22 38.52 21.96 39.6 20.87 41.86 23.44 42.58 24.94 41.09 24.22 38.52"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_join.svg b/web/pgadmin/static/js/Explain/img/ex_join.svg
new file mode 100644
index 000000000..30146a9ea
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#34495e;stroke:#528d19;stroke-miterlimit:10;}.cls-2{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#d3d4f3;}.cls-5{fill:#666bd1;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}.cls-8,.cls-9{fill:none;stroke-linejoin:round;opacity:0.8;}.cls-8{stroke:#666bd1;}.cls-9{stroke:#c18f35;}</style></defs><title>ex_join</title><path class="cls-1" d="M30.27,41.36a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4,2.82,2.82,0,0,0-1.06-1.08,4.47,4.47,0,0,0-2.05-.51V22.2l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9A3.66,3.66,0,0,1,34.15,25a7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-2" d="M41.14,49.88c-1.37,0-1.5-.13-1.5-1.5V19.88h7.28v30Z"/><path class="cls-3" d="M46.42,20.38v29H41.14a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-28h6.28m1-1H39.14v29c0,1.65.35,2,2,2h6.28v-31Z"/><path class="cls-4" d="M46.9,49.88v-30h7.28v28.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-5" d="M53.69,20.38v28a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.4v-29h6.28m1-1H46.4v31h6.28c1.65,0,2-.35,2-2v-29Z"/><path class="cls-6" d="M39.64,19.88v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-7" d="M46.42,14.38v5H40.14v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H41.14c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-6" d="M46.91,19.88v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-7" d="M52.69,14.38a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H47.41v-5h5.28m0-1H46.41v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="46.67" y1="26.23" x2="54.16" y2="26.23"/><line class="cls-9" x1="39.67" y1="26.23" x2="46.4" y2="26.23"/><line class="cls-8" x1="46.67" y1="32.23" x2="54.16" y2="32.23"/><line class="cls-9" x1="39.67" y1="32.23" x2="46.4" y2="32.23"/><line class="cls-8" x1="46.67" y1="38.23" x2="54.16" y2="38.23"/><line class="cls-9" x1="39.67" y1="38.23" x2="46.4" y2="38.23"/><line class="cls-8" x1="46.67" y1="44.23" x2="54.16" y2="44.23"/><line class="cls-9" x1="39.67" y1="44.23" x2="46.4" y2="44.23"/><path class="cls-4" d="M11.34,29.31c-1.37,0-1.5-.13-1.5-1.5V16.31h7.28v13Z"/><path class="cls-5" d="M16.62,16.81v12H11.34a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H9.34v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-4" d="M17.11,29.31v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-5" d="M23.89,16.81v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H17.61v-12h6.28m1-1H16.61v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-6" d="M9.84,16.31v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-7" d="M16.62,10.81v5H10.34v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H11.34c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-6" d="M17.11,16.31v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-7" d="M22.89,10.81a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H17.61v-5h5.28m0-1H16.61v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="16.87" y1="22.66" x2="24.37" y2="22.66"/><line class="cls-8" x1="9.87" y1="22.66" x2="16.61" y2="22.66"/><path class="cls-2" d="M11.34,53.46c-1.37,0-1.5-.13-1.5-1.5V40.46h7.28v13Z"/><path class="cls-3" d="M16.62,41V53H11.34a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V41h6.28m1-1H9.34V52c0,1.65.35,2,2,2h6.28V40Z"/><path class="cls-2" d="M17.11,53.46v-13h7.28V52c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M23.89,41V52a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H17.61V41h6.28m1-1H16.61V54h6.28c1.65,0,2-.35,2-2V40Z"/><path class="cls-6" d="M9.84,40.46V36c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-7" d="M16.62,35v5H10.34V36a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H11.34c-1.65,0-2,.35-2,2v5h8.28V34Z"/><path class="cls-6" d="M17.11,40.46v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-7" d="M22.89,35a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H17.61V35h5.28m0-1H16.61v7h8.28V36c0-1.65-.35-2-2-2Z"/><line class="cls-9" x1="16.87" y1="46.81" x2="24.37" y2="46.81"/><line class="cls-9" x1="9.87" y1="46.81" x2="16.61" y2="46.81"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_limit.svg b/web/pgadmin/static/js/Explain/img/ex_limit.svg
new file mode 100644
index 000000000..244317a72
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_limit.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-4{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{opacity:0.8;}.cls-4{stroke:#c18f36;}.cls-4,.cls-7{stroke-miterlimit:10;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7{fill:#d3d4f3;stroke:#c00;}.cls-7,.cls-9{stroke-linecap:round;stroke-width:1.5px;}.cls-8,.cls-9{fill:#528d19;}.cls-9{stroke:#528d19;stroke-linejoin:round;}</style></defs><title>ex_limit</title><path class="cls-1" d="M12.79,53.47c-1.37,0-1.5-.13-1.5-1.5V19.53h41V52c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M51.77,20V52a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-38a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V20h40m1-1h-42V52c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V19Z"/><g class="cls-3"><line class="cls-4" x1="19.48" y1="52.97" x2="19.48" y2="19.97"/><line class="cls-4" x1="35.88" y1="52.97" x2="35.88" y2="19.97"/><line class="cls-4" x1="27.68" y1="52.97" x2="27.68" y2="19.97"/><line class="cls-4" x1="44.08" y1="52.97" x2="44.08" y2="19.97"/><line class="cls-4" x1="11.78" y1="27.97" x2="51.75" y2="27.97"/><line class="cls-4" x1="11.78" y1="36.34" x2="51.75" y2="36.34"/><line class="cls-4" x1="11.78" y1="44.71" x2="51.75" y2="44.71"/></g><path class="cls-5" d="M44.09,19.53v-9h6.69c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-6" d="M50.78,11a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H44.59V11h6.19m0-1H43.59V20h9.19V12c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M11.29,19.53V12c0-1.37.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-6" d="M19,11v8H11.79V12a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H19m1-1H12.79c-1.65,0-2,.35-2,2v8H20V10Z"/><rect class="cls-5" x="35.88" y="10.53" width="8.18" height="9"/><path class="cls-6" d="M43.57,11v8H36.38V11h7.18m1-1H35.39V20h9.19V10Z"/><rect class="cls-5" x="27.69" y="10.53" width="8.19" height="9"/><path class="cls-6" d="M35.38,11v8H28.19V11h7.18m1-1H27.2V20h9.19V10Z"/><rect class="cls-5" x="19.5" y="10.53" width="8.17" height="9"/><path class="cls-6" d="M27.18,11v8H20V11h7.17m1-1H19V20h9.18V10Z"/><line class="cls-7" x1="6.31" y1="19.76" x2="57.77" y2="19.77"/><line class="cls-7" x1="6.31" y1="36.33" x2="57.77" y2="36.34"/><polygon class="cls-8" points="56.25 34.71 58.78 31.33 53.72 31.33 56.25 34.71"/><line class="cls-9" x1="56.25" y1="32.18" x2="56.25" y2="21.94"/><polygon class="cls-8" points="7.75 34.71 10.28 31.33 5.22 31.33 7.75 34.71"/><line class="cls-9" x1="7.75" y1="32.18" x2="7.75" y2="21.94"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_lock_rows.svg b/web/pgadmin/static/js/Explain/img/ex_lock_rows.svg
new file mode 100644
index 000000000..001ab5f85
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_lock_rows.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-5{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{fill:#ffbd30;}.cls-4{opacity:0.8;}.cls-5{stroke:#c18f36;stroke-miterlimit:10;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}.cls-8{fill:url(#linear-gradient);}.cls-9{fill:#757272;}.cls-10{fill:#fbf1c3;}</style><linearGradient id="linear-gradient" x1="5.03" y1="27.25" x2="14.71" y2="36.93" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ceba59"/><stop offset="1" stop-color="#b76451"/></linearGradient></defs><title>ex_lock_rows</title><path class="cls-1" d="M19.74,57.47c-1.37,0-1.5-.13-1.5-1.5V15.53h41V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M58.72,16V56a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-38a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16h40m1-1h-42V56c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V15Z"/><rect class="cls-3" x="18.72" y="24.56" width="7.22" height="7.44"/><rect class="cls-3" x="35.13" y="41.11" width="7.22" height="7.44"/><rect class="cls-3" x="26.92" y="41.11" width="7.22" height="7.44"/><g class="cls-4"><line class="cls-5" x1="26.43" y1="56.97" x2="26.43" y2="15.97"/><line class="cls-5" x1="42.83" y1="56.97" x2="42.83" y2="15.97"/><line class="cls-5" x1="34.63" y1="56.97" x2="34.63" y2="15.97"/><line class="cls-5" x1="51.03" y1="56.97" x2="51.03" y2="15.97"/><line class="cls-5" x1="18.73" y1="23.97" x2="58.7" y2="23.97"/><line class="cls-5" x1="18.73" y1="32.34" x2="58.7" y2="32.34"/><line class="cls-5" x1="18.73" y1="40.71" x2="58.7" y2="40.71"/><line class="cls-5" x1="18.73" y1="49.08" x2="58.7" y2="49.08"/></g><path class="cls-6" d="M51,15.53v-9h6.69c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-7" d="M57.73,7a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H51.54V7h6.19m0-1H50.54V16h9.19V8c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M18.24,15.53V8c0-1.37.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-7" d="M25.93,7v8H18.74V8a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.19m1-1H19.74c-1.65,0-2,.35-2,2v8h9.19V6Z"/><rect class="cls-6" x="42.83" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M50.52,7v8H43.33V7h7.18m1-1H42.34V16h9.19V6Z"/><rect class="cls-6" x="34.64" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M42.33,7v8H35.14V7h7.18m1-1H34.15V16h9.19V6Z"/><rect class="cls-6" x="26.45" y="6.53" width="8.17" height="9"/><path class="cls-7" d="M34.13,7v8H26.95V7h7.17m1-1H26V16h9.18V6Z"/><path id="Shape" class="cls-8" d="M14.07,27.19H5.67a1.4,1.4,0,0,0-1.4,1.4v7A1.4,1.4,0,0,0,5.67,37h8.4a1.4,1.4,0,0,0,1.4-1.4v-7a1.4,1.4,0,0,0-1.4-1.4Zm0,8.4H5.67v-7h8.4Z"/><path id="Shape-2" data-name="Shape" class="cls-9" d="M13.37,27.19v-1.4a3.5,3.5,0,0,0-7,0s0,1.42,0,1.42H7.7s0-1.41,0-1.42a2.17,2.17,0,0,1,4.34,0v1.4Z"/><rect class="cls-10" x="5.67" y="28.59" width="8.4" height="7"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_materialize.svg b/web/pgadmin/static/js/Explain/img/ex_materialize.svg
new file mode 100644
index 000000000..b0b87457a
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_materialize.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{opacity:0.6;}.cls-2{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6{fill:none;stroke:#c18f35;opacity:0.8;}.cls-6,.cls-7,.cls-8{stroke-linejoin:round;}.cls-7,.cls-8{fill:#528d19;stroke:#528d19;}.cls-8{stroke-linecap:round;stroke-width:2px;}</style></defs><title>ex_materialize</title><g class="cls-1"><path class="cls-2" d="M10.17,49.88c-1.37,0-1.5-.13-1.5-1.5V19.88H16v30Z"/><path class="cls-3" d="M15.45,20.38v29H10.17a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-28h6.28m1-1H8.17v29c0,1.65.35,2,2,2h6.28v-31Z"/><path class="cls-2" d="M15.94,49.88v-30h7.28v28.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M22.72,20.38v28a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H16.44v-29h6.28m1-1H15.44v31h6.28c1.65,0,2-.35,2-2v-29Z"/><path class="cls-4" d="M8.67,19.88v-4.5c0-1.37.13-1.5,1.5-1.5H16v6Z"/><path class="cls-5" d="M15.45,14.38v5H9.17v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H10.17c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M15.94,19.88v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M21.72,14.38a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H16.44v-5h5.28m0-1H15.44v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="15.7" y1="26.23" x2="23.19" y2="26.23"/><line class="cls-6" x1="8.7" y1="26.23" x2="15.44" y2="26.23"/><line class="cls-6" x1="15.7" y1="32.23" x2="23.19" y2="32.23"/><line class="cls-6" x1="8.7" y1="32.23" x2="15.44" y2="32.23"/><line class="cls-6" x1="15.7" y1="38.23" x2="23.19" y2="38.23"/><line class="cls-6" x1="8.7" y1="38.23" x2="15.44" y2="38.23"/><line class="cls-6" x1="15.7" y1="44.23" x2="23.19" y2="44.23"/><line class="cls-6" x1="8.7" y1="44.23" x2="15.44" y2="44.23"/></g><path class="cls-2" d="M42.45,49.88c-1.37,0-1.5-.13-1.5-1.5V19.88h7.28v30Z"/><path class="cls-3" d="M47.73,20.38v29H42.45a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-28h6.28m1-1H40.45v29c0,1.65.35,2,2,2h6.28v-31Z"/><path class="cls-2" d="M48.22,49.88v-30H55.5v28.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M55,20.38v28a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H48.72v-29H55m1-1H47.72v31H54c1.65,0,2-.35,2-2v-29Z"/><path class="cls-4" d="M41,19.88v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M47.73,14.38v5H41.45v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H42.45c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M48.22,19.88v-6H54c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M54,14.38a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H48.72v-5H54m0-1H47.72v7H56v-5c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="47.98" y1="26.23" x2="55.47" y2="26.23"/><line class="cls-6" x1="40.98" y1="26.23" x2="47.72" y2="26.23"/><line class="cls-6" x1="47.98" y1="32.23" x2="55.47" y2="32.23"/><line class="cls-6" x1="40.98" y1="32.23" x2="47.72" y2="32.23"/><line class="cls-6" x1="47.98" y1="38.23" x2="55.47" y2="38.23"/><line class="cls-6" x1="40.98" y1="38.23" x2="47.72" y2="38.23"/><line class="cls-6" x1="47.98" y1="44.23" x2="55.47" y2="44.23"/><line class="cls-6" x1="40.98" y1="44.23" x2="47.72" y2="44.23"/><polygon class="cls-7" points="37.55 32 34.17 29.47 34.17 34.53 37.55 32"/><line class="cls-8" x1="33.89" y1="32" x2="26.45" y2="32"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_merge.svg b/web/pgadmin/static/js/Explain/img/ex_merge.svg
new file mode 100644
index 000000000..5f448693b
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_merge.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3{fill:none;stroke:#666bd1;}.cls-10,.cls-11,.cls-3{stroke-linejoin:round;}.cls-3,.cls-6{opacity:0.8;}.cls-4,.cls-7{fill:#f0ecb6;}.cls-5{fill:#c18f36;}.cls-7{stroke:#c18f36;stroke-miterlimit:10;}.cls-8{fill:#addff3;}.cls-9{fill:#2980b9;}.cls-10,.cls-11{fill:#528d19;stroke:#528d19;}.cls-11{stroke-linecap:round;stroke-width:2px;}</style></defs><title>ex_merge</title><path class="cls-1" d="M9.5,17.48C8.13,17.48,8,17.36,8,16V12c0-1.37.13-1.5,1.5-1.5H14v7Z"/><path class="cls-2" d="M13.5,11v6h-4a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,8.5,16V12a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,9.5,11h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5V10Z"/><path class="cls-1" d="M14,17.48v-7H25.19c1.37,0,1.5.13,1.5,1.5v4c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M25.19,11a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H14.5V11H25.19m0-1H13.5v8H25.19c1.65,0,2-.35,2-2V12c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="20.27" y1="17.1" x2="20.27" y2="10.86"/><path class="cls-4" d="M32.89,53.5c-1.37,0-1.5-.13-1.5-1.5V27.56H56V52c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-5" d="M55.49,28.06V52a3.29,3.29,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H32.89a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V28.06h23.6m1-1H30.89V52c0,1.65.35,2,2,2h21.6c1.65,0,2-.35,2-2V27.06Z"/><g class="cls-6"><line class="cls-7" x1="39.58" y1="52.99" x2="39.58" y2="28"/><line class="cls-7" x1="47.8" y1="52.99" x2="47.8" y2="28"/><line class="cls-7" x1="31.88" y1="33.99" x2="55.47" y2="33.99"/><line class="cls-7" x1="31.88" y1="40.36" x2="55.47" y2="40.36"/><line class="cls-7" x1="31.88" y1="46.73" x2="55.47" y2="46.73"/></g><path class="cls-8" d="M47.81,27.55v-6H54.5c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-9" d="M54.5,22.05a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H48.31v-5H54.5m0-1H47.31v7H56.5v-5c0-1.65-.35-2-2-2Z"/><path class="cls-8" d="M31.39,27.55v-4.5c0-1.37.13-1.5,1.5-1.5h6.69v6Z"/><path class="cls-9" d="M39.08,22.05v5H31.89v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.19m1-1H32.89c-1.65,0-2,.35-2,2v5h9.19v-7Z"/><rect class="cls-8" x="39.6" y="21.55" width="8.19" height="6"/><path class="cls-9" d="M47.29,22.05v5H40.1v-5h7.18m1-1H39.11v7h9.19v-7Z"/><polygon class="cls-10" points="27.87 44 24.49 41.47 24.49 46.53 27.87 44"/><line class="cls-11" x1="24.21" y1="44" x2="17.1" y2="44"/><line class="cls-11" x1="16.96" y1="21.56" x2="16.96" y2="44"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_merge_anti_join.svg b/web/pgadmin/static/js/Explain/img/ex_merge_anti_join.svg
new file mode 100644
index 000000000..2e7a00d65
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_merge_anti_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-10,.cls-13,.cls-3{fill:none;}.cls-3{stroke:#666bd1;}.cls-10,.cls-3,.cls-4,.cls-5{stroke-linejoin:round;}.cls-10,.cls-3{opacity:0.8;}.cls-4,.cls-5{fill:#528d19;stroke:#528d19;}.cls-5{stroke-linecap:round;stroke-width:2px;}.cls-6{fill:#f0ecb6;}.cls-7{fill:#c18f35;}.cls-8{fill:#addff3;}.cls-9{fill:#2980b9;}.cls-10{stroke:#c18f35;}.cls-11{fill:#def4fd;}.cls-12{fill:#c00;}.cls-13{stroke:#c00;stroke-miterlimit:10;stroke-width:1.5px;}</style></defs><title>ex_merge_anti_join</title><path class="cls-1" d="M10.12,18.59c-1.37,0-1.5-.13-1.5-1.5v-4c0-1.37.13-1.5,1.5-1.5h4.5v7Z"/><path class="cls-2" d="M14.12,12.11v6h-4a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-4a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5v-8Z"/><path class="cls-1" d="M14.63,18.59v-7H25.81c1.37,0,1.5.13,1.5,1.5v4c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M25.81,12.11a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H15.13v-6H25.81m0-1H14.13v8H25.81c1.65,0,2-.35,2-2v-4c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="20.9" y1="18.21" x2="20.9" y2="11.97"/><polygon class="cls-4" points="28.49 38.11 25.11 35.58 25.11 40.64 28.49 38.11"/><line class="cls-5" x1="24.83" y1="38.11" x2="17.73" y2="38.11"/><line class="cls-5" x1="17.58" y1="22.67" x2="17.58" y2="38.11"/><path class="cls-6" d="M34.26,52.16c-1.37,0-1.5-.13-1.5-1.5V28.16H40v24Z"/><path class="cls-7" d="M39.54,28.66v23H34.26a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-22h6.28m1-1H32.26v23c0,1.65.35,2,2,2h6.28v-25Z"/><path class="cls-8" d="M32.76,28.16v-4.5c0-1.37.13-1.5,1.5-1.5H40v6Z"/><path class="cls-9" d="M39.54,22.66v5H33.26v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H34.26c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><line class="cls-10" x1="32.79" y1="34.51" x2="39.53" y2="34.51"/><line class="cls-10" x1="32.79" y1="40.51" x2="39.53" y2="40.51"/><line class="cls-10" x1="32.79" y1="46.51" x2="39.53" y2="46.51"/><rect class="cls-6" x="40.06" y="28.16" width="7.28" height="24"/><path class="cls-7" d="M46.84,28.66v23H40.56v-23h6.28m1-1H39.56v25h8.28v-25Z"/><path class="cls-1" d="M47.32,52.16v-24h7.28v22.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M54.11,28.66v22a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H47.82v-23h6.28m1-1H46.82v25h6.28c1.65,0,2-.35,2-2v-23Z"/><rect class="cls-8" x="40.06" y="22.16" width="7.28" height="6"/><path class="cls-9" d="M46.84,22.66v5H40.56v-5h6.28m1-1H39.56v7h8.28v-7Z"/><path class="cls-8" d="M47.32,28.16v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-9" d="M53.11,22.66a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4H47.82v-5h5.28m0-1H46.83v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="47.09" y1="34.51" x2="54.58" y2="34.51"/><line class="cls-10" x1="40.09" y1="34.51" x2="46.82" y2="34.51"/><line class="cls-3" x1="47.09" y1="40.51" x2="54.58" y2="40.51"/><line class="cls-10" x1="40.09" y1="40.51" x2="46.82" y2="40.51"/><line class="cls-3" x1="47.09" y1="46.51" x2="54.58" y2="46.51"/><line class="cls-10" x1="40.09" y1="46.51" x2="46.82" y2="46.51"/><circle class="cls-11" cx="50.83" cy="40.46" r="4.29"/><path class="cls-12" d="M50.83,36.92a3.54,3.54,0,1,1-3.54,3.54,3.55,3.55,0,0,1,3.54-3.54m0-1.5a5,5,0,1,0,5,5,5,5,0,0,0-5-5Z"/><line class="cls-13" x1="47.55" y1="42.73" x2="54.06" y2="38"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_merge_append.svg b/web/pgadmin/static/js/Explain/img/ex_merge_append.svg
new file mode 100644
index 000000000..540f8a969
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_merge_append.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-7{fill:#34495e;}.cls-1{stroke:#528d19;stroke-miterlimit:10;}.cls-2{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-10,.cls-6{fill:none;stroke-linejoin:round;opacity:0.8;}.cls-6{stroke:#c18f35;}.cls-8{fill:#d3d4f3;}.cls-9{fill:#666bd1;}.cls-10{stroke:#666bd1;}</style></defs><title>ex_merge_append</title><path class="cls-1" d="M28.32,41.36a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4,2.82,2.82,0,0,0-1.06-1.08,4.47,4.47,0,0,0-2.05-.51V22.2l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9A3.66,3.66,0,0,1,32.2,25a7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5A2.37,2.37,0,0,0,33,30.69a2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-2" d="M11.39,53.46c-1.37,0-1.5-.13-1.5-1.5V40.46h7.28v13Z"/><path class="cls-3" d="M16.67,41V53H11.39a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V41h6.28m1-1H9.39V52c0,1.65.35,2,2,2h6.28V40Z"/><path class="cls-2" d="M17.16,53.46v-13h7.28V52c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M23.94,41V52a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H17.66V41h6.28m1-1H16.66V54h6.28c1.65,0,2-.35,2-2V40Z"/><path class="cls-4" d="M9.89,40.46V36c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M16.67,35v5H10.39V36a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H11.39c-1.65,0-2,.35-2,2v5h8.28V34Z"/><path class="cls-4" d="M17.16,40.46v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M22.94,35a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H17.66V35h5.28m0-1H16.66v7h8.28V36c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="16.92" y1="46.81" x2="24.41" y2="46.81"/><line class="cls-6" x1="9.92" y1="46.81" x2="16.66" y2="46.81"/><path class="cls-7" d="M13.77,42v3.36H13V42Z"/><path class="cls-7" d="M12.89,48.47v3.36h-.77V48.47Z"/><path class="cls-7" d="M14.95,48.47v3.36h-.77V48.47Z"/><path class="cls-8" d="M11.39,29.31c-1.37,0-1.5-.13-1.5-1.5V16.31h7.28v13Z"/><path class="cls-9" d="M16.67,16.81v12H11.39a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H9.39v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-8" d="M17.16,29.31v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-9" d="M23.94,16.81v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H17.66v-12h6.28m1-1H16.66v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-4" d="M9.89,16.31v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M16.67,10.81v5H10.39v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H11.39c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M17.16,16.31v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M22.94,10.81a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H17.66v-5h5.28m0-1H16.66v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-10" x1="16.92" y1="22.66" x2="24.41" y2="22.66"/><line class="cls-10" x1="9.92" y1="22.66" x2="16.66" y2="22.66"/><path class="cls-7" d="M13.77,17.79v3.36H13V17.79Z"/><path class="cls-7" d="M12.89,24.17v3.36h-.77V24.17Z"/><path class="cls-7" d="M14.95,24.17v3.36h-.77V24.17Z"/><path class="cls-2" d="M40.54,47.76c-1.37,0-1.5-.13-1.5-1.5v-4.8h7.28v6.3Z"/><path class="cls-3" d="M45.82,42v5.3H40.54a3.26,3.26,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V42h6.28m1-1H38.54v5.3c0,1.65.35,2,2,2h6.28V41Z"/><path class="cls-2" d="M46.31,47.76v-6.3h7.28v4.8c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M53.09,42v4.3a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H46.81V42h6.28m1-1H45.81v7.3h6.28c1.65,0,2-.35,2-2V41Z"/><rect class="cls-8" x="39.04" y="35.16" width="7.28" height="6.3"/><path class="cls-9" d="M45.82,35.66V41H39.54v-5.3h6.28m1-1H38.54V42h8.28v-7.3Z"/><rect class="cls-8" x="46.31" y="35.16" width="7.28" height="6.3"/><path class="cls-9" d="M53.09,35.66V41H46.81v-5.3h6.28m1-1H45.81V42h8.28v-7.3Z"/><rect class="cls-2" x="39.04" y="28.84" width="7.28" height="6.3"/><path class="cls-3" d="M45.82,29.34v5.3H39.54v-5.3h6.28m1-1H38.54v7.3h8.28v-7.3Z"/><rect class="cls-2" x="46.31" y="28.84" width="7.28" height="6.3"/><path class="cls-3" d="M53.09,29.34v5.3H46.81v-5.3h6.28m1-1H45.81v7.3h8.28v-7.3Z"/><rect class="cls-8" x="39.04" y="22.55" width="7.28" height="6.3"/><path class="cls-9" d="M45.82,23v5.3H39.54V23h6.28m1-1H38.54v7.3h8.28V22Z"/><rect class="cls-8" x="46.31" y="22.55" width="7.28" height="6.3"/><path class="cls-9" d="M53.09,23v5.3H46.81V23h6.28m1-1H45.81v7.3h8.28V22Z"/><path class="cls-4" d="M39,22.54v-4.8c0-1.37.13-1.5,1.5-1.5h5.78v6.3Z"/><path class="cls-5" d="M45.82,16.74V22H39.54v-4.3a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H40.54c-1.65,0-2,.35-2,2V23h8.28v-7.3Z"/><path class="cls-4" d="M46.31,22.54v-6.3h5.78c1.37,0,1.5.13,1.5,1.5v4.8Z"/><path class="cls-5" d="M52.09,16.74a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92V22H46.81v-5.3h5.28m0-1H45.81V23h8.28v-5.3c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M43,24.12v3.36h-.77V24.12Z"/><path class="cls-7" d="M43,30.33v3.36h-.77V30.33Z"/><path class="cls-7" d="M42.15,43v3.36h-.77V43Z"/><path class="cls-7" d="M44.21,43v3.36h-.77V43Z"/><path class="cls-7" d="M42.15,36.78v3.36h-.77V36.78Z"/><path class="cls-7" d="M44.21,36.78v3.36h-.77V36.78Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_merge_semi_join.svg b/web/pgadmin/static/js/Explain/img/ex_merge_semi_join.svg
new file mode 100644
index 000000000..672cd4e5a
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_merge_semi_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-10,.cls-3{fill:none;opacity:0.8;}.cls-3{stroke:#666bd1;}.cls-10,.cls-3,.cls-4,.cls-5{stroke-linejoin:round;}.cls-4,.cls-5{fill:#528d19;stroke:#528d19;}.cls-5{stroke-linecap:round;stroke-width:2px;}.cls-6{fill:#f0ecb6;}.cls-7{fill:#c18f35;}.cls-8{fill:#addff3;}.cls-9{fill:#2980b9;}.cls-10{stroke:#c18f35;}.cls-11{opacity:0.6;}</style></defs><title>ex_merge_semi_join</title><path class="cls-1" d="M10.51,18.18c-1.37,0-1.5-.13-1.5-1.5v-4c0-1.37.13-1.5,1.5-1.5H15v7Z"/><path class="cls-2" d="M14.51,11.69v6h-4a3.26,3.26,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m1-1h-5c-1.65,0-2,.35-2,2v4c0,1.65.35,2,2,2h5v-8Z"/><path class="cls-1" d="M15,18.18v-7H26.2c1.37,0,1.5.13,1.5,1.5v4c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M26.2,11.69a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4a3.29,3.29,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H15.51v-6H26.2m0-1H14.51v8H26.2c1.65,0,2-.35,2-2v-4c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="21.28" y1="17.79" x2="21.28" y2="11.55"/><polygon class="cls-4" points="28.88 37.69 25.5 35.16 25.5 40.22 28.88 37.69"/><line class="cls-5" x1="25.21" y1="37.69" x2="18.11" y2="37.69"/><line class="cls-5" x1="17.97" y1="22.25" x2="17.97" y2="37.69"/><path class="cls-6" d="M34.64,52.57c-1.37,0-1.5-.13-1.5-1.5V28.57h7.28v24Z"/><path class="cls-7" d="M39.92,29.07v23H34.64a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-22h6.28m1-1H32.64v23c0,1.65.35,2,2,2h6.28v-25Z"/><path class="cls-8" d="M33.14,28.57v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-9" d="M39.92,23.07v5H33.64v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H34.64c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><line class="cls-10" x1="33.17" y1="34.92" x2="39.91" y2="34.92"/><line class="cls-10" x1="33.17" y1="40.92" x2="39.91" y2="40.92"/><line class="cls-10" x1="33.17" y1="46.92" x2="39.91" y2="46.92"/><g class="cls-11"><rect class="cls-6" x="40.44" y="28.57" width="7.28" height="24"/><path class="cls-7" d="M47.22,29.07v23H40.94v-23h6.28m1-1H39.94v25h8.28v-25Z"/><path class="cls-1" d="M47.71,52.57v-24H55v22.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M54.49,29.07v22a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H48.21v-23h6.28m1-1H47.21v25h6.28c1.65,0,2-.35,2-2v-23Z"/><rect class="cls-8" x="40.44" y="22.57" width="7.28" height="6"/><path class="cls-9" d="M47.22,23.07v5H40.94v-5h6.28m1-1H39.94v7h8.28v-7Z"/><path class="cls-8" d="M47.71,28.57v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-9" d="M53.49,23.07a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H48.21v-5h5.28m0-1H47.21v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="47.47" y1="34.92" x2="54.96" y2="34.92"/><line class="cls-10" x1="40.47" y1="34.92" x2="47.21" y2="34.92"/><line class="cls-3" x1="47.47" y1="40.92" x2="54.96" y2="40.92"/><line class="cls-10" x1="40.47" y1="40.92" x2="47.21" y2="40.92"/><line class="cls-3" x1="47.47" y1="46.92" x2="54.96" y2="46.92"/><line class="cls-10" x1="40.47" y1="46.92" x2="47.21" y2="46.92"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_named_tuplestore_scan.svg b/web/pgadmin/static/js/Explain/img/ex_named_tuplestore_scan.svg
new file mode 100644
index 000000000..3d0fa55ff
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_named_tuplestore_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-2,.cls-4,.cls-6,.cls-8{fill:#f3c7d7;}.cls-1,.cls-2{stroke:#d04465;}.cls-1,.cls-2,.cls-3,.cls-4,.cls-5,.cls-6,.cls-7,.cls-8{stroke-miterlimit:10;}.cls-2,.cls-4,.cls-6,.cls-8{opacity:0.5;}.cls-3{fill:#addff3;}.cls-3,.cls-4{stroke:#2980b9;}.cls-5{fill:#f0ecb6;}.cls-5,.cls-6{stroke:#c18f36;}.cls-7{fill:#d3d4f3;}.cls-7,.cls-8{stroke:#666bd1;}.cls-10,.cls-9{fill:#646464;stroke:#646464;stroke-linejoin:round;}.cls-10{stroke-linecap:round;stroke-width:2px;}</style></defs><title>named_tuplestore_scan</title><rect class="cls-1" x="22.81" y="22.32" width="31.05" height="7.04" rx="1"/><line class="cls-2" x1="34.89" y1="29.08" x2="34.89" y2="22.84"/><line class="cls-2" x1="28.83" y1="29.08" x2="28.83" y2="22.84"/><line class="cls-2" x1="41.27" y1="29.08" x2="41.27" y2="22.84"/><line class="cls-2" x1="47.4" y1="29.08" x2="47.4" y2="22.84"/><rect class="cls-3" x="22.81" y="34.68" width="31.05" height="7.04" rx="1"/><line class="cls-4" x1="34.89" y1="41.44" x2="34.89" y2="35.2"/><line class="cls-4" x1="28.83" y1="41.44" x2="28.83" y2="35.2"/><line class="cls-4" x1="41.27" y1="41.44" x2="41.27" y2="35.2"/><line class="cls-4" x1="47.4" y1="41.44" x2="47.4" y2="35.2"/><rect class="cls-5" x="22.81" y="47.24" width="31.05" height="7.04" rx="1"/><line class="cls-6" x1="34.89" y1="54" x2="34.89" y2="47.76"/><line class="cls-6" x1="28.83" y1="54" x2="28.83" y2="47.76"/><line class="cls-6" x1="41.27" y1="54" x2="41.27" y2="47.76"/><line class="cls-6" x1="47.4" y1="54" x2="47.4" y2="47.76"/><rect class="cls-7" x="22.81" y="9.72" width="31.05" height="7.04" rx="1"/><line class="cls-8" x1="34.89" y1="16.47" x2="34.89" y2="10.23"/><line class="cls-8" x1="28.83" y1="16.47" x2="28.83" y2="10.23"/><line class="cls-8" x1="41.27" y1="16.47" x2="41.27" y2="10.23"/><line class="cls-8" x1="47.4" y1="16.47" x2="47.4" y2="10.23"/><polygon class="cls-9" points="13.62 53.99 10.14 49.35 17.09 49.35 13.62 53.99"/><line class="cls-10" x1="13.62" y1="49.98" x2="13.62" y2="10.01"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_nested.svg b/web/pgadmin/static/js/Explain/img/ex_nested.svg
new file mode 100644
index 000000000..3623675b2
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_nested.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-3{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}.cls-1{stroke:#666bd1;}.cls-2{fill:#666bd1;}.cls-3{stroke:#8d8119;}.cls-4{fill:#8d8119;}</style></defs><title>ex_nested</title><path class="cls-1" d="M17.23,20a6,6,0,0,1,6-6h22a6,6,0,0,1,6,6V44a6,6,0,0,1-6,6h-22a6,6,0,0,1-6-6V32.27"/><polygon class="cls-2" points="17.23 27.31 12.77 33.26 21.68 33.26 17.23 27.31"/><path class="cls-3" d="M24.23,25c0-3.3.7-4,4-4h12c3.3,0,4,.7,4,4V39c0,3.3-.7,4-4,4H28.72c-3.3,0-4-.7-4-4V33.72"/><polygon class="cls-4" points="24.67 31.34 20.71 36.1 28.23 36.1 24.67 31.34"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_nested_loop_anti_join.svg b/web/pgadmin/static/js/Explain/img/ex_nested_loop_anti_join.svg
new file mode 100644
index 000000000..085cf6b18
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_nested_loop_anti_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-11,.cls-15,.cls-3,.cls-8{fill:none;}.cls-1,.cls-8{stroke:#666bd1;}.cls-1,.cls-3{stroke-linecap:round;}.cls-1,.cls-11,.cls-3,.cls-8{stroke-linejoin:round;}.cls-1,.cls-15,.cls-3{stroke-width:1.5px;}.cls-2{fill:#666bd1;}.cls-3{stroke:#8d8119;}.cls-4{fill:#8d8119;}.cls-5{fill:#d3d4f3;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}.cls-11,.cls-8{opacity:0.8;}.cls-9{fill:#f0ecb6;}.cls-10{fill:#c18f35;}.cls-11{stroke:#c18f35;}.cls-12{fill:#34495e;stroke:#528d19;}.cls-12,.cls-15{stroke-miterlimit:10;}.cls-13{fill:#def4fd;}.cls-14{fill:#c00;}.cls-15{stroke:#c00;}</style></defs><title>ex_nested_loop_anti_join</title><path class="cls-1" d="M8.68,22.32c0-.35.06-3.69.17-4a3,3,0,0,1,2.83-2h14a3,3,0,0,1,3,3v24a3,3,0,0,1-3,3h-14a3,3,0,0,1-3-3V28.46"/><polygon class="cls-2" points="8.68 25.98 6.45 28.95 10.9 28.95 8.68 25.98"/><path class="cls-3" d="M12.18,24.82c0-.26,0-3.48,0-3.68.11-1.07.58-1.32,2-1.32h9c1.65,0,2,.35,2,2v19c0,1.65-.35,2-2,2H14.42c-1.65,0-2-.35-2-2V29.18"/><polygon class="cls-4" points="12.4 27.99 10.42 30.37 14.18 30.37 12.4 27.99"/><path class="cls-5" d="M48.87,46.94v-24h7.28v22.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M55.65,23.44v22a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H49.37v-23h6.28m1-1H48.37v25h6.28c1.65,0,2-.35,2-2v-23Z"/><path class="cls-6" d="M48.87,22.94v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-7" d="M54.65,17.44a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v4H49.37v-5h5.28m0-1H48.37v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-8" x1="48.63" y1="29.29" x2="56.12" y2="29.29"/><line class="cls-8" x1="48.63" y1="35.29" x2="56.12" y2="35.29"/><line class="cls-8" x1="48.63" y1="41.29" x2="56.12" y2="41.29"/><path class="cls-9" d="M43.1,46.94c-1.37,0-1.5-.13-1.5-1.5V22.94h7.28v24Z"/><path class="cls-10" d="M48.38,23.44v23H43.1a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-22h6.28m1-1H41.1v23c0,1.65.35,2,2,2h6.28v-25Z"/><path class="cls-6" d="M41.6,22.94v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-7" d="M48.38,17.44v5H42.1v-4a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H43.1c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><line class="cls-11" x1="41.63" y1="29.29" x2="48.37" y2="29.29"/><line class="cls-11" x1="41.63" y1="35.29" x2="48.37" y2="35.29"/><line class="cls-11" x1="41.63" y1="41.29" x2="48.37" y2="41.29"/><path class="cls-12" d="M33.87,35.61A9.28,9.28,0,0,1,34,34.4a2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47.5.77.58,1.18a2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06C34.37,34.84,33.85,36.08,33.87,35.61Z"/><circle class="cls-13" cx="52.51" cy="35.29" r="4.29"/><path class="cls-14" d="M52.51,31.75A3.54,3.54,0,1,1,49,35.29a3.55,3.55,0,0,1,3.54-3.54m0-1.5a5,5,0,1,0,5,5,5,5,0,0,0-5-5Z"/><line class="cls-15" x1="49.22" y1="37.57" x2="55.73" y2="32.83"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_nested_loop_semi_join.svg b/web/pgadmin/static/js/Explain/img/ex_nested_loop_semi_join.svg
new file mode 100644
index 000000000..b39cb6936
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_nested_loop_semi_join.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{opacity:0.6;}.cls-2{fill:#d3d4f3;}.cls-3{fill:#666bd1;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-11,.cls-12,.cls-6,.cls-9{fill:none;stroke-linejoin:round;}.cls-11,.cls-6{stroke:#666bd1;}.cls-6,.cls-9{opacity:0.8;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{stroke:#c18f35;}.cls-10{fill:#34495e;stroke:#528d19;stroke-miterlimit:10;}.cls-11,.cls-12{stroke-linecap:round;stroke-width:1.5px;}.cls-12{stroke:#8d8119;}.cls-13{fill:#8d8119;}</style></defs><title>ex_nested_loop_semi_join</title><g class="cls-1"><path class="cls-2" d="M49.45,46.88v-24h7.28v22.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M56.23,23.38v22a3.29,3.29,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H49.95v-23h6.28m1-1H48.95v25h6.28c1.65,0,2-.35,2-2v-23Z"/><path class="cls-4" d="M49.45,22.88v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M55.23,17.38a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H49.95v-5h5.28m0-1H49v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="49.21" y1="29.23" x2="56.71" y2="29.23"/><line class="cls-6" x1="49.21" y1="35.23" x2="56.71" y2="35.23"/><line class="cls-6" x1="49.21" y1="41.23" x2="56.71" y2="41.23"/></g><path class="cls-7" d="M43.68,46.88c-1.37,0-1.5-.13-1.5-1.5V22.88h7.28v24Z"/><path class="cls-8" d="M49,23.38v23H43.68a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-22H49m1-1H41.68v23c0,1.65.35,2,2,2H50v-25Z"/><path class="cls-4" d="M42.18,22.88v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M49,17.38v5H42.68v-4a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H49m1-1H43.68c-1.65,0-2,.35-2,2v5H50v-7Z"/><line class="cls-9" x1="42.21" y1="29.23" x2="48.95" y2="29.23"/><line class="cls-9" x1="42.21" y1="35.23" x2="48.95" y2="35.23"/><line class="cls-9" x1="42.21" y1="41.23" x2="48.95" y2="41.23"/><path class="cls-10" d="M34.45,35.55a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47.5.77.58,1.18a2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73A2.39,2.39,0,0,0,35,34.37C35,34.78,34.44,36,34.45,35.55Z"/><path class="cls-11" d="M9,23.05c0-.35.06-3.69.17-4a3,3,0,0,1,2.83-2H26a3,3,0,0,1,3,3v24a3,3,0,0,1-3,3H12a3,3,0,0,1-3-3V29.19"/><polygon class="cls-3" points="8.99 26.71 6.77 29.68 11.22 29.68 8.99 26.71"/><path class="cls-12" d="M12.49,25.55c0-.26,0-3.48,0-3.68.11-1.07.58-1.32,2-1.32h9c1.65,0,2,.35,2,2v19c0,1.65-.35,2-2,2H14.74c-1.65,0-2-.35-2-2V29.91"/><polygon class="cls-13" points="12.71 28.72 10.74 31.1 14.49 31.1 12.71 28.72"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_projectset.svg b/web/pgadmin/static/js/Explain/img/ex_projectset.svg
new file mode 100644
index 000000000..5943ed4aa
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_projectset.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64.03 64"><defs><style>.cls-1{fill:none;stroke:#d04465;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}.cls-2,.cls-4{fill:#e6e6e6;}.cls-3{fill:#818181;}.cls-4{stroke:#818181;}.cls-4,.cls-5{stroke-miterlimit:10;}.cls-5{fill:#d3d4f3;stroke:#666bd1;}</style></defs><title>projectset</title><g id="_1" data-name="1"><line class="cls-1" x1="53.64" y1="20.63" x2="54.58" y2="18.35"/><line class="cls-1" x1="51.36" y1="26.18" x2="52.52" y2="23.36"/><line class="cls-1" x1="41.33" y1="50.52" x2="50.31" y2="28.73"/><line class="cls-1" x1="10.38" y1="20.63" x2="9.45" y2="18.35"/><line class="cls-1" x1="12.67" y1="26.18" x2="11.51" y2="23.36"/><line class="cls-1" x1="22.7" y1="50.52" x2="13.72" y2="28.73"/><line class="cls-1" x1="20.61" y1="32.38" x2="19.44" y2="29.56"/><line class="cls-1" x1="28.13" y1="50.52" x2="21.66" y2="34.93"/><line class="cls-1" x1="21.34" y1="9.61" x2="20.84" y2="8.73"/><line class="cls-1" x1="24.27" y1="16.64" x2="22.39" y2="12.16"/><line class="cls-1" x1="43.58" y1="32.38" x2="44.74" y2="29.56"/><line class="cls-1" x1="36.05" y1="50.52" x2="42.53" y2="34.93"/><line class="cls-1" x1="43.56" y1="10.89" x2="44.11" y2="9.64"/><line class="cls-1" x1="41.07" y1="16.85" x2="42.51" y2="13.44"/><path class="cls-2" d="M14.41,63.53v-4a3.5,3.5,0,0,1,3.5-3.5h28.5a3.5,3.5,0,0,1,3.5,3.5v4Z"/><path class="cls-3" d="M46.41,56.49a3,3,0,0,1,3,3V63H14.91V59.49a3,3,0,0,1,3-3h28.5m0-1H17.91a4,4,0,0,0-4,4V64h36.5V59.49a4,4,0,0,0-4-4Z"/><path class="cls-2" d="M22.27,55.9l-1.85-5.48a.11.11,0,0,1,.09-.05h23.3a.11.11,0,0,1,.09.05L42.05,55.9Z"/><path class="cls-3" d="M43.22,50.87,41.69,55.4H22.63L21.1,50.87H43.22m.59-1H20.51a.6.6,0,0,0-.6.6l2,5.93h20.5l2-5.93a.6.6,0,0,0-.6-.6Z"/><path class="cls-2" d="M32.08,61.06a1.39,1.39,0,1,1,1.38-1.38A1.38,1.38,0,0,1,32.08,61.06Z"/><path class="cls-3" d="M32.08,58.79a.89.89,0,1,1-.89.89.89.89,0,0,1,.89-.89m0-1A1.89,1.89,0,1,0,34,59.68a1.89,1.89,0,0,0-1.88-1.89Z"/><line class="cls-4" x1="21.17" y1="52.28" x2="28.29" y2="52.28"/><line class="cls-4" x1="21.83" y1="54.08" x2="34.69" y2="54.08"/></g><g id="ex_append"><path class="cls-5" d="M22.06,23.87H42a0,0,0,0,1,0,0v3a1,1,0,0,1-1,1H23.06a1,1,0,0,1-1-1v-3A0,0,0,0,1,22.06,23.87Z"/><rect class="cls-5" x="27.07" y="23.87" width="5" height="4"/><rect class="cls-5" x="32.03" y="23.87" width="5" height="4"/><path class="cls-5" d="M23.06,20.13H41a1,1,0,0,1,1,1v3a0,0,0,0,1,0,0H22.06a0,0,0,0,1,0,0v-3A1,1,0,0,1,23.06,20.13Z"/><rect class="cls-5" x="27.07" y="20.13" width="5" height="4"/><rect class="cls-5" x="32.03" y="20.13" width="5" height="4"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_recursive_union.svg b/web/pgadmin/static/js/Explain/img/ex_recursive_union.svg
new file mode 100644
index 000000000..fb5e2e402
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_recursive_union.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#34495e;stroke:#528d19;stroke-miterlimit:10;}.cls-2{fill:#d3d4f3;}.cls-3{fill:#666bd1;}.cls-4{fill:#addff3;}.cls-5{fill:#2980b9;}.cls-6,.cls-9{fill:none;stroke-linejoin:round;opacity:0.8;}.cls-6{stroke:#666bd1;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{stroke:#c18f35;}</style></defs><title>ex_recursive_union</title><path class="cls-1" d="M4.59,25.21A9.28,9.28,0,0,1,4.7,24,2.76,2.76,0,0,1,5,23.18a2.37,2.37,0,0,1,.44-.6A2.53,2.53,0,0,1,6,22.12a4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13A3.65,3.65,0,0,1,6,21.2a2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47.5.77.58,1.18a2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73A2.39,2.39,0,0,0,5.17,24C5.09,24.44,4.57,25.68,4.59,25.21Z"/><path class="cls-1" d="M4.59,49.36a9.28,9.28,0,0,1,.11-1.2A2.76,2.76,0,0,1,5,47.33a2.37,2.37,0,0,1,.44-.6A2.53,2.53,0,0,1,6,46.27,4,4,0,0,1,6.65,46a3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13A3.65,3.65,0,0,1,6,45.35a2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47.5.77.58,1.18a2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06C5.09,48.59,4.57,49.83,4.59,49.36Z"/><path class="cls-1" d="M29.2,41.36a4.42,4.42,0,0,0,2.05-.51,2.81,2.81,0,0,0,1.06-1.08,3.59,3.59,0,0,0,.4-1.4q.06-.75.06-1.46v.35c0-.68,0-1.25,0-1.72a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2q0-.71,0-1.73v.36c0-.47,0-1-.06-1.46a3.58,3.58,0,0,0-.4-1.4,2.82,2.82,0,0,0-1.06-1.08,4.47,4.47,0,0,0-2.05-.51V22.2l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9A3.66,3.66,0,0,1,33.08,25a7,7,0,0,1,.16,1.29c0,.47,0,1.88,0,1.88a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06,7.9,7.9,0,0,0-.12,1.5s-.09,2.78-.18,3.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><path class="cls-2" d="M12.27,29.31c-1.37,0-1.5-.13-1.5-1.5V16.31h7.28v13Z"/><path class="cls-3" d="M17.55,16.81v12H12.27a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H10.27v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-2" d="M18,29.31v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-3" d="M24.82,16.81v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H18.54v-12h6.28m1-1H17.54v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-4" d="M10.77,16.31v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M17.55,10.81v5H11.27v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H12.27c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-4" d="M18,16.31v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M23.82,10.81a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H18.54v-5h5.28m0-1H17.54v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="17.8" y1="22.66" x2="25.29" y2="22.66"/><line class="cls-6" x1="10.8" y1="22.66" x2="17.54" y2="22.66"/><path class="cls-7" d="M12.27,53.46c-1.37,0-1.5-.13-1.5-1.5V40.46h7.28v13Z"/><path class="cls-8" d="M17.55,41V53H12.27a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V41h6.28m1-1H10.27V52c0,1.65.35,2,2,2h6.28V40Z"/><path class="cls-7" d="M18,53.46v-13h7.28V52c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M24.82,41V52a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H18.54V41h6.28m1-1H17.54V54h6.28c1.65,0,2-.35,2-2V40Z"/><path class="cls-4" d="M10.77,40.46V36c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M17.55,35v5H11.27V36a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H12.27c-1.65,0-2,.35-2,2v5h8.28V34Z"/><path class="cls-4" d="M18,40.46v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M23.82,35a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H18.54V35h5.28m0-1H17.54v7h8.28V36c0-1.65-.35-2-2-2Z"/><line class="cls-9" x1="17.8" y1="46.81" x2="25.29" y2="46.81"/><line class="cls-9" x1="10.8" y1="46.81" x2="17.54" y2="46.81"/><path class="cls-1" d="M56.61,35.55a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47.5.77.58,1.18a2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73,2.39,2.39,0,0,0-.47,1.06C57.11,34.78,56.6,36,56.61,35.55Z"/><rect class="cls-2" x="39.17" y="21.4" width="7.28" height="13"/><path class="cls-3" d="M45.95,21.9v12H39.67v-12h6.28m1-1H38.67v14h8.28v-14Z"/><rect class="cls-2" x="46.44" y="21.4" width="7.28" height="13"/><path class="cls-3" d="M53.22,21.9v12H46.93v-12h6.28m1-1H45.93v14h8.28v-14Z"/><path class="cls-4" d="M39.17,21.4V16.9c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-5" d="M45.95,15.9v5H39.67v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H40.67c-1.65,0-2,.35-2,2v5H47v-7Z"/><path class="cls-4" d="M46.44,21.4v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-5" d="M52.22,15.9a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H46.94v-5h5.28m0-1H45.94v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-6" x1="46.2" y1="27.75" x2="53.69" y2="27.75"/><line class="cls-6" x1="39.2" y1="27.75" x2="45.93" y2="27.75"/><path class="cls-7" d="M40.67,47.38c-1.37,0-1.5-.13-1.5-1.5V34.38h7.28v13Z"/><path class="cls-8" d="M45.95,34.88v12H40.67a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H38.67v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-7" d="M46.44,47.38v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-8" d="M53.22,34.88v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H46.93v-12h6.28m1-1H45.93v14h6.28c1.65,0,2-.35,2-2v-12Z"/><line class="cls-9" x1="46.2" y1="40.73" x2="53.69" y2="40.73"/><line class="cls-9" x1="39.2" y1="40.73" x2="45.93" y2="40.73"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_redistribute_motion.svg b/web/pgadmin/static/js/Explain/img/ex_redistribute_motion.svg
new file mode 100644
index 000000000..dd7d95353
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_redistribute_motion.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f3c7d7;}.cls-2{fill:#d04465;}.cls-3{fill:none;stroke:#2980b9;stroke-linecap:round;stroke-linejoin:round;}.cls-4{fill:#2980b9;}.cls-5{fill:#addff3;}</style></defs><title>ex_redistribute_motion</title><g id="ex_delete"><rect class="cls-1" x="47.19" y="45.43" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M55.69,45.93v8h-8v-8h8m0-1h-8a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="7.81" y="45.43" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M16.31,45.93v8h-8v-8h8m0-1h-8a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><rect class="cls-1" x="27.51" y="9.57" width="9" height="9" rx="0.5" ry="0.5"/><path class="cls-2" d="M36,10.07v8H28v-8h8m0-1H28a1,1,0,0,0-1,1v8a1,1,0,0,0,1,1h8a1,1,0,0,0,1-1v-8a1,1,0,0,0-1-1Z"/><line class="cls-3" x1="20.23" y1="43" x2="24.01" y2="40.82"/><path class="cls-4" d="M18.14,44.2a12.09,12.09,0,0,1,3.91-.06l-1.55-1.3-.35-2A12.08,12.08,0,0,1,18.14,44.2Z"/><line class="cls-3" x1="39.96" y1="41.02" x2="43.75" y2="43.2"/><path class="cls-4" d="M45.84,44.4a12.09,12.09,0,0,0-3.91-.06L43.47,43l.35-2A12.08,12.08,0,0,0,45.84,44.4Z"/><line class="cls-3" x1="32.08" y1="22.48" x2="32.08" y2="26.85"/><path class="cls-4" d="M32.09,20.07a12.09,12.09,0,0,1-1.91,3.42l1.9-.69,1.9.69A12.08,12.08,0,0,1,32.09,20.07Z"/><circle class="cls-5" cx="32.08" cy="35.93" r="7.5"/><path class="cls-4" d="M32.08,28.93a7,7,0,1,1-7,7,7,7,0,0,1,7-7m0-1a8,8,0,1,0,8,8,8,8,0,0,0-8-8Z"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_result.svg b/web/pgadmin/static/js/Explain/img/ex_result.svg
new file mode 100644
index 000000000..f8bbd0ab6
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_result.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-6{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{fill:#addff3;}.cls-4{fill:#2980b9;}.cls-5{opacity:0.8;}.cls-6{stroke:#34495e;stroke-miterlimit:10;}</style></defs><title>ex_result</title><path class="cls-1" d="M10,57.47c-1.37,0-1.5-.13-1.5-1.5V15.53h47V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M55,16V56a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,54,57H10a3.25,3.25,0,0,1-.9-.08h0A2.17,2.17,0,0,1,9,56V16H55m1-1H8V56c0,1.65.35,2,2,2H54c1.65,0,2-.35,2-2V15Z"/><path class="cls-3" d="M8.5,15.53V8c0-1.37.13-1.5,1.5-1.5H54c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-4" d="M54,7a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,55,8v7H9V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,10,7H54m0-1H10C8.35,6,8,6.38,8,8v8H56V8c0-1.65-.35-2-2-2Z"/><g class="cls-5"><line class="cls-6" x1="14" y1="25.97" x2="23.97" y2="25.97"/><line class="cls-6" x1="14" y1="34.34" x2="23.97" y2="34.34"/><line class="cls-6" x1="14" y1="42.71" x2="23.97" y2="42.71"/><line class="cls-6" x1="14" y1="51.08" x2="23.97" y2="51.08"/></g><g class="cls-5"><line class="cls-6" x1="14" y1="21.97" x2="23.97" y2="21.97"/><line class="cls-6" x1="14" y1="30.34" x2="23.97" y2="30.34"/><line class="cls-6" x1="14" y1="38.71" x2="23.97" y2="38.71"/><line class="cls-6" x1="14" y1="47.08" x2="23.97" y2="47.08"/></g><g class="cls-5"><line class="cls-6" x1="14" y1="11.03" x2="23.97" y2="11.03"/></g><g class="cls-5"><line class="cls-6" x1="27" y1="11.03" x2="36.97" y2="11.03"/></g><g class="cls-5"><line class="cls-6" x1="40" y1="11.03" x2="49.97" y2="11.03"/></g><g class="cls-5"><line class="cls-6" x1="27" y1="25.97" x2="36.97" y2="25.97"/><line class="cls-6" x1="27" y1="34.34" x2="36.97" y2="34.34"/><line class="cls-6" x1="27" y1="42.71" x2="36.97" y2="42.71"/><line class="cls-6" x1="27" y1="51.08" x2="36.97" y2="51.08"/></g><g class="cls-5"><line class="cls-6" x1="27" y1="21.97" x2="36.97" y2="21.97"/><line class="cls-6" x1="27" y1="30.34" x2="36.97" y2="30.34"/><line class="cls-6" x1="27" y1="38.71" x2="36.97" y2="38.71"/><line class="cls-6" x1="27" y1="47.08" x2="36.97" y2="47.08"/></g><g class="cls-5"><line class="cls-6" x1="40" y1="25.97" x2="49.97" y2="25.97"/><line class="cls-6" x1="40" y1="34.34" x2="49.97" y2="34.34"/><line class="cls-6" x1="40" y1="42.71" x2="49.97" y2="42.71"/><line class="cls-6" x1="40" y1="51.08" x2="49.97" y2="51.08"/></g><g class="cls-5"><line class="cls-6" x1="40" y1="21.97" x2="49.97" y2="21.97"/><line class="cls-6" x1="40" y1="30.34" x2="49.97" y2="30.34"/><line class="cls-6" x1="40" y1="38.71" x2="49.97" y2="38.71"/><line class="cls-6" x1="40" y1="47.08" x2="49.97" y2="47.08"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_scan.svg b/web/pgadmin/static/js/Explain/img/ex_scan.svg
new file mode 100644
index 000000000..fe3216105
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-2,.cls-8{fill:#2980b9;}.cls-1,.cls-2{stroke:#2980b9;stroke-linejoin:round;}.cls-2{stroke-linecap:round;stroke-width:2px;}.cls-3,.cls-6{fill:#f0ecb6;}.cls-4{fill:#c18f36;}.cls-5{opacity:0.8;}.cls-6{stroke:#c18f36;stroke-miterlimit:10;}.cls-7{fill:#addff3;}</style></defs><title>ex_scan</title><polygon class="cls-1" points="9.06 50.78 5.58 46.14 12.54 46.14 9.06 50.78"/><line class="cls-2" x1="9.06" y1="46.77" x2="9.06" y2="15.8"/><path class="cls-3" d="M18.43,57.47c-1.38,0-1.5-.12-1.5-1.5V15.53h41V56c0,1.38-.12,1.5-1.5,1.5Z"/><path class="cls-4" d="M57.41,16V56a3.71,3.71,0,0,1-.07.9,2.16,2.16,0,0,1-.93.1h-38a3.54,3.54,0,0,1-.9-.07h0a2.16,2.16,0,0,1-.1-.93V16h40m1-1h-42V56c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V15Z"/><g class="cls-5"><line class="cls-6" x1="25.13" y1="56.97" x2="25.13" y2="15.97"/><line class="cls-6" x1="41.52" y1="56.97" x2="41.52" y2="15.97"/><line class="cls-6" x1="33.32" y1="56.97" x2="33.32" y2="15.97"/><line class="cls-6" x1="49.72" y1="56.97" x2="49.72" y2="15.97"/><line class="cls-6" x1="17.42" y1="23.97" x2="57.4" y2="23.97"/><line class="cls-6" x1="17.42" y1="32.34" x2="57.4" y2="32.34"/><line class="cls-6" x1="17.42" y1="40.71" x2="57.4" y2="40.71"/><line class="cls-6" x1="17.42" y1="49.08" x2="57.4" y2="49.08"/></g><path class="cls-7" d="M49.73,15.53v-9h6.69c1.37,0,1.5.12,1.5,1.5v7.5Z"/><path class="cls-8" d="M56.42,7a3.54,3.54,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v7H50.23V7h6.19m0-1H49.23V16h9.19V8c0-1.65-.35-2-2-2Z"/><path class="cls-7" d="M16.92,15.53V8c0-1.38.13-1.5,1.5-1.5h6.7v9Z"/><path class="cls-8" d="M24.62,7v8h-7.2V8a3.18,3.18,0,0,1,.08-.9,2.08,2.08,0,0,1,.92-.1h6.2m1-1h-7.2c-1.65,0-2,.35-2,2v8h9.2V6Z"/><rect class="cls-7" x="41.52" y="6.53" width="8.19" height="9"/><path class="cls-8" d="M49.21,7v8H42V7h7.19m1-1H41V16h9.19V6Z"/><rect class="cls-7" x="33.33" y="6.53" width="8.19" height="9"/><path class="cls-8" d="M41,7v8H33.83V7H41m1-1H32.83V16H42V6Z"/><rect class="cls-7" x="25.14" y="6.53" width="8.17" height="9"/><path class="cls-8" d="M32.81,7v8H25.64V7h7.17m1-1H24.64V16h9.17V6Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_seek.svg b/web/pgadmin/static/js/Explain/img/ex_seek.svg
new file mode 100644
index 000000000..6a75a5eab
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_seek.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-4{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{opacity:0.8;}.cls-4{stroke:#c18f36;stroke-miterlimit:10;}.cls-5{fill:#addff3;}.cls-6{fill:#2980b9;}.cls-7,.cls-8{fill:#528d19;stroke:#528d19;stroke-linejoin:round;}.cls-8{stroke-linecap:round;stroke-width:2px;}.cls-9{fill:#34495e;}</style></defs><title>ex_seek</title><path class="cls-1" d="M18,57.46c-1.37,0-1.5-.13-1.5-1.5V15.52h41V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M57,16V56a3.28,3.28,0,0,1-.08.9A2.18,2.18,0,0,1,56,57H18a3.25,3.25,0,0,1-.9-.08h0A2.18,2.18,0,0,1,17,56V16H57m1-1H16V56c0,1.65.35,2,2,2H56c1.65,0,2-.35,2-2V15Z"/><g class="cls-3"><line class="cls-4" x1="24.7" y1="56.95" x2="24.7" y2="15.96"/><line class="cls-4" x1="41.1" y1="56.95" x2="41.1" y2="15.96"/><line class="cls-4" x1="32.9" y1="56.95" x2="32.9" y2="15.96"/><line class="cls-4" x1="49.3" y1="56.95" x2="49.3" y2="15.96"/><line class="cls-4" x1="17" y1="23.95" x2="56.97" y2="23.95"/><line class="cls-4" x1="17" y1="32.32" x2="56.97" y2="32.32"/><line class="cls-4" x1="17" y1="40.69" x2="56.97" y2="40.69"/><line class="cls-4" x1="17" y1="49.06" x2="56.97" y2="49.06"/></g><path class="cls-5" d="M49.31,15.51v-9H56c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-6" d="M56,7a3.25,3.25,0,0,1,.9.08h0A2.18,2.18,0,0,1,57,8v7H49.81V7H56m0-1H48.81V16H58V8c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M16.51,15.51V8c0-1.37.13-1.5,1.5-1.5H24.7v9Z"/><path class="cls-6" d="M24.2,7v8H17V8a3.28,3.28,0,0,1,.08-.9A2.18,2.18,0,0,1,18,7H24.2m1-1H18c-1.65,0-2,.35-2,2v8H25.2V6Z"/><rect class="cls-5" x="41.1" y="6.51" width="8.19" height="9"/><path class="cls-6" d="M48.79,7v8H41.6V7h7.18m1-1H40.61V16h9.19V6Z"/><rect class="cls-5" x="32.91" y="6.51" width="8.19" height="9"/><path class="cls-6" d="M40.6,7v8H33.41V7H40.6m1-1H32.42V16H41.6V6Z"/><rect class="cls-5" x="24.72" y="6.51" width="8.17" height="9"/><path class="cls-6" d="M32.4,7v8H25.22V7H32.4m1-1H24.22V16H33.4V6Z"/><polygon class="cls-7" points="14.91 53.1 11.53 50.57 11.53 55.63 14.91 53.1"/><line class="cls-8" x1="11.25" y1="53.1" x2="4.14" y2="53.1"/><polygon class="cls-7" points="14.91 36.29 11.53 33.76 11.53 38.82 14.91 36.29"/><line class="cls-8" x1="11.25" y1="36.29" x2="4.14" y2="36.29"/><line class="cls-8" x1="4" y1="15.66" x2="4" y2="53.1"/><rect class="cls-9" x="17.99" y="33.77" width="5.22" height="5.44"/><rect class="cls-9" x="17.99" y="50.57" width="5.22" height="5.44"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_setop.svg b/web/pgadmin/static/js/Explain/img/ex_setop.svg
new file mode 100644
index 000000000..43b309b5f
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_setop.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{opacity:0.7;}.cls-2{fill:#f0ecb6;}.cls-3{fill:#c18f35;}.cls-4{fill:#d3d4f3;}.cls-5{fill:#666bd1;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}</style></defs><title>ex_setop</title><g class="cls-1"><rect class="cls-2" x="46.01" y="35.58" width="7" height="7"/><path class="cls-3" d="M52.51,36.08v6h-6v-6h6m1-1h-8v8h8v-8Z"/><path class="cls-2" d="M19.51,49.56a1.5,1.5,0,0,1-1.5-1.5v-5.5h7v7Z"/><path class="cls-3" d="M24.51,43.06v6h-5a1,1,0,0,1-1-1v-5h6m1-1h-8v6a2,2,0,0,0,2,2h6v-8Z"/><rect class="cls-2" x="25.04" y="42.56" width="7" height="7"/><path class="cls-3" d="M31.54,43.06v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-2" x="32.01" y="42.56" width="7" height="7"/><path class="cls-3" d="M38.51,43.06v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-2" x="39.04" y="42.56" width="7" height="7"/><path class="cls-3" d="M45.54,43.06v6h-6v-6h6m1-1h-8v8h8v-8Z"/><path class="cls-2" d="M46,49.56v-7h7v5.5a1.5,1.5,0,0,1-1.5,1.5Z"/><path class="cls-3" d="M52.51,43.06v5a1,1,0,0,1-1,1h-5v-6h6m1-1h-8v8h6a2,2,0,0,0,2-2v-6Z"/><path class="cls-2" d="M46,28.43v-7h5.5a1.5,1.5,0,0,1,1.5,1.5v5.5Z"/><path class="cls-3" d="M51.51,21.93a1,1,0,0,1,1,1v5h-6v-6h5m0-1h-6v8h8v-6a2,2,0,0,0-2-2Z"/><rect class="cls-2" x="46.01" y="28.5" width="7" height="7"/><path class="cls-3" d="M52.51,29v6h-6V29h6m1-1h-8v8h8V28Z"/></g><g class="cls-1"><rect class="cls-4" x="10.99" y="28.36" width="7" height="7"/><path class="cls-5" d="M17.49,28.86v6h-6v-6h6m1-1h-8v8h8v-8Z"/><path class="cls-4" d="M12.49,42.36a1.5,1.5,0,0,1-1.5-1.5v-5.5h7v7Z"/><path class="cls-5" d="M17.49,35.86v6h-5a1,1,0,0,1-1-1v-5h6m1-1h-8v6a2,2,0,0,0,2,2h6v-8Z"/><rect class="cls-4" x="10.99" y="21.28" width="7" height="7"/><path class="cls-5" d="M17.49,21.78v6h-6v-6h6m1-1h-8v8h8v-8Z"/><path class="cls-4" d="M11,21.44v-5.5a1.5,1.5,0,0,1,1.5-1.5H18v7Z"/><path class="cls-5" d="M17.49,14.94v6h-6v-5a1,1,0,0,1,1-1h5m1-1h-6a2,2,0,0,0-2,2v6h8v-8Z"/><rect class="cls-4" x="17.96" y="14.44" width="7" height="7"/><path class="cls-5" d="M24.46,14.94v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-4" x="24.99" y="14.44" width="7" height="7"/><path class="cls-5" d="M31.49,14.94v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-4" x="32.01" y="14.44" width="7" height="7"/><path class="cls-5" d="M38.51,14.94v6h-6v-6h6m1-1h-8v8h8v-8Z"/><path class="cls-4" d="M39,21.44v-7h5.5a1.5,1.5,0,0,1,1.5,1.5v5.5Z"/><path class="cls-5" d="M44.49,14.94a1,1,0,0,1,1,1v5h-6v-6h5m0-1h-6v8h8v-6a2,2,0,0,0-2-2Z"/></g><rect class="cls-6" x="18.01" y="21.43" width="7" height="7"/><path class="cls-7" d="M24.51,21.93v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="25.04" y="21.43" width="7" height="7"/><path class="cls-7" d="M31.54,21.93v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="39.04" y="21.43" width="7" height="7"/><path class="cls-7" d="M45.54,21.93v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="39.04" y="28.5" width="7" height="7"/><path class="cls-7" d="M45.54,29v6h-6V29h6m1-1h-8v8h8V28Z"/><rect class="cls-6" x="18.01" y="35.58" width="7" height="7"/><path class="cls-7" d="M24.51,36.08v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="25.04" y="35.58" width="7" height="7"/><path class="cls-7" d="M31.54,36.08v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="32.01" y="35.58" width="7" height="7"/><path class="cls-7" d="M38.51,36.08v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="39.04" y="35.58" width="7" height="7"/><path class="cls-7" d="M45.54,36.08v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="32.01" y="21.43" width="7" height="7"/><path class="cls-7" d="M38.51,21.93v6h-6v-6h6m1-1h-8v8h8v-8Z"/><rect class="cls-6" x="18.01" y="28.5" width="7" height="7"/><path class="cls-7" d="M24.51,29v6h-6V29h6m1-1h-8v8h8V28Z"/><rect class="cls-6" x="25.04" y="28.5" width="7" height="7"/><path class="cls-7" d="M31.54,29v6h-6V29h6m1-1h-8v8h8V28Z"/><rect class="cls-6" x="32.01" y="28.5" width="7" height="7"/><path class="cls-7" d="M38.51,29v6h-6V29h6m1-1h-8v8h8V28Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_sort.svg b/web/pgadmin/static/js/Explain/img/ex_sort.svg
new file mode 100644
index 000000000..568639aad
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_sort.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#d3d4f3;}.cls-2{fill:#666bd1;}.cls-3{fill:#b6eeff;}.cls-4{fill:#0cc5ff;}.cls-5{fill:#c8ffac;}.cls-6{fill:#48d601;}.cls-7{fill:#f0ecb6;}.cls-8{fill:#c18f35;}.cls-9{fill:#ffe7e7;}.cls-10{fill:#ff3d3d;}.cls-11{fill:#addff3;}.cls-12{fill:#2980b9;}.cls-13{fill:#34495e;}.cls-14,.cls-15{fill:#528d19;stroke:#528d19;stroke-linejoin:round;}.cls-15{stroke-linecap:round;stroke-width:2px;}</style></defs><title>ex_sort</title><path class="cls-1" d="M13.19,50.94c-1.37,0-1.5-.13-1.5-1.5v-4.8H23v6.3Z"/><path class="cls-2" d="M22.47,45.14v5.3H13.19a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-4.3H22.47m1-1H11.19v5.3c0,1.65.35,2,2,2H23.47v-7.3Z"/><rect class="cls-3" x="11.69" y="38.31" width="11.28" height="6.3"/><path class="cls-4" d="M22.47,38.81v5.3H12.19v-5.3H22.47m1-1H11.19v7.3H23.47v-7.3Z"/><rect class="cls-5" x="11.69" y="31.97" width="11.28" height="6.31"/><path class="cls-6" d="M22.47,32.47v5.3H12.19v-5.3H22.47m1-1H11.19v7.3H23.47v-7.3Z"/><rect class="cls-7" x="11.69" y="25.68" width="11.28" height="6.3"/><path class="cls-8" d="M22.47,26.18v5.3H12.19v-5.3H22.47m1-1H11.19v7.3H23.47v-7.3Z"/><rect class="cls-9" x="11.69" y="19.38" width="11.28" height="6.3"/><path class="cls-10" d="M22.47,19.88v5.3H12.19v-5.3H22.47m1-1H11.19v7.3H23.47v-7.3Z"/><path class="cls-11" d="M11.69,19.36v-4.8c0-1.37.22-1.44,1.07-1.49H21.9c.85,0,1.07.12,1.07,1.49v4.8Z"/><path class="cls-12" d="M21.87,13.56h0a2.24,2.24,0,0,1,.5.07h0a2.23,2.23,0,0,1,.1.92v4.3H12.19v-4.3a3.07,3.07,0,0,1,.08-.9,1.3,1.3,0,0,1,.52-.09h9.08m1.6-1h0Zm0,0H12.74c-1.25.07-1.54.5-1.54,2v5.3H23.47v-5.3c0-1.48-.29-1.91-1.54-2Z"/><path class="cls-13" d="M17.51,20.93v3.36h-.77V20.93Z"/><path class="cls-13" d="M19.32,20.93v3.36h-.77V20.93Z"/><path class="cls-13" d="M15.67,20.93v3.36H14.9V20.93Z"/><path class="cls-13" d="M17.51,27.22v3.36h-.77V27.22Z"/><path class="cls-13" d="M19.32,27.22v3.36h-.77V27.22Z"/><path class="cls-13" d="M21.07,27.22v3.36H20.3V27.22Z"/><path class="cls-13" d="M14,27.22v3.36h-.77V27.22Z"/><path class="cls-13" d="M15.67,27.22v3.36H14.9V27.22Z"/><path class="cls-13" d="M18.65,39.78v3.36h-.77V39.78Z"/><path class="cls-13" d="M20.4,39.78v3.36h-.77V39.78Z"/><path class="cls-13" d="M15.23,39.78v3.36h-.77V39.78Z"/><path class="cls-13" d="M16.87,39.78v3.36H16.1V39.78Z"/><path class="cls-13" d="M17.51,33.59v3.36h-.77V33.59Z"/><path class="cls-13" d="M18.65,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M16.87,46.11v3.36H16.1V46.11Z"/><path class="cls-7" d="M42.53,50.94c-1.37,0-1.5-.13-1.5-1.5v-4.8H52.31v6.3Z"/><path class="cls-8" d="M51.81,45.14v5.3H42.53a3.26,3.26,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-4.3H51.81m1-1H40.53v5.3c0,1.65.35,2,2,2H52.81v-7.3Z"/><rect class="cls-3" x="41.03" y="38.31" width="11.28" height="6.3"/><path class="cls-4" d="M51.81,38.81v5.3H41.53v-5.3H51.81m1-1H40.53v7.3H52.81v-7.3Z"/><rect class="cls-9" x="41.03" y="31.97" width="11.28" height="6.31"/><path class="cls-10" d="M51.81,32.47v5.3H41.53v-5.3H51.81m1-1H40.53v7.3H52.81v-7.3Z"/><rect class="cls-1" x="41.03" y="25.68" width="11.28" height="6.3"/><path class="cls-2" d="M51.81,26.18v5.3H41.53v-5.3H51.81m1-1H40.53v7.3H52.81v-7.3Z"/><rect class="cls-5" x="41.03" y="19.38" width="11.28" height="6.3"/><path class="cls-6" d="M51.81,19.88v5.3H41.53v-5.3H51.81m1-1H40.53v7.3H52.81v-7.3Z"/><path class="cls-11" d="M41,19.36v-4.8c0-1.37.22-1.44,1.07-1.49h9.14c.85,0,1.07.12,1.07,1.49v4.8Z"/><path class="cls-12" d="M51.21,13.56h0a2.24,2.24,0,0,1,.5.07h0a2.23,2.23,0,0,1,.1.92v4.3H41.53v-4.3a3.07,3.07,0,0,1,.08-.9,1.3,1.3,0,0,1,.52-.09h9.08m1.6-1h0Zm0,0H42.07c-1.25.07-1.54.5-1.54,2v5.3H52.81v-5.3c0-1.48-.29-1.91-1.54-2Z"/><path class="cls-13" d="M46.84,33.44V36.8h-.77V33.44Z"/><path class="cls-13" d="M48.66,33.44V36.8h-.77V33.44Z"/><path class="cls-13" d="M45,33.44V36.8h-.77V33.44Z"/><path class="cls-13" d="M46.84,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M48.66,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M50.41,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M43.36,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M45,46.11v3.36h-.77V46.11Z"/><path class="cls-13" d="M48,39.78v3.36h-.77V39.78Z"/><path class="cls-13" d="M49.73,39.78v3.36H49V39.78Z"/><path class="cls-13" d="M44.57,39.78v3.36H43.8V39.78Z"/><path class="cls-13" d="M46.2,39.78v3.36h-.77V39.78Z"/><path class="cls-13" d="M46.84,20.85v3.36h-.77V20.85Z"/><path class="cls-13" d="M47.73,27.22v3.36H47V27.22Z"/><path class="cls-13" d="M46,27.22v3.36h-.77V27.22Z"/><polygon class="cls-14" points="37.38 32 34 29.47 34 34.53 37.38 32"/><line class="cls-15" x1="33.72" y1="32" x2="26.62" y2="32"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_subplan.svg b/web/pgadmin/static/js/Explain/img/ex_subplan.svg
new file mode 100644
index 000000000..e98e99f73
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_subplan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{fill:#addff3;}.cls-4{fill:#2980b9;}.cls-5,.cls-6,.cls-8{fill:none;}.cls-5{stroke:#c18f35;opacity:0.8;}.cls-10,.cls-11,.cls-5,.cls-6,.cls-8{stroke-linejoin:round;}.cls-6{stroke:#666bd1;}.cls-11,.cls-6,.cls-8{stroke-linecap:round;}.cls-6,.cls-8{stroke-width:1.5px;}.cls-7{fill:#666bd1;}.cls-8{stroke:#8d8119;}.cls-9{fill:#8d8119;}.cls-10,.cls-11{fill:#528d19;stroke:#528d19;}.cls-11{stroke-width:2px;}</style></defs><title>ex_subplan</title><path class="cls-1" d="M9,53.38c-1.37,0-1.5-.13-1.5-1.5V40.38h7.28v13Z"/><path class="cls-2" d="M14.23,40.88v12H9a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H7v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-1" d="M14.72,53.38v-13H22v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M21.5,40.88v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H15.22v-12H21.5m1-1H14.22v14H20.5c1.65,0,2-.35,2-2v-12Z"/><path class="cls-3" d="M7.45,40.38v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-4" d="M14.23,34.88v5H8v-4A3.28,3.28,0,0,1,8,35a2.18,2.18,0,0,1,.92-.1h5.28m1-1H9c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-3" d="M14.72,40.38v-6H20.5c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-4" d="M20.5,34.88a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v4H15.22v-5H20.5m0-1H14.22v7H22.5v-5c0-1.65-.35-2-2-2Z"/><line class="cls-5" x1="14.48" y1="46.73" x2="21.97" y2="46.73"/><line class="cls-5" x1="7.48" y1="46.73" x2="14.22" y2="46.73"/><path class="cls-1" d="M43.53,53.38c-1.37,0-1.5-.13-1.5-1.5V40.38h7.28v13Z"/><path class="cls-2" d="M48.81,40.88v12H43.53a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92v-11h6.28m1-1H41.53v12c0,1.65.35,2,2,2h6.28v-14Z"/><path class="cls-1" d="M49.3,53.38v-13h7.28v11.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M56.08,40.88v11a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H49.8v-12h6.28m1-1H48.8v14h6.28c1.65,0,2-.35,2-2v-12Z"/><path class="cls-3" d="M42,40.38v-4.5c0-1.37.13-1.5,1.5-1.5h5.78v6Z"/><path class="cls-4" d="M48.81,34.88v5H42.53v-4a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H43.53c-1.65,0-2,.35-2,2v5h8.28v-7Z"/><path class="cls-3" d="M49.3,40.38v-6h5.78c1.37,0,1.5.13,1.5,1.5v4.5Z"/><path class="cls-4" d="M55.08,34.88A3.25,3.25,0,0,1,56,35h0a2.17,2.17,0,0,1,.1.92v4H49.8v-5h5.28m0-1H48.8v7h8.28v-5c0-1.65-.35-2-2-2Z"/><line class="cls-5" x1="49.06" y1="46.73" x2="56.55" y2="46.73"/><line class="cls-5" x1="42.06" y1="46.73" x2="48.8" y2="46.73"/><path class="cls-6" d="M23.5,12.88a3,3,0,0,1,3-3h11a3,3,0,0,1,3,3v12a3,3,0,0,1-3,3h-11a3,3,0,0,1-3-3V19"/><polygon class="cls-7" points="23.5 16.54 21.27 19.51 25.73 19.51 23.5 16.54"/><path class="cls-8" d="M27,15.38c0-1.65.35-2,2-2h6c1.65,0,2,.35,2,2v7c0,1.65-.35,2-2,2H29.25c-1.65,0-2-.35-2-2V19.74"/><polygon class="cls-9" points="27.22 18.55 25.24 20.93 29 20.93 27.22 18.55"/><polygon class="cls-10" points="40.91 44.2 37.53 41.67 37.53 46.73 40.91 44.2"/><line class="cls-11" x1="39.25" y1="44.2" x2="32.14" y2="44.2"/><polygon class="cls-10" points="23.23 44.2 26.61 41.67 26.61 46.73 23.23 44.2"/><line class="cls-11" x1="24.9" y1="44.2" x2="32" y2="44.2"/><line class="cls-11" x1="32" y1="29.77" x2="32" y2="44.2"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_table_func_scan.svg b/web/pgadmin/static/js/Explain/img/ex_table_func_scan.svg
new file mode 100644
index 000000000..7a8555506
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_table_func_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f3c7d7;}.cls-2{fill:#d04465;}.cls-3{opacity:0.5;}.cls-4{fill:none;stroke:#d04465;}.cls-4,.cls-7,.cls-8{stroke-linejoin:round;}.cls-5{fill:#addff3;}.cls-6,.cls-7,.cls-8{fill:#2980b9;}.cls-7,.cls-8{stroke:#2980b9;}.cls-8{stroke-linecap:round;stroke-width:2px;}</style></defs><title>table_fun_scan</title><path class="cls-1" d="M22.85,49c-1.37,0-1.5-.12-1.5-1.5V24H54.14V47.47c0,1.38-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M53.64,24.53V47.47a3.18,3.18,0,0,1-.08.9s-.19.1-.92.1H22.85A3.39,3.39,0,0,1,22,48.4h0a2.4,2.4,0,0,1-.1-.93V24.53H53.64m1-1H20.85V47.47c0,1.65.35,2,2,2H52.64c1.65,0,2-.35,2-2V23.53Z"/><g class="cls-3"><line class="cls-4" x1="37.75" y1="48.47" x2="37.75" y2="24.47"/><line class="cls-4" x1="29.55" y1="48.47" x2="29.55" y2="24.47"/><line class="cls-4" x1="45.95" y1="48.47" x2="45.95" y2="24.47"/><line class="cls-4" x1="21.85" y1="32.47" x2="53.62" y2="32.47"/><line class="cls-4" x1="21.85" y1="40.84" x2="53.62" y2="40.84"/></g><path class="cls-5" d="M46,24V15h6.69c1.38,0,1.5.12,1.5,1.5V24Z"/><path class="cls-6" d="M52.64,15.53a3.44,3.44,0,0,1,.9.07h0a2.16,2.16,0,0,1,.1.93v7H46.45v-8h6.19m0-1H45.45v10h9.19v-8c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M21.35,24v-7.5c0-1.38.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-6" d="M29,15.53v8H21.85v-7a2.7,2.7,0,0,1,.08-.9s.19-.1.92-.1H29m1-1H22.85c-1.65,0-2,.35-2,2v8H30v-10Z"/><rect class="cls-5" x="37.75" y="15.03" width="8.19" height="9"/><path class="cls-6" d="M45.43,15.53v8H38.24v-8h7.18m1-1H37.25v10h9.19v-10Z"/><rect class="cls-5" x="29.56" y="15.03" width="8.19" height="9"/><path class="cls-6" d="M37.24,15.53v8H30.05v-8h7.18m1-1H29.06v10h9.19v-10Z"/><polygon class="cls-7" points="12.83 47.38 9.36 42.73 16.31 42.73 12.83 47.38"/><line class="cls-8" x1="12.83" y1="43.36" x2="12.83" y2="16.39"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_tid_scan.svg b/web/pgadmin/static/js/Explain/img/ex_tid_scan.svg
new file mode 100644
index 000000000..d42bafd57
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_tid_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#e6e6e6;}.cls-2{fill:#818181;}.cls-3{fill:#fbe65a;}.cls-4{fill:#c4953b;}.cls-5{fill:none;stroke:#2980b9;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5px;}.cls-6{fill:#2980b9;}</style></defs><title>ex_tid_scan</title><path class="cls-1" d="M35.47,50.69c-9.57,0-16-2.84-16-5.5s6.43-5.5,16-5.5,16,2.84,16,5.5S45,50.69,35.47,50.69Z"/><path class="cls-2" d="M35.47,40.19c8.87,0,15.5,2.64,15.5,5s-6.63,5-15.5,5S20,47.55,20,45.19s6.63-5,15.5-5m0-1c-9.11,0-16.5,2.69-16.5,6s7.39,6,16.5,6S52,48.5,52,45.19s-7.39-6-16.5-6Z"/><path class="cls-1" d="M35.47,46.28c-9.57,0-16-2.84-16-5.5s6.43-5.5,16-5.5,16,2.84,16,5.5S45,46.28,35.47,46.28Z"/><path class="cls-2" d="M35.47,35.78c8.87,0,15.5,2.64,15.5,5s-6.63,5-15.5,5S20,43.14,20,40.78s6.63-5,15.5-5m0-1c-9.11,0-16.5,2.69-16.5,6s7.39,6,16.5,6,16.5-2.69,16.5-6-7.39-6-16.5-6Z"/><path class="cls-1" d="M35.47,42c-9.57,0-16-2.84-16-5.5s6.43-5.5,16-5.5,16,2.84,16,5.5S45,42,35.47,42Z"/><path class="cls-2" d="M35.47,31.46c8.87,0,15.5,2.64,15.5,5s-6.63,5-15.5,5S20,38.83,20,36.46s6.63-5,15.5-5m0-1c-9.11,0-16.5,2.69-16.5,6s7.39,6,16.5,6,16.5-2.69,16.5-6-7.39-6-16.5-6Z"/><path class="cls-1" d="M35.47,37.52c-9.57,0-16-2.84-16-5.5s6.43-5.5,16-5.5,16,2.84,16,5.5S45,37.52,35.47,37.52Z"/><path class="cls-2" d="M35.47,27C44.34,27,51,29.66,51,32s-6.63,5-15.5,5S20,34.38,20,32s6.63-5,15.5-5m0-1C26.36,26,19,28.7,19,32s7.39,6,16.5,6S52,35.33,52,32s-7.39-6-16.5-6Z"/><path class="cls-1" d="M35.47,32.69c-9.57,0-16-2.84-16-5.5s6.43-5.5,16-5.5,16,2.84,16,5.5S45,32.69,35.47,32.69Z"/><path class="cls-2" d="M35.47,22.19c8.87,0,15.5,2.64,15.5,5s-6.63,5-15.5,5S20,29.55,20,27.19s6.63-5,15.5-5m0-1c-9.11,0-16.5,2.69-16.5,6s7.39,6,16.5,6S52,30.5,52,27.19s-7.39-6-16.5-6Z"/><path class="cls-3" d="M19.55,26.69c.73-2.45,6.72-4.91,15.43-5v5Z"/><path class="cls-4" d="M34.47,22.2v4H20.35c1.46-2,7-3.83,14.12-4m1-1c-9.11,0-16.5,2.69-16.5,6h16.5v-6Z"/><path class="cls-5" d="M12,26c2.38-8.37,12.92-13.21,22-13.21"/><path class="cls-6" d="M40.4,12.81a31.73,31.73,0,0,0-9,5l1.81-5-1.81-5A31.7,31.7,0,0,0,40.4,12.81Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_unique.svg b/web/pgadmin/static/js/Explain/img/ex_unique.svg
new file mode 100644
index 000000000..d6cc261b4
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_unique.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-3{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{stroke:#c18f35;}.cls-3,.cls-4,.cls-7,.cls-8{stroke-miterlimit:10;}.cls-3,.cls-7{opacity:0.8;}.cls-4{fill:#34495e;stroke:#528d19;}.cls-5,.cls-7,.cls-8{fill:#d3d4f3;}.cls-6{fill:#666bd1;}.cls-7{stroke:#666bd1;}.cls-8{stroke:#c00;stroke-linecap:round;stroke-width:1.5px;opacity:0.5;}</style></defs><title>ex_unique</title><rect class="cls-1" x="40.3" y="17.26" width="9.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M48.79,17.76a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-7a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V18.76a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h7m0-1h-7c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h7c1.65,0,2-.35,2-2V18.76c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="13.71" y="10.76" width="9.98" height="18.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M22.2,11.26a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92V27.95a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-7a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V12.26a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h7m0-1h-7c-1.65,0-2,.35-2,2V27.95c0,1.65.35,2,2,2h7c1.65,0,2-.35,2-2V12.26c0-1.65-.35-2-2-2Z"/><line class="cls-3" x1="14.1" y1="17.17" x2="23.34" y2="17.17"/><line class="cls-3" x1="14.1" y1="23.4" x2="23.34" y2="23.4"/><path class="cls-4" d="M28.87,27.47A4.42,4.42,0,0,0,30.92,27,2.81,2.81,0,0,0,32,25.88a3.59,3.59,0,0,0,.4-1.4c0-.5.07-.36.09-.83a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47,0-.33-.09-.83a3.58,3.58,0,0,0-.4-1.4,2.82,2.82,0,0,0-1.06-1.08,4.47,4.47,0,0,0-2.05-.51V12.3l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07,7,7,0,0,1,.16,1.29c0,.47,0-.12,0-.12a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73A2.39,2.39,0,0,0,33,22.47a7.9,7.9,0,0,0-.12,1.5s-.09.78-.18,1.17a3.65,3.65,0,0,1-.43,1.07,3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><rect class="cls-5" x="40.3" y="41.05" width="9.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-6" d="M48.79,41.55a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-7a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V42.55a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h7m0-1h-7c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h7c1.65,0,2-.35,2-2V42.55c0-1.65-.35-2-2-2Z"/><rect class="cls-5" x="13.79" y="34.55" width="9.98" height="18.69" rx="1.5" ry="1.5"/><path class="cls-6" d="M22.28,35.05a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92V51.74a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-7a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V36.05a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h7m0-1h-7c-1.65,0-2,.35-2,2V51.74c0,1.65.35,2,2,2h7c1.65,0,2-.35,2-2V36.05c0-1.65-.35-2-2-2Z"/><line class="cls-7" x1="14.18" y1="40.97" x2="23.42" y2="40.97"/><line class="cls-7" x1="14.18" y1="47.2" x2="23.42" y2="47.2"/><path class="cls-4" d="M28.87,51.26a4.42,4.42,0,0,0,2.05-.51A2.81,2.81,0,0,0,32,49.67a3.59,3.59,0,0,0,.4-1.4c0-.5.07-.36.09-.83a9.28,9.28,0,0,1,.11-1.2,2.76,2.76,0,0,1,.26-.83,2.37,2.37,0,0,1,.44-.6,2.53,2.53,0,0,1,.62-.46,4,4,0,0,1,.64-.27,3.5,3.5,0,0,1,.6-.13c.19,0,.19-.1,0-.12a3.52,3.52,0,0,1-.6-.13,3.65,3.65,0,0,1-.64-.27,2.52,2.52,0,0,1-.62-.46,2.41,2.41,0,0,1-.44-.6,2.77,2.77,0,0,1-.26-.83,9.31,9.31,0,0,1-.11-1.2c0-.47,0-.33-.09-.83a3.58,3.58,0,0,0-.4-1.4A2.82,2.82,0,0,0,30.92,37a4.47,4.47,0,0,0-2.05-.51V36.1l.53,0a4.11,4.11,0,0,1,.67.11,5.12,5.12,0,0,1,.72.23,2.89,2.89,0,0,1,.7.41,3.34,3.34,0,0,1,.83.9,3.66,3.66,0,0,1,.43,1.07,7,7,0,0,1,.16,1.29c0,.47,0-.12,0-.12a7.9,7.9,0,0,0,.12,1.5,2.37,2.37,0,0,0,.47,1.06,2.65,2.65,0,0,0,1,.73.69.69,0,0,1,0,1.17,2.66,2.66,0,0,0-1,.73A2.39,2.39,0,0,0,33,46.27a7.9,7.9,0,0,0-.12,1.5s-.09.78-.18,1.17A3.65,3.65,0,0,1,32.32,50a3.31,3.31,0,0,1-.83.9,2.9,2.9,0,0,1-.7.41,5.39,5.39,0,0,1-.72.23,4.34,4.34,0,0,1-.67.11l-.53,0Z"/><line class="cls-8" x1="14.1" y1="29.05" x2="23.34" y2="10.99"/><line class="cls-8" x1="14.18" y1="52.92" x2="23.42" y2="34.87"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_unknown.svg b/web/pgadmin/static/js/Explain/img/ex_unknown.svg
new file mode 100644
index 000000000..3af69574d
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_unknown.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#1cafe4;}</style></defs><title>ex_unknown</title><path class="cls-1" d="M23.34,13.2a14.76,14.76,0,0,1,8.16-2.14,17.57,17.57,0,0,1,10.56,3q4.2,3,4.2,9a10.18,10.18,0,0,1-1.82,6.16,21,21,0,0,1-4.09,3.88l-2,1.55a6,6,0,0,0-2.16,3A12.5,12.5,0,0,0,35.83,41H28.2a22.11,22.11,0,0,1,.9-6.57,11.57,11.57,0,0,1,3.76-4.18l2-1.6A7.17,7.17,0,0,0,36.53,27a5.67,5.67,0,0,0,1.12-3.4,6.58,6.58,0,0,0-1.25-3.9q-1.25-1.76-4.56-1.76t-4.62,2.17a8.34,8.34,0,0,0-1.36,4.5H17.74Q18.08,16.55,23.34,13.2ZM28,44.81h8.41v8.13H28Z"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_update.svg b/web/pgadmin/static/js/Explain/img/ex_update.svg
new file mode 100644
index 000000000..1f82bb1db
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_update.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-5{fill:#f0ecb6;}.cls-2{fill:#c18f36;}.cls-3{fill:#ffbd30;}.cls-4{opacity:0.8;}.cls-5{stroke:#c18f36;stroke-miterlimit:10;}.cls-6{fill:#addff3;}.cls-7{fill:#2980b9;}</style></defs><title>ex_update</title><path class="cls-1" d="M16.73,57.47c-1.37,0-1.5-.13-1.5-1.5V15.53h41V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-2" d="M55.72,16V56a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-38a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V16h40m1-1h-42V56c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V15Z"/><rect class="cls-3" x="32.12" y="41.2" width="7.22" height="7.44"/><rect class="cls-3" x="23.92" y="41.2" width="7.22" height="7.44"/><rect class="cls-3" x="15.72" y="24.48" width="7.22" height="7.44"/><g class="cls-4"><line class="cls-5" x1="23.43" y1="56.97" x2="23.43" y2="15.97"/><line class="cls-5" x1="39.83" y1="56.97" x2="39.83" y2="15.97"/><line class="cls-5" x1="31.63" y1="56.97" x2="31.63" y2="15.97"/><line class="cls-5" x1="48.03" y1="56.97" x2="48.03" y2="15.97"/><line class="cls-5" x1="15.73" y1="23.97" x2="55.7" y2="23.97"/><line class="cls-5" x1="15.73" y1="32.34" x2="55.7" y2="32.34"/><line class="cls-5" x1="15.73" y1="40.71" x2="55.7" y2="40.71"/><line class="cls-5" x1="15.73" y1="49.08" x2="55.7" y2="49.08"/></g><path class="cls-6" d="M48,15.53v-9h6.69c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-7" d="M54.73,7a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H48.53V7h6.19m0-1H47.53V16h9.19V8c0-1.65-.35-2-2-2Z"/><path class="cls-6" d="M15.23,15.53V8c0-1.37.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-7" d="M22.93,7v8H15.73V8a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.19m1-1H16.73c-1.65,0-2,.35-2,2v8h9.19V6Z"/><rect class="cls-6" x="39.83" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M47.52,7v8H40.33V7h7.18m1-1H39.33V16h9.19V6Z"/><rect class="cls-6" x="31.64" y="6.53" width="8.19" height="9"/><path class="cls-7" d="M39.33,7v8H32.14V7h7.18m1-1H31.14V16h9.19V6Z"/><rect class="cls-6" x="23.45" y="6.53" width="8.17" height="9"/><path class="cls-7" d="M31.12,7v8H23.95V7h7.17m1-1H23V16h9.18V6Z"/><path class="cls-2" d="M7.27,28.28A3.37,3.37,0,0,1,13,25.85h0l.13.13.05.05L24.72,37.58a.4.4,0,0,1,.18.2l1.63,5.82v.06h0a.46.46,0,0,1,0,.12.41.41,0,0,1,0,.12s0,0,0,0h0a.4.4,0,0,1-.06.1l0,0,0,0-.1.06h0l-.12,0-.12,0H26l-5.82-1.63a.4.4,0,0,1-.2-.18L8.22,30.64a.4.4,0,0,1-.08-.12A3.35,3.35,0,0,1,7.27,28.28Zm12.9,13.15,1-2h0l-9.93-9.93L9.73,31Zm5,.55-.84.84,1.17.33Zm-1-3.48L22,39.6l-1.09,2.26,2.57.72,1.49-1.49Zm-.42-.7L13.35,27.37l-1.52,1.52,9.93,9.93h0ZM8.1,28.31a2.55,2.55,0,0,0,.83,1.88h0l.22.22,3.62-3.62-.22-.22h0A2.56,2.56,0,0,0,8.1,28.31Z"/><polygon class="cls-1" points="20.17 41.45 21.17 39.42 21.18 39.41 11.25 29.48 9.73 31 20.17 41.45"/><polygon class="cls-1" points="13.35 27.38 11.83 28.9 21.76 38.83 21.77 38.82 23.79 37.83 13.35 27.38"/><path class="cls-1" d="M8.12,28.27A2.55,2.55,0,0,0,9,30.15H9l.22.22,3.62-3.62-.22-.22h0a2.56,2.56,0,0,0-4.45,1.73Z"/><polygon class="cls-1" points="24.22 38.52 21.96 39.6 20.87 41.86 23.44 42.58 24.94 41.09 24.22 38.52"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_values_scan.svg b/web/pgadmin/static/js/Explain/img/ex_values_scan.svg
new file mode 100644
index 000000000..42c831901
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_values_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3{fill:#2980b9;stroke:#2980b9;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style></defs><title>ex_values_scan</title><rect class="cls-1" x="20.5" y="14.17" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M26,14.67a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H22a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V15.67a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H22c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V15.67c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="33.5" y="17.17" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M39,17.67a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H35a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V18.67a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H35c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V18.67c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="46.5" y="20.14" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M52,20.64a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H48a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V21.64a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H48c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V21.64c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="20.5" y="26.25" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M26,26.75a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H22a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V27.75a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H22c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V27.75c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="33.5" y="29.25" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M39,29.75a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H35a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V30.75a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H35c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V30.75c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="46.5" y="32.22" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M52,32.72a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H48a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V33.72a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H48c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V33.72c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="20.5" y="38.17" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M26,38.67a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H22a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V39.67a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H22c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V39.67c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="33.5" y="41.17" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M39,41.67a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H35a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V42.67a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H35c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V42.67c0-1.65-.35-2-2-2Z"/><rect class="cls-1" x="46.5" y="44.14" width="6.98" height="5.69" rx="1.5" ry="1.5"/><path class="cls-2" d="M52,44.64a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v2.69a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H48a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V45.64a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h4m0-1H48c-1.65,0-2,.35-2,2v2.69c0,1.65.35,2,2,2h4c1.65,0,2-.35,2-2V45.64c0-1.65-.35-2-2-2Z"/><polygon class="cls-3" points="13.49 49.49 10.01 44.85 16.97 44.85 13.49 49.49"/><line class="cls-3" x1="13.49" y1="45.48" x2="13.49" y2="14.51"/></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_window_aggregate.svg b/web/pgadmin/static/js/Explain/img/ex_window_aggregate.svg
new file mode 100644
index 000000000..85b3c855c
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_window_aggregate.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1{fill:#f0ecb6;}.cls-2{fill:#c18f35;}.cls-3,.cls-8{fill:#c8ffac;}.cls-4{fill:#48d601;}.cls-5{fill:#d3d4f3;}.cls-6{fill:#666bd1;}.cls-10,.cls-7,.cls-9{fill:none;}.cls-7{stroke:#666bd1;}.cls-10,.cls-11,.cls-7,.cls-9{stroke-linejoin:round;}.cls-7,.cls-9{opacity:0.8;}.cls-8{stroke:#48d601;stroke-miterlimit:10;opacity:0.7;}.cls-9{stroke:#c18f35;}.cls-10,.cls-11{stroke:#34495e;}.cls-10{stroke-linecap:round;}.cls-11{fill:#34495e;}</style></defs><title>ex_window_aggregate</title><g id="ex_broadcast_motion"><rect class="cls-1" x="40.67" y="27.31" width="8.28" height="9"/><path class="cls-2" d="M48.45,27.81v8H41.17v-8h7.28m1-1H40.17v10h9.28v-10Z"/><rect class="cls-1" x="48.97" y="27.31" width="8.28" height="9"/><path class="cls-2" d="M56.75,27.81v8H49.47v-8h7.28m1-1H48.47v10h9.28v-10Z"/><path class="cls-3" d="M42.17,45.29c-1.37,0-1.5-.13-1.5-1.5v-7.5h8.28v9Z"/><path class="cls-4" d="M48.45,36.79v8H42.17a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92v-7h7.28m1-1H40.17v8c0,1.65.35,2,2,2h7.28v-10Z"/><path class="cls-3" d="M49,45.29v-9h8.28v7.5c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M56.75,36.79v7a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H49.47v-8h7.28m1-1H48.47v10h7.28c1.65,0,2-.35,2-2v-8Z"/><path class="cls-5" d="M40.67,27.33v-7.5c0-1.37.13-1.5,1.5-1.5h6.78v9Z"/><path class="cls-6" d="M48.45,18.83v8H41.17v-7a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h6.28m1-1H42.17c-1.65,0-2,.35-2,2v8h9.28v-10Z"/><path class="cls-5" d="M49,27.33v-9h6.78c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-6" d="M55.75,18.83a3.25,3.25,0,0,1,.9.08h0a2.17,2.17,0,0,1,.1.92v7H49.47v-8h6.28m0-1H48.47v10h9.28v-8c0-1.65-.35-2-2-2Z"/><path class="cls-5" d="M11.28,25.49V14c0-1.37.13-1.5,1.5-1.5h5.78v13Z"/><path class="cls-6" d="M18.06,13V25H11.78V14a3.28,3.28,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1h5.28m1-1H12.78c-1.65,0-2,.35-2,2V26h8.28V12Z"/><path class="cls-5" d="M18.57,25.49v-13h5.78c1.37,0,1.5.13,1.5,1.5v11.5Z"/><path class="cls-6" d="M24.35,13a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92V25H19.07V13h5.28m0-1H18.07V26h8.28V14c0-1.65-.35-2-2-2Z"/><line class="cls-7" x1="11.31" y1="18.83" x2="18.05" y2="18.83"/><line class="cls-7" x1="18.33" y1="18.83" x2="25.82" y2="18.83"/><path class="cls-3" d="M12.78,51.51c-1.37,0-1.5-.13-1.5-1.5V38.51h7.28v13Z"/><path class="cls-4" d="M18.06,39V51H12.78a3.25,3.25,0,0,1-.9-.08h0a2.18,2.18,0,0,1-.1-.92V39h6.28m1-1H10.78V50c0,1.65.35,2,2,2h6.28V38Z"/><path class="cls-3" d="M18.57,51.51v-13h7.28V50c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M25.35,39V50a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1H19.07V39h6.28m1-1H18.07V52h6.28c1.65,0,2-.35,2-2V38Z"/><line class="cls-8" x1="18.33" y1="44.86" x2="25.82" y2="44.86"/><line class="cls-8" x1="11.31" y1="44.86" x2="18.05" y2="44.86"/><rect class="cls-1" x="18.57" y="25.49" width="7.28" height="13"/><path class="cls-2" d="M25.35,26V38H19.07V26h6.28m1-1H18.07V39h8.28V25Z"/><line class="cls-9" x1="18.33" y1="31.84" x2="25.82" y2="31.84"/><rect class="cls-1" x="11.28" y="25.49" width="7.28" height="13"/><path class="cls-2" d="M18.06,26V38H11.78V26h6.28m1-1H10.78V39h8.28V25Z"/><line class="cls-9" x1="11.31" y1="31.84" x2="18.05" y2="31.84"/><rect class="cls-10" x="6.25" y="18.84" width="25" height="25.99" rx="2" ry="2"/><polygon class="cls-11" points="39.32 22.95 36.68 24.93 36.68 20.98 39.32 22.95"/><line class="cls-10" x1="37.14" y1="23.05" x2="31.25" y2="23.09"/><polygon class="cls-11" points="39.32 31.84 36.68 33.82 36.68 29.86 39.32 31.84"/><line class="cls-10" x1="37.14" y1="31.94" x2="31.25" y2="31.98"/><polygon class="cls-11" points="39.32 40.6 36.68 42.58 36.68 38.63 39.32 40.6"/><line class="cls-10" x1="37.14" y1="40.7" x2="31.25" y2="40.75"/></g></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/img/ex_worktable_scan.svg b/web/pgadmin/static/js/Explain/img/ex_worktable_scan.svg
new file mode 100644
index 000000000..eebc0169c
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/img/ex_worktable_scan.svg
@@ -0,0 +1 @@
+<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-1,.cls-9{fill:#2980b9;}.cls-1,.cls-2{stroke:#2980b9;}.cls-1,.cls-2,.cls-6,.cls-7{stroke-linejoin:round;}.cls-2,.cls-6,.cls-7{fill:none;}.cls-2{stroke-linecap:round;stroke-width:2px;}.cls-3{fill:#f3c7d7;}.cls-4{fill:#d04465;}.cls-5{opacity:0.5;}.cls-6,.cls-7{stroke:#d04465;}.cls-7{opacity:0.8;}.cls-8{fill:#addff3;}.cls-10{font-size:10px;fill:#34495e;font-family:HelveticaNeue-Bold, Helvetica Neue;font-weight:700;}.cls-10,.cls-12{letter-spacing:0.1em;}.cls-11{letter-spacing:0.01em;}</style></defs><title>ex_worktable_scan</title><polygon class="cls-1" points="9.72 50.78 6.25 46.14 13.2 46.14 9.72 50.78"/><line class="cls-2" x1="9.72" y1="46.77" x2="9.72" y2="15.8"/><path class="cls-3" d="M17.76,57.47c-1.37,0-1.5-.13-1.5-1.5V15.53h41V56c0,1.37-.13,1.5-1.5,1.5Z"/><path class="cls-4" d="M56.75,16V56a3.28,3.28,0,0,1-.08.9,2.18,2.18,0,0,1-.92.1h-38a3.25,3.25,0,0,1-.9-.08h0a2.17,2.17,0,0,1-.1-.92V16h40m1-1h-42V56c0,1.65.35,2,2,2h38c1.65,0,2-.35,2-2V15Z"/><g class="cls-5"><line class="cls-6" x1="24.46" y1="56.97" x2="24.46" y2="15.97"/><line class="cls-6" x1="40.86" y1="56.97" x2="40.86" y2="15.97"/><line class="cls-6" x1="32.66" y1="56.97" x2="32.66" y2="15.97"/><line class="cls-6" x1="49.06" y1="56.97" x2="49.06" y2="15.97"/><line class="cls-6" x1="16.76" y1="23.97" x2="56.73" y2="23.97"/><line class="cls-7" x1="16.76" y1="32.34" x2="56.73" y2="32.34"/><line class="cls-6" x1="16.76" y1="40.71" x2="56.73" y2="40.71"/><line class="cls-6" x1="16.76" y1="49.08" x2="56.73" y2="49.08"/></g><path class="cls-8" d="M49.06,15.53v-9h6.69c1.37,0,1.5.13,1.5,1.5v7.5Z"/><path class="cls-9" d="M55.76,7a3.25,3.25,0,0,1,.9.08h0a2.18,2.18,0,0,1,.1.92v7H49.56V7h6.19m0-1H48.56V16h9.19V8c0-1.65-.35-2-2-2Z"/><path class="cls-8" d="M16.26,15.53V8c0-1.37.13-1.5,1.5-1.5h6.69v9Z"/><path class="cls-9" d="M24,7v8H16.76V8a3.29,3.29,0,0,1,.08-.9,2.18,2.18,0,0,1,.92-.1H24m1-1H17.76c-1.65,0-2,.35-2,2v8H25V6Z"/><rect class="cls-8" x="40.86" y="6.53" width="8.19" height="9"/><path class="cls-9" d="M48.54,7v8H41.36V7h7.18m1-1H40.36V16h9.19V6Z"/><rect class="cls-8" x="32.67" y="6.53" width="8.19" height="9"/><path class="cls-9" d="M40.36,7v8H33.17V7h7.18m1-1H32.17V16h9.19V6Z"/><rect class="cls-8" x="24.48" y="6.53" width="8.17" height="9"/><path class="cls-9" d="M32.15,7v8H25V7h7.17m1-1H24V16h9.18V6Z"/><text class="cls-10" transform="translate(18.98 33.55)">WORK<tspan class="cls-11"><tspan x="-0.42" y="12">T</tspan><tspan class="cls-12" x="5.78" y="12">ABLE</tspan></tspan></text></svg>
\ No newline at end of file
diff --git a/web/pgadmin/static/js/Explain/index.jsx b/web/pgadmin/static/js/Explain/index.jsx
new file mode 100644
index 000000000..f5845a9e8
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/index.jsx
@@ -0,0 +1,487 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { Box, Tab, Tabs } from '@material-ui/core';
+import React from 'react';
+import _ from 'lodash';
+import Graphical from './Graphical';
+import TabPanel from '../components/TabPanel';
+import gettext  from 'sources/gettext';
+import ImageMapper from './ImageMapper';
+import { makeStyles } from '@material-ui/styles';
+import Analysis from './Analysis';
+import ExplainStatistics from './ExplainStatistics';
+import PropTypes from 'prop-types';
+import EmptyPanelMessage from '../components/EmptyPanelMessage';
+
+const useStyles = makeStyles((theme)=>({
+  tabPanel: {
+    padding: 0,
+    backgroundColor: theme.palette.background.default,
+  }
+}));
+
+// Some predefined constants used to calculate image location and its border
+var pWIDTH = 100.;
+var pHEIGHT = 100.;
+var offsetX = 200,
+  offsetY = 60;
+var xMargin = 25,
+  yMargin = 25;
+
+const DEFAULT_ARROW_SIZE = 2;
+
+function nodeExplainTableData(_planData, _ctx) {
+  let node_info,
+    display_text = [],
+    tooltip = [],
+    node_extra_info = [],
+    info = _ctx.explainTable;
+
+  // Display: <NODE>[ using <Index> ] [ on <Schema>.<Table>[ as <Alias>]]
+
+  if (/Scan/.test(_planData['Node Type'])) {
+    display_text.push(_planData['Node Type']);
+    tooltip.push(_planData['Node Type']);
+  } else {
+    display_text.push(_planData['image_text']);
+    tooltip.push(_planData['image_text']);
+  }
+  node_info = tooltip.join('');
+
+  if (typeof(_planData['Index Name']) !== 'undefined') {
+    display_text.push(' using ');
+    tooltip.push(' using ');
+    display_text.push(_.escape(_planData['Index Name']));
+    tooltip.push(_planData['Index Name']);
+  }
+
+  if (typeof(_planData['Relation Name']) !== 'undefined') {
+    display_text.push(' on ');
+    tooltip.push(' on ');
+    if (typeof(_planData['Schema']) !== 'undefined') {
+      display_text.push(_.escape(_planData['Schema']));
+      tooltip.push(_planData['Schema']);
+      display_text.push('.');
+      tooltip.push('.');
+    }
+    display_text.push(_.escape(_planData['Relation Name']));
+    tooltip.push(_planData['Relation Name']);
+
+    if (typeof(_planData['Alias']) !== 'undefined') {
+      display_text.push(' as ');
+      tooltip.push(' as ');
+      display_text.push(_.escape(_planData['Alias']));
+      tooltip.push(_.escape(_planData['Alias']));
+    }
+  }
+
+  if (
+    typeof(_planData['Plan Rows']) !== 'undefined' &&
+    typeof(_planData['Plan Width']) !== 'undefined'
+  ) {
+    let cost = [
+      ' (cost=',
+      (typeof(_planData['Startup Cost']) !== 'undefined' ?
+        _planData['Startup Cost'] : ''),
+      '..',
+      (typeof(_planData['Total Cost']) !== 'undefined' ?
+        _planData['Total Cost'] : ''),
+      ' rows=',
+      _planData['Plan Rows'],
+      ' width=',
+      _planData['Plan Width'],
+      ')',
+    ].join('');
+    display_text.push(cost);
+    tooltip.push(cost);
+  }
+
+  if (
+    typeof(_planData['Actual Startup Time']) !== 'undefined' ||
+    typeof(_planData['Actual Total Time']) !== 'undefined' ||
+    typeof(_planData['Actual Rows']) !== 'undefined'
+  ) {
+    let actual = [
+      ' (',
+      (typeof(_planData['Actual Startup Time']) !== 'undefined' ?
+        ('actual=' + _planData['Actual Startup Time']) + '..' : ''
+      ),
+      (typeof(_planData['Actual Total Time']) !== 'undefined' ?
+        _planData['Actual Total Time'] + ' ' : ''
+      ),
+      (typeof(_planData['Actual Rows']) !== 'undefined' ?
+        ('rows=' + _planData['Actual Rows']) : ''
+      ),
+      (typeof(_planData['Actual Loops']) !== 'undefined' ?
+        (' loops=' + _planData['Actual Loops']) : ''
+      ),
+      ')',
+    ].join('');
+
+    display_text.push(actual);
+    tooltip.push(actual);
+  }
+
+  if ('Join Filter' in _planData) {
+    node_extra_info.push(
+      '<strong>' + gettext('Join Filter') + '</strong>: ' + _.escape(_planData['Join Filter'])
+    );
+  }
+
+  if ('Filter' in _planData) {
+    node_extra_info.push('<strong>' + gettext('Filter') + '</strong>: ' + _.escape(_planData['Filter']));
+  }
+
+  if ('Index Cond' in _planData) {
+    node_extra_info.push('<strong>' + gettext('Index Cond') + '</strong>: ' + _.escape(_planData['Index Cond']));
+  }
+
+  if ('Hash Cond' in _planData) {
+    node_extra_info.push('<strong>' + gettext('Hash Cond') + '</strong>: ' + _.escape(_planData['Hash Cond']));
+  }
+
+  if ('Rows Removed by Filter' in _planData) {
+    node_extra_info.push(
+      '<strong>' + gettext('Rows Removed by Filter') + '</strong>: ' +
+        _.escape(_planData['Rows Removed by Filter'])
+    );
+  }
+
+  if ('Peak Memory Usage' in _planData) {
+    var buffer = [
+      '<strong>' + gettext('Buckets') + '</strong>:', _.escape(_planData['Hash Buckets']),
+      '<strong>' + gettext('Batches') + '</strong>:', _.escape(_planData['Hash Batches']),
+      '<strong>' + gettext('Memory Usage') + '</strong>:', _.escape(_planData['Peak Memory Usage']), 'kB',
+    ].join(' ');
+    node_extra_info.push(buffer);
+  }
+
+  if ('Recheck Cond' in _planData) {
+    node_extra_info.push('<strong>' + gettext('Recheck Cond') + '</strong>: ' + _planData['Recheck Cond']);
+  }
+
+  if ('Exact Heap Blocks' in _planData) {
+    node_extra_info.push('<strong>' + gettext('Heap Blocks') + '</strong>: exact=' + _planData['Exact Heap Blocks']);
+  }
+
+  info.rows.push({
+    data: _planData,
+    display_text: display_text.join(''),
+    tooltip_text: tooltip.join(''),
+    node_extra_info: node_extra_info,
+  });
+
+  if (typeof(_planData['exclusive_flag']) !== 'undefined') {
+    info.show_timings = true;
+  }
+
+  if (typeof(_planData['rowsx_flag']) !== 'undefined') {
+    info.show_rowsx = true;
+  }
+
+  if (typeof(_planData['Actual Loops']) !== 'undefined') {
+    info.show_rows = true;
+  }
+
+  if (typeof(_planData['Plan Rows']) !== 'undefined') {
+    info.show_plan_rows = true;
+  }
+
+  if (typeof(_planData['total_time']) !== 'undefined') {
+    info.total_time = _planData['total_time'];
+  }
+
+  let node;
+
+  if (typeof(_planData['Relation Name']) !== 'undefined') {
+    let relationName = (
+        typeof(_planData['Schema']) !== 'undefined' ?
+          (_planData['Schema'] + '.') : ''
+      ) + _planData['Relation Name'],
+      table = info.statistics.tables[relationName] || {
+        name: relationName,
+        count: 0,
+        sum_of_times: 0,
+        nodes: {},
+      };
+
+    node = table.nodes[node_info] || {
+      name: node_info,
+      count: 0,
+      sum_of_times: 0,
+    };
+
+    table.count++;
+    table.sum_of_times += _planData['exclusive'];
+    node.count++;
+    node.sum_of_times += _planData['exclusive'];
+
+    table.nodes[node_info] = node;
+    info.statistics.tables[relationName] = table;
+  }
+
+  node = info.statistics.nodes[node_info] || {
+    name: node_info,
+    count: 0,
+    sum_of_times: 0,
+  };
+
+  node.count++;
+  node.sum_of_times += _planData['exclusive'];
+  info.statistics.nodes[node_info] = node;
+}
+
+function parsePlan(data, ctx) {
+  var idx = 1,
+    lvl = data.level = data.level || [idx],
+    plans = [],
+    nodeType = data['Node Type'],
+    // Calculating relative xpos of current node from top node
+    xpos = data.xpos = data.xpos - pWIDTH,
+    // Calculating relative ypos of current node from top node
+    ypos = data.ypos,
+    maxChildWidth = 0;
+
+  ctx.totalNodes++;
+  ctx.explainTable.total_time = data['total_time'] || data['Actual Total Time'];
+
+  data['_serial'] = ctx.totalNodes;
+  data['width'] = pWIDTH;
+  data['height'] = pHEIGHT;
+
+  // Calculate arrow width according to cost of a particular plan
+  let arrowSize = DEFAULT_ARROW_SIZE;
+  let startCost = data['Startup Cost'],
+    totalCost = data['Total Cost'];
+  if (startCost != undefined && totalCost != undefined) {
+    arrowSize = Math.round(Math.log((startCost + totalCost) / 2 + startCost));
+    arrowSize = arrowSize < 1 ? 1 : arrowSize > 10 ? 10 : arrowSize;
+  }
+  data['arr_id'] = _.uniqueId('arr');
+  ctx.arrows[data['arr_id']] = arrowSize;
+  /*
+   * calculating xpos, ypos, width and height if current node is a subplan
+   */
+  if (data['Parent Relationship'] === 'SubPlan') {
+    data['width'] += (xMargin * 2) + (xMargin / 2);
+    data['height'] += (yMargin * 2);
+    data['ypos'] += yMargin;
+    xpos -= xMargin;
+    ypos += yMargin;
+  }
+
+  if (nodeType.startsWith('(slice'))
+    nodeType = nodeType.substring(0, 7);
+
+  // Get the image information for current node
+  var mappedImage = (_.isFunction(ImageMapper[nodeType]) &&
+      ImageMapper[nodeType].apply(undefined, [data])) ||
+    ImageMapper[nodeType] || {
+    'image': 'ex_unknown.svg',
+    'image_text': nodeType,
+  };
+
+  data['image'] = mappedImage['image'];
+  data['image_text'] = mappedImage['image_text'];
+
+  if ('Actual Total Time' in data && 'Actual Loops' in data) {
+    data['inclusive'] = Math.ceil10(
+      data['Actual Total Time'] * data['Actual Loops'], -3
+    );
+    data['exclusive'] = data['inclusive'];
+    data['inclusive_factor'] =  data['inclusive'] / (
+      data['total_time'] || data['Actual Total Time']
+    );
+    data['inclusive_flag'] = data['inclusive_factor'] <= 0.1 ? '1' :
+      data['inclusive_factor'] < 0.5 ? '2' :
+        data['inclusive_factor'] <= 0.9 ? '3' : '4';
+  }
+
+  if ('Actual Rows' in data && 'Plan Rows' in data) {
+    if (
+      data['Actual Rows'] === 0 || data['Actual Rows'] > data['Plan Rows']
+    ) {
+      data['rowsx'] = data['Plan Rows'] === 0 ? 0 :
+        (data['Actual Rows'] / data['Plan Rows']);
+      data['rowsx_direction'] = 'negative';
+    } else {
+      data['rowsx'] = data['Actual Rows'] === 0 ? 0 : (
+        data['Plan Rows'] / data['Actual Rows']
+      );
+      data['rowsx_direction'] = 'positive';
+    }
+    data['rowsx_flag'] = data['rowsx'] <= 10 ? '1' : (
+      data['rowsx'] <= 100 ? '2' : (data['rowsx'] <= 1000 ? '3' : '4')
+    );
+    data['rowsx'] = Math.ceil10(data['rowsx'], -2);
+  }
+
+  // Start calculating xpos, ypos, width and height for child plans if any
+  if ('Plans' in data) {
+    data['width'] += offsetX;
+
+    data['Plans'].forEach(function(p) {
+      let level = _.clone(lvl);
+      level.push(idx);
+
+      let plan = parsePlan({
+        ...p,
+        'level': level,
+        xpos: xpos - offsetX,
+        ypos: ypos,
+        total_time: data['total_time'] || data['Actual Total Time'],
+        parent_node: lvl.join('_'),
+      }, ctx);
+
+      if (maxChildWidth < plan.width) {
+        maxChildWidth = plan.width;
+      }
+
+      if ('exclusive' in data) {
+        if (plan.inclusive) {
+          data['exclusive'] -= plan.inclusive;
+        }
+      }
+
+      var childHeight = plan.height;
+
+      if (idx !== 1) {
+        data['height'] = data['height'] + childHeight + offsetY;
+      } else if (childHeight > data['height']) {
+        data['height'] = childHeight;
+      }
+      ypos += childHeight + offsetY;
+
+      plans.push(plan);
+      idx++;
+    });
+  }
+
+  if ('exclusive' in data) {
+    data['exclusive'] = Math.ceil10(data['exclusive'], -3);
+    data['exclusive_factor'] = (
+      data['exclusive'] / (data['total_time'] || data['Actual Total Time'])
+    );
+    data['exclusive_flag'] = data['exclusive_factor'] <= 0.1 ? '1' :
+      data['exclusive_factor'] < 0.5 ? '2' :
+        data['exclusive_factor'] <= 0.9 ? '3' : '4';
+  }
+
+  // Final Width and Height of current node
+  data['width'] += maxChildWidth;
+  data['Plans'] = plans;
+  nodeExplainTableData(data, ctx);
+
+  return data;
+}
+
+function parsePlanData(data, ctx) {
+  let retPlan = {};
+  if(data) {
+    if ('Plan' in data) {
+      let plan = parsePlan({
+        ...data['Plan'],
+        xpos: 0,
+        ypos: 0,
+      }, ctx);
+      retPlan['Plan'] = plan;
+      retPlan['xpos'] = 0;
+      retPlan['ypos'] = 0;
+      retPlan['width'] = plan.width + (xMargin * 2);
+      retPlan['height'] = plan.height + (yMargin * 4);
+    }
+
+    retPlan['Statistics'] = {
+      'JIT': [],
+      'Triggers': [],
+      'Summary': {},
+    };
+    if (data && 'JIT' in data) {
+      retPlan['Statistics']['JIT'] = retPlan['JIT'];
+    }
+    if (data && 'Triggers' in data) {
+      retPlan['Statistics']['Triggers'] = retPlan['JITriggersT'];
+    }
+    if(data) {
+      let summKeys = ['Planning Time', 'Execution Time'],
+        summary = {};
+
+      summKeys.forEach((key)=>{
+        if (key in data) {
+          summary[key] = data[key];
+        }
+      });
+
+      retPlan['Statistics']['Summary'] = summary;
+    }
+    if (data && 'Settings' in data) {
+      retPlan['Statistics']['Settings'] = data['Settings'];
+    }
+  }
+  return retPlan;
+}
+
+export default function Explain({plans=[]}) {
+  const classes = useStyles();
+  const [tabValue, setTabValue] = React.useState(0);
+
+  let ctx = React.useRef({
+    totalNodes: 0,
+    totalDownloadedNodes: 0,
+    isDownloaded: 0,
+    explainTable: {
+      rows: [],
+      statistics: {
+        tables: {},
+        nodes: {},
+      },
+    },
+    arrows: {},
+  });
+  let planData = React.useMemo(()=>(plans && parsePlanData(plans[0], ctx.current)), [plans]);
+
+  if(_.isEmpty(plans)) {
+    return <Box height="100%" display="flex" flexDirection="column">
+      <EmptyPanelMessage text={gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".')} />
+    </Box>;
+  }
+  return (
+    <Box height="100%" display="flex" flexDirection="column">
+      <Box>
+        <Tabs
+          value={tabValue}
+          onChange={(_e, selTabValue) => {
+            setTabValue(selTabValue);
+          }}
+          // indicatorColor="primary"
+          variant="scrollable"
+          scrollButtons="auto"
+          action={(ref)=>ref && ref.updateIndicator()}
+        >
+          <Tab label="Graphical" />
+          <Tab label="Analysis" />
+          <Tab label="Statistics" />
+        </Tabs>
+      </Box>
+      <TabPanel value={tabValue} index={0} classNameRoot={classes.tabPanel}>
+        <Graphical planData={planData} ctx={ctx.current}/>
+      </TabPanel>
+      <TabPanel value={tabValue} index={1} classNameRoot={classes.tabPanel}>
+        <Analysis explainTable={ctx.current.explainTable} />
+      </TabPanel>
+      <TabPanel value={tabValue} index={2} classNameRoot={classes.tabPanel}>
+        <ExplainStatistics explainTable={ctx.current.explainTable} />
+      </TabPanel>
+    </Box>
+  );
+}
+
+Explain.propTypes = {
+  plans: PropTypes.array,
+};
diff --git a/web/pgadmin/static/js/Explain/svg_download.js b/web/pgadmin/static/js/Explain/svg_download.js
new file mode 100644
index 000000000..5f70f8183
--- /dev/null
+++ b/web/pgadmin/static/js/Explain/svg_download.js
@@ -0,0 +1,54 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import getApiInstance from '../api_instance';
+
+function convertImageURLtoDataURI(api, image) {
+  return new Promise(function(resolve, reject) {
+    let href = image.getAttribute('href') || image.getAttributeNS('http://www.w3.org/1999/xlink', 'href');
+    api.get(href).then(({data})=>{
+      image.setAttribute('href', 'data:image/svg+xml;base64,'+window.btoa(data));
+      resolve();
+    }).catch(()=>{
+      reject();
+    });
+  });
+}
+
+export function downloadSvg(svg, svgName) {
+  let svgDiv = document.createElement('div');
+  svgDiv.innerHTML = svg;
+  svgDiv.style.visibility = 'hidden';
+  svgDiv.style.display = 'table';
+  svgDiv.style.position = 'absolute';
+  let svgElement = svgDiv.firstChild;
+  let api = getApiInstance();
+  if (!svgElement) { return; }
+
+  let images = svgElement.getElementsByTagName('image');
+  let image_promises = [];
+  if (images){
+    for (let image of images) {
+      if ((image.getAttribute('href') && image.getAttribute('href').indexOf('data:') === -1)
+      || (image.getAttribute('xlink:href') && image.getAttribute('xlink:href').indexOf('data:') === -1)) {
+        image_promises.push(convertImageURLtoDataURI(api, image));
+      }
+    }
+  }
+
+  Promise.all(image_promises).then(function() {
+    let blob = new Blob([svgElement.outerHTML], {type: 'image/svg+xml'});
+    let svgURL = (window.URL || window.webkitURL).createObjectURL(blob);
+    let newElement = document.createElement('a');
+    newElement.href = svgURL;
+    newElement.setAttribute('download', svgName);
+    document.body.appendChild(newElement);
+    newElement.click();
+    document.body.removeChild(newElement);
+  });
+}
diff --git a/web/pgadmin/static/js/SchemaView/FormView.jsx b/web/pgadmin/static/js/SchemaView/FormView.jsx
index 684390d7f..7f0d988ed 100644
--- a/web/pgadmin/static/js/SchemaView/FormView.jsx
+++ b/web/pgadmin/static/js/SchemaView/FormView.jsx
@@ -187,8 +187,8 @@ export default function FormView({
           if(_.isArray(dep)) {
             source = dep;
           }
-          if(field.depChange) {
-            depListener.addDepListener(source, accessPath.concat(field.id), field.depChange);
+          if(field.depChange || field.deferredDepChange) {
+            depListener.addDepListener(source, accessPath.concat(field.id), field.depChange, field.deferredDepChange);
           }
           if(field.depChange || field.deferredDepChange) {
             depListener.addDepListener(source, accessPath.concat(field.id), field.depChange, field.deferredDepChange);
diff --git a/web/pgadmin/static/js/SchemaView/index.jsx b/web/pgadmin/static/js/SchemaView/index.jsx
index ec6df7d17..3b85c67a0 100644
--- a/web/pgadmin/static/js/SchemaView/index.jsx
+++ b/web/pgadmin/static/js/SchemaView/index.jsx
@@ -429,7 +429,7 @@ function prepareData(val, createMode=false) {
 
 /* If its the dialog */
 function SchemaDialogView({
-  getInitData, viewHelperProps, schema={}, showFooter=true, isTabView=true, ...props}) {
+  getInitData, viewHelperProps, loadingText, schema={}, showFooter=true, isTabView=true, ...props}) {
   const classes = useDialogStyles();
   /* Some useful states */
   const [dirty, setDirty] = useState(false);
@@ -704,7 +704,7 @@ function SchemaDialogView({
       <DepListenerContext.Provider value={depListenerObj.current}>
         <Box className={classes.root}>
           <Box className={classes.form}>
-            <Loader message={loaderText}/>
+            <Loader message={loaderText || loadingText}/>
             <FormView value={sessData} viewHelperProps={viewHelperProps}
               schema={schema} accessPath={[]} dataDispatch={sessDispatchWithListener}
               hasSQLTab={props.hasSQL} getSQLValue={getSQLValue} firstEleRef={firstEleRef} isTabView={isTabView} className={props.formClassName} />
@@ -746,6 +746,7 @@ SchemaDialogView.propTypes = {
     }),
     inCatalog: PropTypes.bool,
   }).isRequired,
+  loadingText: PropTypes.string,
   schema: CustomPropTypes.schemaUI,
   onSave: PropTypes.func,
   onClose: PropTypes.func,
diff --git a/web/pgadmin/static/js/Theme/dark.js b/web/pgadmin/static/js/Theme/dark.js
index 617af6753..36ccb69ca 100644
--- a/web/pgadmin/static/js/Theme/dark.js
+++ b/web/pgadmin/static/js/Theme/dark.js
@@ -22,7 +22,7 @@ export default function(basicSettings) {
         disabledContrastText: '#fff',
         hoverMain: '#303030',
         hoverContrastText: '#fff',
-        hoverBorderColor: '#2e2e2e',
+        hoverBorderColor: '#151515',
       },
       primary: {
         main: '#234d6e',
@@ -73,7 +73,7 @@ export default function(basicSettings) {
       icon: {
         main: '#6b6b6b',
         contrastText: '#fff',
-        borderColor: '#2e2e2e',
+        borderColor: darken('#2e2e2e', 0.6),
         disabledMain: '#6b6b6b',
         disabledContrastText: '#fff',
         disabledBorderColor: '#2e2e2e',
@@ -86,6 +86,7 @@ export default function(basicSettings) {
       inputBorderColor: '#6b6b6b',
       inputDisabledBg: 'inherit',
       headerBg: '#424242',
+      activeBorder: '#d4d4d4',
       activeColor: '#d4d4d4',
       tableBg: '#424242',
       activeStepBg: '#234d6e',
@@ -93,6 +94,11 @@ export default function(basicSettings) {
       stepBg: '#FFFFFF',
       stepFg: '#000',
       toggleBtnBg: '#000',
+      editorToolbarBg: '#303030',
+      qtDatagridBg: '#2e2e2e',
+      qtDatagridSelectFg: '#d4d4d4',
+      cardHeaderBg: '#424242',
+      colorFg: '#FFFFFF',
       emptySpaceBg: '#212121',
     }
   });
diff --git a/web/pgadmin/static/js/Theme/high_contrast.js b/web/pgadmin/static/js/Theme/high_contrast.js
index c9d0af429..338f1ccb6 100644
--- a/web/pgadmin/static/js/Theme/high_contrast.js
+++ b/web/pgadmin/static/js/Theme/high_contrast.js
@@ -80,17 +80,23 @@ export default function(basicSettings) {
       }
     },
     otherVars: {
-      borderColor: '#4a4a4a',
+      borderColor: '#A6B7C8',
       inputBorderColor: '#6b6b6b',
       inputDisabledBg: '#1F2932',
       headerBg: '#010B15',
-      activeColor: '#d4d4d4',
+      activeBorder: '#fff',
+      activeColor: '#fff',
       tableBg: '#010B15',
       activeStepBg: '#84D6FF',
       activeStepFg: '#010b15',
       stepBg: '#FFFFFF',
       stepFg: '#000',
       toggleBtnBg: '#6B6B6B',
+      editorToolbarBg: '#010B15',
+      qtDatagridBg: '#010B15',
+      qtDatagridSelectFg: '#010B15',
+      cardHeaderBg: '#062F57',
+      colorFg: '#FFFFFF',
       emptySpaceBg: '#010B15',
     }
   });
diff --git a/web/pgadmin/static/js/Theme/index.jsx b/web/pgadmin/static/js/Theme/index.jsx
index d2a070f15..9124743ec 100644
--- a/web/pgadmin/static/js/Theme/index.jsx
+++ b/web/pgadmin/static/js/Theme/index.jsx
@@ -81,7 +81,7 @@ basicSettings = createMuiTheme(basicSettings, {
           height: '28px',
           fontSize: '0.875rem',
           '& .MuiSvgIcon-root': {
-            height: '0.8em',
+            height: '1.2rem',
           }
         },
       },
@@ -167,13 +167,17 @@ basicSettings = createMuiTheme(basicSettings, {
     MuiDialog: {
       paper: {
         margin: 0,
+      },
+      scrollPaper: {
+        alignItems: 'flex-start',
+        margin: '5% auto',
       }
     },
     MuiTooltip: {
       popper: {
         top: 0,
         zIndex: 9999,
-      }
+      },
     },
     MuiMenu: {
       list: {
@@ -231,6 +235,9 @@ basicSettings = createMuiTheme(basicSettings, {
     MuiListItem: {
       disableGutters: true,
     },
+    MuiTooltip: {
+      arrow: true,
+    }
   },
 });
 
@@ -460,6 +467,7 @@ function getFinalTheme(baseTheme) {
       MuiCardHeader: {
         root: {
           padding: '4px 8px',
+          backgroundColor: baseTheme.otherVars.cardHeaderBg,
           ...mixins.panelBorder.bottom,
         }
       },
@@ -487,11 +495,22 @@ function getFinalTheme(baseTheme) {
           '&$selected': {
             backgroundColor: baseTheme.palette.primary.light,
             borderColor: baseTheme.palette.primary.main,
+            color: basicSettings.palette.getContrastText(baseTheme.palette.primary.light),
             '&:hover': {
               backgroundColor: baseTheme.palette.primary.light,
             }
           },
         }
+      },
+      MuiTooltip: {
+        tooltip: {
+          fontSize: '0.7rem',
+          color: baseTheme.palette.background.default,
+          backgroundColor: baseTheme.palette.text.primary,
+        },
+        arrow: {
+          color: baseTheme.palette.text.primary,
+        }
       }
     }
   }, baseTheme);
diff --git a/web/pgadmin/static/js/Theme/standard.js b/web/pgadmin/static/js/Theme/standard.js
index 998429f00..1efe55300 100644
--- a/web/pgadmin/static/js/Theme/standard.js
+++ b/web/pgadmin/static/js/Theme/standard.js
@@ -101,7 +101,9 @@ export default function(basicSettings) {
       stepFg: '#000',
       toggleBtnBg: '#000',
       editorToolbarBg: '#ebeef3',
-      datagridBg: '#fff',
+      qtDatagridBg: '#fff',
+      qtDatagridSelectFg: '#222',
+      cardHeaderBg: '#fff',
       emptySpaceBg: '#ebeef3',
     }
   });
diff --git a/web/pgadmin/static/js/api_instance.js b/web/pgadmin/static/js/api_instance.js
index c6ce38cb1..4a4c65467 100644
--- a/web/pgadmin/static/js/api_instance.js
+++ b/web/pgadmin/static/js/api_instance.js
@@ -29,7 +29,7 @@ export function parseApiError(error) {
     // The request was made and the server responded with a status code
     // that falls out of the range of 2xx
     if(error.response.headers['content-type'] == 'application/json') {
-      return `INTERNAL SERVER ERROR: ${error.response.data.errormsg}`;
+      return error.response.data.errormsg;
     } else {
       return error.response.statusText;
     }
diff --git a/web/pgadmin/static/js/clipboard.js b/web/pgadmin/static/js/clipboard.js
new file mode 100644
index 000000000..747c4444e
--- /dev/null
+++ b/web/pgadmin/static/js/clipboard.js
@@ -0,0 +1,13 @@
+export function copyToClipboard(text) {
+  try {
+    navigator.clipboard.writeText(text);
+  } catch {
+    /* Suppress error */
+    console.error('Does not have clipboard acccess');
+  }
+  localStorage.setItem('clipboard', text);
+}
+
+export function getFromClipboard() {
+  return localStorage.getItem('clipboard');
+}
diff --git a/web/pgadmin/static/js/components/Buttons.jsx b/web/pgadmin/static/js/components/Buttons.jsx
index 3eaf7c155..ae20f1045 100644
--- a/web/pgadmin/static/js/components/Buttons.jsx
+++ b/web/pgadmin/static/js/components/Buttons.jsx
@@ -42,6 +42,7 @@ const useStyles = makeStyles((theme)=>({
     }
   },
   iconButton: {
+    minWidth: 0,
     padding: '3px 6px',
     '&.MuiButton-sizeSmall, &.MuiButton-outlinedSizeSmall, &.MuiButton-containedSizeSmall': {
       padding: '1px 4px',
@@ -59,6 +60,7 @@ const useStyles = makeStyles((theme)=>({
     '&:hover': {
       backgroundColor: theme.custom.icon.hoverMain,
       color: theme.custom.icon.hoverContrastText,
+      borderColor: theme.custom.icon.borderColor,
     },
   },
   splitButton: {
@@ -72,6 +74,7 @@ const useStyles = makeStyles((theme)=>({
   xsButton: {
     padding: '2px 1px',
     height: '24px',
+    minWidth: '24px',
     '& .MuiSvgIcon-root': {
       height: '0.8em',
     }
@@ -127,27 +130,29 @@ DefaultButton.propTypes = {
 
 
 /* pgAdmin Icon button, takes Icon component as input */
-export const PgIconButton = forwardRef(({icon, title, shortcut, accessKey, className, splitButton, style, color, ...props}, ref)=>{
+export const PgIconButton = forwardRef(({icon, title, shortcut, className, splitButton, style, color, accesskey, ...props}, ref)=>{
   const classes = useStyles();
 
   let shortcutTitle = null;
-  if(accessKey || shortcut) {
-    shortcutTitle = <ShortcutTitle title={title} accessKey={accessKey} shortcut={shortcut}/>;
+  if(accesskey || shortcut) {
+    shortcutTitle = <ShortcutTitle title={title} accesskey={accesskey} shortcut={shortcut}/>;
   }
 
   /* Tooltip does not work for disabled items */
   if(props.disabled) {
     if(color == 'primary') {
       return (
-        <PrimaryButton ref={ref} style={{minWidth: 0, ...style}}
-          className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)} {...props}>
+        <PrimaryButton ref={ref} style={style}
+          className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
+          accessKey={accesskey} {...props}>
           {icon}
         </PrimaryButton>
       );
     } else {
       return (
-        <DefaultButton ref={ref} style={{minWidth: 0, ...style}}
-          className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)} {...props}>
+        <DefaultButton ref={ref} style={style}
+          className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
+          accessKey={accesskey} {...props}>
           {icon}
         </DefaultButton>
       );
@@ -156,8 +161,9 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, accessKey, class
     if(color == 'primary') {
       return (
         <Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
-          <PrimaryButton ref={ref} style={{minWidth: 0, ...style}}
-            className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)} {...props}>
+          <PrimaryButton ref={ref} style={style}
+            className={clsx(classes.iconButton, (splitButton ? classes.splitButton : ''), className)}
+            accessKey={accesskey} {...props}>
             {icon}
           </PrimaryButton>
         </Tooltip>
@@ -165,8 +171,9 @@ export const PgIconButton = forwardRef(({icon, title, shortcut, accessKey, class
     } else {
       return (
         <Tooltip title={shortcutTitle || title || ''} aria-label={title || ''}>
-          <DefaultButton ref={ref} style={{minWidth: 0, ...style}}
-            className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)} {...props}>
+          <DefaultButton ref={ref} style={style}
+            className={clsx(classes.iconButton, classes.iconButtonDefault, (splitButton ? classes.splitButton : ''), className)}
+            accessKey={accesskey} {...props}>
             {icon}
           </DefaultButton>
         </Tooltip>
@@ -179,7 +186,7 @@ PgIconButton.propTypes = {
   icon: CustomPropTypes.children,
   title: PropTypes.string.isRequired,
   shortcut: CustomPropTypes.shortcut,
-  accessKey: PropTypes.string,
+  accesskey: PropTypes.string,
   className: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
   style: PropTypes.object,
   color: PropTypes.oneOf(['primary', 'default', undefined]),
diff --git a/web/pgadmin/static/js/components/CodeMirror.jsx b/web/pgadmin/static/js/components/CodeMirror.jsx
index 7e3c0cdda..de891d62b 100644
--- a/web/pgadmin/static/js/components/CodeMirror.jsx
+++ b/web/pgadmin/static/js/components/CodeMirror.jsx
@@ -8,11 +8,11 @@
 //////////////////////////////////////////////////////////////
 
 import React, { useEffect, useMemo, useRef, useState } from 'react';
-import {default as OrigCodeMirror} from 'bundled_codemirror';
+import OrigCodeMirror from 'bundled_codemirror';
 import {useOnScreen} from 'sources/custom_hooks';
 import PropTypes from 'prop-types';
 import CustomPropTypes from '../custom_prop_types';
-import pgAdmin from 'sources/pgadmin';
+import pgWindow from 'sources/window';
 import gettext from 'sources/gettext';
 import { Box, InputAdornment, makeStyles } from '@material-ui/core';
 import clsx from 'clsx';
@@ -31,6 +31,11 @@ const useStyles = makeStyles((theme)=>({
   root: {
     position: 'relative',
   },
+  hideCursor: {
+    '& .CodeMirror-cursors': {
+      display: 'none'
+    }
+  },
   findDialog: {
     position: 'absolute',
     zIndex: 99,
@@ -70,31 +75,40 @@ function parseQuery(query, useRegex=false, matchCase=false) {
   return query;
 }
 
+function getRegexFinder(query) {
+  return (stream) => {
+    query.lastIndex = stream.pos;
+    var match = query.exec(stream.string);
+    if (match && match.index == stream.pos) {
+      stream.pos += match[0].length || 1;
+      return 'searching';
+    } else if (match) {
+      stream.pos = match.index;
+    } else {
+      stream.skipToEnd();
+    }
+  };
+}
+
+
+function getPlainStringFinder(query, matchCase) {
+  return (stream) => {
+    var matchIndex = (matchCase ? stream.string :  stream.string.toLowerCase()).indexOf(query, stream.pos);
+    if(matchIndex == -1) {
+      stream.skipToEnd();
+    } else if(matchIndex == stream.pos) {
+      stream.pos += query.length;
+      return 'searching';
+    } else {
+      stream.pos = matchIndex;
+    }
+  };
+}
+
 function searchOverlay(query, matchCase) {
   return {
     token: typeof query == 'string' ?
-      (stream) =>{
-        var matchIndex = (matchCase ? stream.string :  stream.string.toLowerCase()).indexOf(query, stream.pos);
-        if(matchIndex == -1) {
-          stream.skipToEnd();
-        } else if(matchIndex == stream.pos) {
-          stream.pos += query.length;
-          return 'searching';
-        } else {
-          stream.pos = matchIndex;
-        }
-      } : (stream) => {
-        query.lastIndex = stream.pos;
-        var match = query.exec(stream.string);
-        if (match && match.index == stream.pos) {
-          stream.pos += match[0].length || 1;
-          return 'searching';
-        } else if (match) {
-          stream.pos = match.index;
-        } else {
-          stream.skipToEnd();
-        }
-      }
+      getPlainStringFinder(query, matchCase) : getRegexFinder(query)
   };
 }
 
@@ -260,28 +274,96 @@ FindDialog.propTypes = {
   onClose: PropTypes.func,
 };
 
+function handleDrop(editor, e) {
+  var dropDetails = null;
+  try {
+    dropDetails = JSON.parse(e.dataTransfer.getData('text'));
+
+    /* Stop firefox from redirecting */
+
+    if(e.preventDefault) {
+      e.preventDefault();
+    }
+    if (e.stopPropagation) {
+      e.stopPropagation();
+    }
+  } catch(error) {
+    /* if parsing fails, it must be the drag internal of codemirror text */
+    return;
+  }
+
+  var cursor = editor.coordsChar({
+    left: e.x,
+    top: e.y,
+  });
+  editor.replaceRange(dropDetails.text, cursor);
+  editor.focus();
+  editor.setSelection({
+    ...cursor,
+    ch: cursor.ch + dropDetails.cur.from,
+  },{
+    ...cursor,
+    ch: cursor.ch +dropDetails.cur.to,
+  });
+}
+
+function calcFontSize(fontSize) {
+  if(fontSize) {
+    fontSize = parseFloat((Math.round(parseFloat(fontSize + 'e+2')) + 'e-2'));
+    let rounded = Number(fontSize);
+    if(rounded > 0) {
+      return rounded + 'em';
+    }
+  }
+  return '1em';
+}
+
 /* React wrapper for CodeMirror */
-export default function CodeMirror({currEditor, name, value, options, events, readonly, disabled, className}) {
+export default function CodeMirror({currEditor, name, value, options, events, readonly, disabled, className, autocomplete=false}) {
   const taRef = useRef();
   const editor = useRef();
   const cmWrapper = useRef();
   const isVisibleTrack = useRef();
   const classes = useStyles();
   const [[showFind, isReplace], setShowFind] = useState([false, false]);
-  const defaultOptions = {
-    tabindex: '0',
-    lineNumbers: true,
-    styleSelectedText: true,
-    mode: 'text/x-pgsql',
-    foldOptions: {
-      widget: '\u2026',
-    },
-    foldGutter: true,
-    gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
-    extraKeys: pgAdmin.Browser.editor_shortcut_keys,
-    dragDrop: false,
-    screenReaderLabel: gettext('SQL editor'),
-  };
+  const defaultOptions = useMemo(()=>{
+    let goLeftKey = 'Ctrl-Alt-Left',
+      goRightKey = 'Ctrl-Alt-Right',
+      commentKey = 'Ctrl-/';
+    if(isMac()) {
+      goLeftKey = 'Cmd-Alt-Left';
+      goRightKey = 'Cmd-Alt-Right';
+      commentKey = 'Cmd-/';
+    }
+    return {
+      tabindex: '0',
+      lineNumbers: true,
+      styleSelectedText: true,
+      mode: 'text/x-pgsql',
+      foldOptions: {
+        widget: '\u2026',
+      },
+      foldGutter: true,
+      gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
+      extraKeys: {
+        // Autocomplete sql command
+        ...(autocomplete ? {
+          'Ctrl-Space': 'autocomplete',
+        }: {}),
+        'Alt-Up': 'goLineUp',
+        'Alt-Down': 'goLineDown',
+        // Move word by word left/right
+        [goLeftKey]: 'goGroupLeft',
+        [goRightKey]: 'goGroupRight',
+        // Allow user to delete Tab(s)
+        'Shift-Tab': 'indentLess',
+        //comment
+        [commentKey]: 'toggleComment',
+      },
+      dragDrop: true,
+      screenReaderLabel: gettext('SQL editor'),
+    };
+  });
 
   useEffect(()=>{
     const finalOptions = {...defaultOptions, ...options};
@@ -317,29 +399,61 @@ export default function CodeMirror({currEditor, name, value, options, events, re
             setShowFind([false, false]);
             setShowFind([true, true]);
           }
-        }
+        },
+        'Cmd-G': false,
       });
     }
 
     Object.keys(events||{}).forEach((eventName)=>{
       editor.current.on(eventName, events[eventName]);
     });
-
+    editor.current.on('drop', handleDrop);
+    initPreferences();
     return ()=>{
       editor.current?.toTextArea();
     };
   }, []);
 
+  const initPreferences = ()=>{
+    reflectPreferences();
+    pgWindow?.pgAdmin?.Browser?.onPreferencesChange('sqleditor', function() {
+      reflectPreferences();
+    });
+  };
+
+  const reflectPreferences = ()=>{
+    let pref = pgWindow?.pgAdmin?.Browser?.get_preferences_for_module('sqleditor') || {};
+    let wrapEle = editor?.current.getWrapperElement();
+    wrapEle && (wrapEle.style.fontSize = calcFontSize(pref.sql_font_size));
+
+    if(pref.plain_editor_mode) {
+      editor?.current.setOption('mode', 'text/plain');
+      /* Although not required, setting explicitly as codemirror will remove code folding only on next edit */
+      editor?.current.setOption('foldGutter', false);
+    } else {
+      editor?.current.setOption('mode', 'text/x-pgsql');
+      editor?.current.setOption('foldGutter', pref.code_folding);
+    }
+
+    editor?.current.setOption('indentWithTabs', pref.indent_with_tabs);
+    editor?.current.setOption('indentUnit', pref.tab_size);
+    editor?.current.setOption('tabSize', pref.tab_size);
+    editor?.current.setOption('lineWrapping', pref.wrap_code);
+    editor?.current.setOption('autoCloseBrackets', pref.insert_pair_brackets);
+    editor?.current.setOption('matchBrackets', pref.brace_matching);
+    editor?.current.refresh();
+  };
+
   useEffect(()=>{
     if(editor.current) {
       if(disabled) {
-        cmWrapper.current.classList.add('cm_disabled');
-        editor.current.setOption('readOnly', 'nocursor');
+        editor.current.setOption('readOnly', true);
+        cmWrapper.current.classList.add(classes.hideCursor);
       } else if(readonly) {
-        cmWrapper.current.classList.add('cm_disabled');
         editor.current.setOption('readOnly', true);
         editor.current.addKeyMap({'Tab': false});
         editor.current.addKeyMap({'Shift-Tab': false});
+        cmWrapper.current.classList.add(classes.hideCursor);
       } else {
         cmWrapper.current.classList.remove('cm_disabled');
         editor.current.setOption('readOnly', false);
@@ -392,4 +506,5 @@ CodeMirror.propTypes = {
   readonly: PropTypes.bool,
   disabled: PropTypes.bool,
   className: CustomPropTypes.className,
+  autocomplete: PropTypes.bool,
 };
diff --git a/web/pgadmin/static/js/components/ExternalIcon.jsx b/web/pgadmin/static/js/components/ExternalIcon.jsx
index 2c6433764..3fa91b895 100644
--- a/web/pgadmin/static/js/components/ExternalIcon.jsx
+++ b/web/pgadmin/static/js/components/ExternalIcon.jsx
@@ -22,17 +22,16 @@ ExternalIcon.propTypes = {
   Icon: PropTypes.elementType.isRequired,
 };
 
-export const QueryToolIcon = ()=><ExternalIcon Icon={QueryToolSvg} style={{height: '0.7em'}} />;
-export const SaveDataIcon = ()=><ExternalIcon Icon={SaveDataSvg} style={{height: '0.7em'}} />;
+export const QueryToolIcon = ()=><ExternalIcon Icon={QueryToolSvg} style={{height: '1rem'}} />;
+export const SaveDataIcon = ()=><ExternalIcon Icon={SaveDataSvg} style={{height: '1rem'}} />;
 export const PasteIcon = ()=><ExternalIcon Icon={PasteSvg} />;
 export const FilterIcon = ()=><ExternalIcon Icon={FilterSvg} />;
 export const CommitIcon = ()=><ExternalIcon Icon={CommitSvg} />;
 export const RollbackIcon = ()=><ExternalIcon Icon={RollbackSvg} />;
 export const ClearIcon = ()=><ExternalIcon Icon={ClearSvg} />;
-export const ConnectedIcon = ()=><ExternalIcon Icon={ConnectedSvg} style={{height: '0.7em'}} />;
-export const DisonnectedIcon = ()=><ExternalIcon Icon={DisconnectedSvg} style={{height: '0.7em'}} />;
+export const ConnectedIcon = ()=><ExternalIcon Icon={ConnectedSvg} style={{height: '1rem'}} />;
+export const DisonnectedIcon = ()=><ExternalIcon Icon={DisconnectedSvg} style={{height: '1rem'}} />;
 export const RegexIcon = ()=><ExternalIcon Icon={RegexSvg} />;
 export const FormatCaseIcon = ()=><ExternalIcon Icon={FormatCaseSvg} />;
-export const ExpandDialogIcon = ()=><ExternalIcon Icon={Expand} style={{height: '1.2em'}}  />;
-export const MinimizeDialogIcon = ()=><ExternalIcon Icon={Collapse} style={{height: '1.4em'}} />;
-
+export const ExpandDialogIcon = ()=><ExternalIcon Icon={Expand} style={{height: '1.2rem'}}  />;
+export const MinimizeDialogIcon = ()=><ExternalIcon Icon={Collapse} style={{height: '1.4rem'}} />;
diff --git a/web/pgadmin/static/js/components/FormComponents.jsx b/web/pgadmin/static/js/components/FormComponents.jsx
index 1e425215c..cec2ae439 100644
--- a/web/pgadmin/static/js/components/FormComponents.jsx
+++ b/web/pgadmin/static/js/components/FormComponents.jsx
@@ -207,6 +207,7 @@ FormInputSQL.propTypes = {
   helpMessage: PropTypes.string,
   testcid: PropTypes.string,
   value: PropTypes.string,
+  controlProps: PropTypes.object,
   noLabel: PropTypes.bool,
   change: PropTypes.func,
 };
@@ -570,7 +571,7 @@ export function InputRadio({ helpid, value, onChange, controlProps, readonly, ..
           disableRipple
           {...props}
         />
-          
+
       }
       label={controlProps.label}
       className={(readonly || props.disabled) ? classes.readOnlySwitch : null}
@@ -821,6 +822,7 @@ export const InputSelect = forwardRef(({
   const [[finalOptions, isLoading], setFinalOptions] = useState([[], true]);
   const theme = useTheme();
 
+
   /* React will always take options var as changed parameter. So,
   We cannot run the below effect with options dependency as it will keep on
   loading the options. optionsReloadBasis is helpful to avoid repeated
@@ -1174,20 +1176,21 @@ const useStylesFormFooter = makeStyles((theme) => ({
 }));
 
 /* The form footer used mostly for showing error */
-export function FormFooterMessage(props) {
+export function FormFooterMessage({style, ...props}) {
   const classes = useStylesFormFooter();
 
   if (!props.message) {
     return <></>;
   }
   return (
-    <Box className={classes.root}>
+    <Box className={classes.root} style={style}>
       <NotifierMessage {...props}></NotifierMessage>
     </Box>
   );
 }
 
 FormFooterMessage.propTypes = {
+  style: PropTypes.object,
   message: PropTypes.string,
 };
 
diff --git a/web/pgadmin/static/js/components/JsonEditor.jsx b/web/pgadmin/static/js/components/JsonEditor.jsx
index ef7b910d7..3dae7df05 100644
--- a/web/pgadmin/static/js/components/JsonEditor.jsx
+++ b/web/pgadmin/static/js/components/JsonEditor.jsx
@@ -13,7 +13,7 @@ import PropTypes from 'prop-types';
 import CustomPropTypes from '../custom_prop_types';
 
 /* React wrapper for JsonEditor */
-export default function JsonEditor({currEditor, value, options, className}) {
+export default function JsonEditor({getEditor, value, options, className}) {
   const eleRef = useRef();
   const editor = useRef();
   const defaultOptions = {
@@ -34,9 +34,10 @@ export default function JsonEditor({currEditor, value, options, className}) {
       }
     });
     editor.current.setText(value);
-    currEditor && currEditor(editor.current);
+    getEditor?.(editor.current);
     editor.current.focus();
-    return ()=>editor.current?.destroy();
+    /* Required by json editor */
+    eleRef.current.style.height = eleRef.current.offsetHeight + 'px';
   }, []);
 
   useMemo(() => {
@@ -53,7 +54,7 @@ export default function JsonEditor({currEditor, value, options, className}) {
 }
 
 JsonEditor.propTypes = {
-  currEditor: PropTypes.func,
+  getEditor: PropTypes.func,
   value: PropTypes.string,
   options: PropTypes.object,
   className: CustomPropTypes.className,
diff --git a/web/pgadmin/static/js/components/Menu.jsx b/web/pgadmin/static/js/components/Menu.jsx
index 2920d678d..02b3e0b3c 100644
--- a/web/pgadmin/static/js/components/Menu.jsx
+++ b/web/pgadmin/static/js/components/Menu.jsx
@@ -18,6 +18,9 @@ const useStyles = makeStyles((theme)=>({
     '& .szh-menu': {
       padding: '4px 0px',
       zIndex: 1000,
+      backgroundColor: theme.palette.background.default,
+      color: theme.palette.text.primary,
+      border: `1px solid ${theme.otherVars.borderColor}`
     },
     '& .szh-menu__divider': {
       margin: 0,
diff --git a/web/pgadmin/static/js/components/ShortcutTitle.jsx b/web/pgadmin/static/js/components/ShortcutTitle.jsx
index 6ef1a564e..d822f3d5e 100644
--- a/web/pgadmin/static/js/components/ShortcutTitle.jsx
+++ b/web/pgadmin/static/js/components/ShortcutTitle.jsx
@@ -4,8 +4,13 @@ import PropTypes from 'prop-types';
 import { isMac } from '../keyboard_shortcuts';
 import _ from 'lodash';
 import CustomPropTypes from '../custom_prop_types';
+import gettext from 'sources/gettext';
 
 const useStyles = makeStyles((theme)=>({
+  shortcutTitle: {
+    width: '100%',
+    textAlign: 'center',
+  },
   shortcut: {
     justifyContent: 'center',
     marginTop: '0.125rem',
@@ -19,10 +24,43 @@ const useStyles = makeStyles((theme)=>({
   },
 }));
 
+export function getBrowserAccesskey() {
+  /* Ref: https://github.com/tillsanders/access-key-label-polyfill/ */
+  let ua = window.navigator.userAgent;
+  // macOS
+  if (ua.match(/macintosh/i)) {
+    // Firefox
+    if (ua.match(/firefox/i)) {
+      const firefoxVersion = ua.match(/firefox[\s/](\d+)/i);
+      // Firefox < v14
+      if (firefoxVersion[1] && parseInt(firefoxVersion[1], 10) < 14) {
+        return ['Ctrl'];
+      }
+    }
+    return ['Option', 'Ctrl'];
+  }
+
+  // Internet Explorer / Edge
+  if (ua.match(/msie|trident/i) || ua.match(/\sedg/i)) {
+    return ['Alt'];
+  }
+
+  // iOS / iPadOS
+  if (ua.match(/(ipod|iphone|ipad)/i)) {
+    // accesskeyLabel is supported > v14, but we're not checking for versions here, since we use
+    // feature support detection
+    return ['Option', 'Ctrl'];
+  }
+
+  // Fallback
+  // Note: Apparently, Chrome for Android is not even supporting accesskey, so be prepared.
+  return [gettext('Accesskey')];
+}
+
 export function shortcutToString(shortcut, accesskey=null, asArray=false) {
   let keys = [];
   if(accesskey) {
-    keys.push('Accesskey');
+    keys = getBrowserAccesskey();
     keys.push(_.capitalize(accesskey?.toUpperCase()));
   } else if(shortcut) {
     shortcut.alt && keys.push((isMac() ? 'Option' : 'Alt'));
@@ -41,12 +79,12 @@ export function shortcutToString(shortcut, accesskey=null, asArray=false) {
 }
 
 /* The tooltip content to show shortcut details */
-export default function ShortcutTitle({title, shortcut, accessKey}) {
+export default function ShortcutTitle({title, shortcut, accesskey}) {
   const classes = useStyles();
-  let keys = shortcutToString(shortcut, accessKey, true);
+  let keys = shortcutToString(shortcut, accesskey, true);
   return (
     <>
-      <div>{title}</div>
+      <div className={classes.shortcutTitle}>{title}</div>
       <div className={classes.shortcut}>
         {keys.map((key, i)=>{
           return <div key={i} className={classes.key}>{key}</div>;
@@ -59,5 +97,5 @@ export default function ShortcutTitle({title, shortcut, accessKey}) {
 ShortcutTitle.propTypes = {
   title: PropTypes.string,
   shortcut: CustomPropTypes.shortcut,
-  accessKey: PropTypes.string,
+  accesskey: PropTypes.string,
 };
diff --git a/web/pgadmin/static/js/custom_hooks.js b/web/pgadmin/static/js/custom_hooks.js
index b6370bbbe..a38088851 100644
--- a/web/pgadmin/static/js/custom_hooks.js
+++ b/web/pgadmin/static/js/custom_hooks.js
@@ -1,5 +1,6 @@
 import {useRef, useEffect, useState, useCallback} from 'react';
-export { useStopwatch } from 'react-timer-hook';
+import moment from 'moment';
+// export { useStopwatch } from 'react-timer-hook';
 
 /* React hook for setInterval */
 export function useInterval(callback, delay) {
@@ -78,6 +79,50 @@ export function useIsMounted() {
   return useCallback(() => ref.current, []);
 }
 
+export function useStopwatch() {
+  const prevTime = useRef(new Date());
+  const [totalMsec, setTotalMsec] = useState(0);
+  const [isRunning, setIsRunning] = useState(false);
+
+  useInterval(() => {
+    setTotalMsec(moment(new Date()).diff(prevTime.current));
+  }, isRunning ? 100 : -1);
+
+  function start(startTime) {
+    prevTime.current = startTime || new Date();
+    setIsRunning(true);
+  }
+
+  const pause = (endTime)=>{
+    setIsRunning(false);
+    setTotalMsec(moment(endTime || new Date()).diff(prevTime.current));
+  };
+
+  function reset() {
+    setTotalMsec(0);
+  }
+
+  let msec = totalMsec;
+  /* Extract seconds from millisecs */
+  let seconds = parseInt(msec/1000);
+  msec = msec%1000;
+
+  /* Extract mins from seconds */
+  let minutes = parseInt(seconds/60);
+  seconds = seconds%60;
+
+  /* Extract hrs from mins */
+  let hours = parseInt(minutes/60);
+  minutes = minutes%60;
+
+  return {
+    hours: hours,
+    minutes: minutes,
+    seconds: seconds,
+    msec: msec,
+    start, pause, reset, isRunning,
+  };
+}
 
 /*
   shortcuts = [
@@ -105,27 +150,30 @@ export function useKeyboardShortcuts(shortcuts, eleRef) {
   const matchFound = (shortcut, e)=>{
     if(!shortcut) return false;
     let keyCode = e.which || e.keyCode;
-    return shortcut.alt == e.altKey &&
-      shortcut.shift == e.shiftKey &&
-      shortcut.control == e.ctrlKey &&
+    return Boolean(shortcut.alt) == e.altKey &&
+      Boolean(shortcut.shift) == e.shiftKey &&
+      Boolean(shortcut.control) == e.ctrlKey &&
       shortcut.key.key_code == keyCode;
   };
   useEffect(()=>{
     let ele = eleRef.current ?? document;
-    const keyupCallback = (e)=>{
-      for(let i=0; i<(shortcutsRef.current??[]).length; i++){
-        let {shortcut, options} = shortcutsRef.current[i];
-        if(matchFound(shortcut, e)) {
-          if(options.callback && (options.enabled ?? true)) {
-            options.callback(e);
-          }
-          break;
+    const keydownCallback = (e)=>{
+      Promise.resolve(0).then(()=>{
+        let allListeners = _.filter(shortcutsRef.current, (s)=>matchFound(s.shortcut, e));
+        for(const {options} of allListeners) {
+          Promise.resolve(0).then(()=>{
+            if(options.callback && (options.enabled ?? true)) {
+              e.preventDefault();
+              e.stopPropagation();
+              options.callback(e);
+            }
+          });
         }
-      }
+      });
     };
-    ele.addEventListener('keyup', keyupCallback);
+    ele.addEventListener('keydown', keydownCallback);
     return ()=>{
-      ele.removeEventListener('keyup', keyupCallback);
+      ele.removeEventListener('keydown', keydownCallback);
     };
   }, [eleRef.current]);
 
diff --git a/web/pgadmin/static/js/custom_prop_types.js b/web/pgadmin/static/js/custom_prop_types.js
index 6ba21a6d3..1a2d4c935 100644
--- a/web/pgadmin/static/js/custom_prop_types.js
+++ b/web/pgadmin/static/js/custom_prop_types.js
@@ -31,9 +31,9 @@ const CustomPropTypes = {
   ]),
 
   shortcut: PropTypes.shape({
-    alt: PropTypes.bool,
-    control: PropTypes.bool,
-    shift: PropTypes.bool,
+    alt: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
+    control: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
+    shift: PropTypes.oneOfType([PropTypes.bool, PropTypes.number]),
     key: PropTypes.shape({
       char: PropTypes.string,
     }),
diff --git a/web/pgadmin/static/js/helpers/EventBus.js b/web/pgadmin/static/js/helpers/EventBus.js
index a9d077820..9a6906429 100644
--- a/web/pgadmin/static/js/helpers/EventBus.js
+++ b/web/pgadmin/static/js/helpers/EventBus.js
@@ -1,4 +1,3 @@
-import React from 'react';
 import _ from 'lodash';
 
 export default class EventBus {
@@ -6,11 +5,12 @@ export default class EventBus {
     this._eventListeners = [];
   }
 
-  registerListener(event, callback) {
+  registerListener(event, callback, once=false) {
     this._eventListeners = this._eventListeners || [];
     this._eventListeners.push({
       event: event,
       callback: callback,
+      fired: once ? 'pending' : 'ignore',
     });
   }
 
@@ -28,17 +28,19 @@ export default class EventBus {
   }
 
   fireEvent(event, ...args) {
+    let self = this;
     Promise.resolve(0).then(()=>{
       let allListeners = _.filter(this._eventListeners, (e)=>e.event==event);
       if(allListeners) {
         for(const listener of allListeners) {
           Promise.resolve(0).then(()=>{
             listener.callback(...args);
+            if(listener.fired == 'pending') {
+              self.deregisterListener(event, listener.callback);
+            }
           });
         }
       }
     });
   }
 }
-
-export const EventBusContext = React.createContext(new EventBus());
diff --git a/web/pgadmin/static/js/helpers/Layout.jsx b/web/pgadmin/static/js/helpers/Layout.jsx
index 5e3cbe9b0..5074024cc 100644
--- a/web/pgadmin/static/js/helpers/Layout.jsx
+++ b/web/pgadmin/static/js/helpers/Layout.jsx
@@ -1,8 +1,14 @@
-import React from 'react';
+import React, { useRef, useMemo } from 'react';
 import DockLayout from 'rc-dock';
 import { makeStyles } from '@material-ui/styles';
 import PropTypes from 'prop-types';
-import CustomPropTypes from '../custom_prop_types';
+import EventBus from './EventBus';
+import getApiInstance from '../api_instance';
+import url_for from 'sources/url_for';
+import { PgIconButton } from '../components/Buttons';
+import CloseIcon from '@material-ui/icons/CloseRounded';
+import gettext from 'sources/gettext';
+import {ExpandDialogIcon, MinimizeDialogIcon } from '../components/ExternalIcon';
 
 
 const useStyles = makeStyles((theme)=>({
@@ -26,10 +32,21 @@ const useStyles = makeStyles((theme)=>({
       paddingLeft: 0,
       backgroundColor: theme.palette.background.default,
       ...theme.mixins.panelBorder.bottom,
+      '& .dock-nav-wrap': {
+        cursor: 'move',
+      }
     },
     '& .dock-panel': {
       border: 'none',
+      '&.dragging': {
+        opacity: 0.6,
+        pointerEvents: 'visible',
+      },
+      '& .dock':  {
+        borderRadius: 'inherit',
+      },
       '&.dock-style-dialogs': {
+        borderRadius: theme.shape.borderRadius,
         '&.dock-panel.dragging': {
           opacity: 1,
         },
@@ -46,6 +63,10 @@ const useStyles = makeStyles((theme)=>({
             color: theme.palette.text.primary,
           }
         },
+      },
+      '& .dock-tabpane': {
+        backgroundColor: theme.palette.background.default,
+        color: theme.palette.text.primary,
       }
     },
     '& .dock-tab': {
@@ -54,10 +75,33 @@ const useStyles = makeStyles((theme)=>({
       marginRight: 0,
       background: 'unset',
       fontWeight: 'unset',
+      color: theme.palette.text.primary,
       '&::hover': {
         color: 'unset',
+      },
+      '& > div': {
+        padding: '4px 10px',
+      },
+      '& .drag-initiator': {
+        display: 'flex',
+        '& .dock-tab-close-btn': {
+          color: theme.palette.text.primary,
+          position: 'unset',
+          marginLeft: '8px',
+          fontSize: '18px',
+          transition: 'none',
+          '&::before': {
+            content: '"\\00d7"',
+            position: 'relative',
+            top: '-5px',
+          }
+        }
       }
     },
+    '& .dock-extra-content': {
+      alignItems: 'center',
+      paddingRight: '10px',
+    },
     '& .dock-vbox, & .dock-hbox .dock-vbox': {
       '& .dock-divider': {
         flexBasis: '1px',
@@ -91,25 +135,33 @@ const useStyles = makeStyles((theme)=>({
     },
     '& .dock-fbox': {
       zIndex: 1060,
+    },
+    '& .dock-mbox': {
+      zIndex: 1080,
+    },
+    '& .drag-accept-reject::after': {
+      content: '',
     }
   }
 }));
 
+export const LayoutEventsContext = React.createContext();
 
 export class LayoutHelper {
   static getPanel(attrs) {
     return {
       cached: true,
+      group: 'default',
       ...attrs,
     };
   }
 
   static close(docker, panelId) {
-    docker.dockMove(docker.find(panelId), 'remove');
+    docker?.dockMove(docker.find(panelId), 'remove');
   }
 
   static focus(docker, panelId) {
-    docker.updateTab(panelId, null, true);
+    docker?.updateTab(panelId, null, true);
   }
 
   static openDialog(docker, panelData, width=500, height=300) {
@@ -128,12 +180,16 @@ export class LayoutHelper {
         tabs: [LayoutHelper.getPanel({
           ...panelData,
           group: 'dialogs',
-          closable: true,
+          closable: false,
         })],
       }, null, 'float');
     }
   }
 
+  static isTabOpen(docker, panelId) {
+    return Boolean(docker.find(panelId));
+  }
+
   static openTab(docker, panelData, refTabId, direction, forceRerender=false) {
     let panel = docker.find(panelData.id);
     if(panel) {
@@ -149,31 +205,110 @@ export class LayoutHelper {
   }
 }
 
-export default function Layout({groups, layoutInstance, ...props}) {
+function saveLayout(layoutObj, layoutId) {
+  let api = getApiInstance();
+  if(!layoutId || !layoutObj) {
+    return;
+  }
+  const formData = new FormData();
+  formData.append('setting', layoutId);
+  formData.append('value', JSON.stringify(layoutObj.saveLayout()));
+  api.post(url_for('settings.store_bulk'), formData)
+    .catch(()=>{/* No need to throw error */});
+}
+
+function getDialogsGroup() {
+  return {
+    disableDock: true,
+    tabLocked: true,
+    floatable: 'singleTab',
+    panelExtra: (panelData, context) => (
+      <div>
+        <PgIconButton title={gettext('Close')} icon={<CloseIcon  />} size="xs" noBorder onClick={()=>{
+          context.dockMove(panelData, null, 'remove');
+        }} />
+      </div>
+    )
+  };
+}
+
+function getDefaultGroup() {
+  return {
+    maximizable: false,
+    panelExtra: (panelData, context) => {
+      let icon = <ExpandDialogIcon style={{width: '0.7em'}}/>;
+      let title = gettext('Maximise');
+      if(panelData?.parent?.mode == 'maximize') {
+        icon = <MinimizeDialogIcon />;
+        title = gettext('Restore');
+      }
+      return <div>
+        <PgIconButton title={title} icon={icon} size="xs" noBorder onClick={()=>{
+          context.dockMove(panelData, null, 'maximize');
+        }} />
+      </div>;
+    }
+  };
+}
+
+export default function Layout({groups, getLayoutInstance, layoutId, savedLayout, ...props}) {
   const classes = useStyles();
+  const layoutObj = useRef();
   const defaultGroups = React.useMemo(()=>({
-    'dialogs': {
-      disableDock: true,
-      tabLocked: true,
-      floatable: 'singleTab',
-    },
+    'dialogs': getDialogsGroup(),
+    'default': getDefaultGroup(),
     ...groups,
   }), [groups]);
-  return (
+
+  const layoutEventBus = React.useRef(new EventBus());
+  return useMemo(()=>(
     <div className={classes.docklayout}>
-      <DockLayout
-        style={{
-          height: '100%',
-        }}
-        ref={layoutInstance}
-        groups={defaultGroups}
-        {...props}
-      />
+      <LayoutEventsContext.Provider value={layoutEventBus.current}>
+        <DockLayout
+          style={{
+            height: '100%',
+          }}
+          ref={(obj)=>{
+            layoutObj.current = obj;
+            if(layoutObj.current) {
+              layoutObj.current.resetLayout = ()=>{
+                layoutObj.current.loadLayout(props.defaultLayout);
+                saveLayout(layoutObj.current, layoutId);
+              };
+            }
+            getLayoutInstance?.(layoutObj.current);
+            try {
+              layoutObj.current?.loadLayout(JSON.parse(savedLayout));
+            } catch {
+              /* Fallback to default */
+              layoutObj.current?.loadLayout(props.defaultLayout);
+            }
+          }}
+          groups={defaultGroups}
+          onLayoutChange={(_l, currentTabId, direction)=>{
+            saveLayout(layoutObj.current, layoutId);
+            if(Object.values(LAYOUT_EVENTS).indexOf(direction) > -1) {
+              layoutEventBus.current.fireEvent(LAYOUT_EVENTS[direction.toUpperCase()], currentTabId);
+            }
+          }}
+          {...props}
+        />
+      </LayoutEventsContext.Provider>
     </div>
-  );
+  ), []);
 }
 
 Layout.propTypes = {
   groups: PropTypes.object,
-  layoutInstance: CustomPropTypes.ref,
+  getLayoutInstance: PropTypes.func,
+};
+
+
+export const LAYOUT_EVENTS = {
+  ACTIVE: 'active',
+  REMOVE: 'remove',
+  FLOAT: 'float',
+  FRONT: 'front',
+  MAXIMIZE: 'maximize',
+  MOVE: 'move',
 };
diff --git a/web/pgadmin/static/js/helpers/ModalProvider.jsx b/web/pgadmin/static/js/helpers/ModalProvider.jsx
index 1f6d07433..f51f69568 100644
--- a/web/pgadmin/static/js/helpers/ModalProvider.jsx
+++ b/web/pgadmin/static/js/helpers/ModalProvider.jsx
@@ -209,7 +209,7 @@ PaperComponent.propTypes = {
   height: PropTypes.number,
 };
 
-const useModalStyles = makeStyles(() => ({
+export const useModalStyles = makeStyles((theme) => ({
   titleBar: {
     display: 'flex',
     flexGrow: 1
@@ -227,9 +227,18 @@ const useModalStyles = makeStyles(() => ({
     flexShrink: 0,
     userSelect: 'none',
   },
+  footer: {
+    display: 'flex',
+    justifyContent: 'flex-end',
+    padding: '0.5rem',
+    ...theme.mixins.panelBorder?.top,
+  },
+  margin: {
+    marginLeft: '0.25rem',
+  },
   iconButtonStyle: {
     marginLeft: 'auto',
-    marginRight: '0.3em'
+    marginRight: '4px'
   }
 }));
 
diff --git a/web/pgadmin/static/js/sqleditor/calculate_query_run_time.js b/web/pgadmin/static/js/sqleditor/calculate_query_run_time.js
deleted file mode 100644
index 8182ab42a..000000000
--- a/web/pgadmin/static/js/sqleditor/calculate_query_run_time.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import moment from 'moment';
-import gettext from 'sources/gettext';
-
-export function calculateQueryRunTime(startTime, endTime) {
-  let total_ms = moment(endTime).diff(startTime);
-  let result = '';
-  let secs, mins, hrs;
-
-  /* Extract seconds from millisecs */
-  secs = parseInt(total_ms/1000);
-  total_ms = total_ms%1000;
-
-  /* Extract mins from seconds */
-  mins = parseInt(secs/60);
-  secs = secs%60;
-
-  /* Extract hrs from mins */
-  hrs = parseInt(mins/60);
-  mins = mins%60;
-
-  result = (hrs>0 ? hrs + ' ' + gettext('hr') + ' ': '')
-          + (mins>0 ? mins + ' ' + gettext('min') + ' ': '')
-          + (hrs<=0 && secs>0 ? secs + ' ' + gettext('secs') + ' ': '')
-          + (hrs<=0 && mins<=0 ? total_ms + ' ' + gettext('msec') + ' ':'');
-  return result.trim();
-}
diff --git a/web/pgadmin/static/js/sqleditor/call_render_after_poll.js b/web/pgadmin/static/js/sqleditor/call_render_after_poll.js
deleted file mode 100644
index 16fe689e0..000000000
--- a/web/pgadmin/static/js/sqleditor/call_render_after_poll.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {calculateQueryRunTime} from './calculate_query_run_time';
-import gettext from '../gettext';
-
-function hasResultsToDisplay(res) {
-  return res.colinfo != null;
-}
-
-function isQueryTool(sqlEditor) {
-  return sqlEditor.is_query_tool;
-}
-
-function isNotificationEnabled(sqlEditor) {
-  return sqlEditor.info_notifier_timeout >= 0;
-}
-
-export function callRenderAfterPoll(sqlEditor, Notify, res) {
-  sqlEditor.query_end_time = new Date();
-  sqlEditor.rows_affected = res.rows_affected;
-  sqlEditor.has_more_rows = res.has_more_rows;
-
-  if (hasResultsToDisplay(res)) {
-    sqlEditor._render(res);
-  } else {
-    sqlEditor.total_time = calculateQueryRunTime(
-      sqlEditor.query_start_time,
-      sqlEditor.query_end_time);
-    const msg = gettext('Query returned successfully in %s.', sqlEditor.total_time);
-    if (res.result)
-      res.result += '\n\n' + msg;
-    else
-      res.result = msg;
-    sqlEditor.update_msg_history(true, res.result, false);
-    sqlEditor.reset_data_store();
-    if (isNotificationEnabled(sqlEditor)) {
-      Notify.success(msg, sqlEditor.info_notifier_timeout);
-    }
-    sqlEditor.enable_disable_download_btn(true);
-  }
-
-  if (isQueryTool(sqlEditor)) {
-    sqlEditor.disable_tool_buttons(false);
-  }
-
-  sqlEditor.trigger('pgadmin-sqleditor:check_synchronous_db_name_change', res);
-
-  sqlEditor.setIsQueryRunning(false);
-  sqlEditor.trigger('pgadmin-sqleditor:loading-icon:hide');
-}
diff --git a/web/pgadmin/static/js/sqleditor/execute_query.js b/web/pgadmin/static/js/sqleditor/execute_query.js
deleted file mode 100644
index e6a911d85..000000000
--- a/web/pgadmin/static/js/sqleditor/execute_query.js
+++ /dev/null
@@ -1,370 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-import gettext from '../gettext';
-import $ from 'jquery';
-import url_for from '../url_for';
-import axios from 'axios';
-import * as httpErrorHandler from './query_tool_http_error_handler';
-import * as queryTxnStatus from 'sources/sqleditor/query_txn_status_constants';
-import * as SqlEditorUtils from 'sources/sqleditor_utils';
-
-class LoadingScreen {
-  constructor(sqlEditor) {
-    this.sqlEditor = sqlEditor;
-  }
-
-  setMessage(message) {
-    this.sqlEditor.trigger(
-      'pgadmin-sqleditor:loading-icon:message',
-      gettext(message)
-    );
-  }
-
-  show(withMessage) {
-    this.sqlEditor.trigger(
-      'pgadmin-sqleditor:loading-icon:show',
-      withMessage
-    );
-  }
-
-  hide() {
-    this.sqlEditor.trigger('pgadmin-sqleditor:loading-icon:hide');
-  }
-}
-
-class ExecuteQuery {
-  constructor(sqlEditor, userManagement) {
-    this.sqlServerObject = sqlEditor;
-    this.loadingScreen = new LoadingScreen(sqlEditor);
-    this.userManagement = userManagement;
-  }
-
-  delayedPoll() {
-    const self = this;
-    setTimeout(
-      () => {
-        self.poll();
-      }, self.sqlServerObject.POLL_FALLBACK_TIME());
-  }
-
-  execute(sqlStatement, explainPlan, connect) {
-    // If it is an empty query, do nothing.
-    if (sqlStatement.length <= 0) {
-      // Enable query execution button if user execute empty query.
-      $('#btn-flash').prop('disabled', false);
-      return;
-    }
-
-    const self = this;
-    self.explainPlan = explainPlan;
-
-    const sqlStatementWithAnalyze = ExecuteQuery.prepareAnalyzeSql(sqlStatement, explainPlan);
-
-    self.initializeExecutionOnSqlEditor(sqlStatementWithAnalyze);
-    axios.post(
-      this.generateURLReconnectionFlag(connect),
-      JSON.stringify(sqlStatementWithAnalyze),
-      {headers: {'Content-Type': 'application/json'}})
-      .then(function (result) {
-        let httpMessageData = result.data;
-        self.removeGridViewMarker();
-
-        self.updateSqlEditorLastTransactionStatus(httpMessageData.data.transaction_status);
-
-        if (ExecuteQuery.isSqlCorrect(httpMessageData)) {
-          self.loadingScreen.setMessage('Waiting for the query to complete...');
-
-          // Disable drop down arrow to change connections
-          SqlEditorUtils.disable_connection_dropdown(true);
-
-          self.updateSqlEditorStateWithInformationFromServer(httpMessageData.data);
-
-          // If status is True then poll the result.
-          self.delayedPoll();
-        } else {
-          self.loadingScreen.hide();
-          self.enableSQLEditorButtons();
-          self.disableDownloadButton();
-          // Enable/Disable commit and rollback button.
-          if (result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
-            || result.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
-            self.enableTransactionButtons();
-          } else {
-            self.disableTransactionButtons();
-          }
-          self.sqlServerObject.update_msg_history(false, httpMessageData.data.result);
-          if ('notifies' in httpMessageData.data)
-            self.sqlServerObject.update_notifications(httpMessageData.data.notifies);
-
-          // Highlight the error in the sql panel
-          self.sqlServerObject._highlight_error(httpMessageData.data.result);
-        }
-      }).catch(function (error) {
-        self.onExecuteHTTPError(error);
-      }
-      );
-  }
-
-  generateURLReconnectionFlag(shouldReconnect) {
-    let url = url_for('sqleditor.query_tool_start', {
-      'trans_id': this.sqlServerObject.transId,
-    });
-
-    if (shouldReconnect) {
-      url += '?connect=1';
-    }
-    return url;
-  }
-
-  poll() {
-    const self = this;
-    axios.get(
-      url_for('sqleditor.poll', {
-        'trans_id': self.sqlServerObject.transId,
-      })
-    ).then(
-      (httpMessage) => {
-        self.updateSqlEditorLastTransactionStatus(httpMessage.data.data.transaction_status);
-
-        // Enable/Disable commit and rollback button.
-        if (httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INTRANS
-          || httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR) {
-          self.enableTransactionButtons();
-        } else {
-          self.disableTransactionButtons();
-        }
-
-        if (ExecuteQuery.isQueryFinished(httpMessage)) {
-          if (this.sqlServerObject.close_on_idle_transaction &&
-              httpMessage.data.data.transaction_status == queryTxnStatus.TRANSACTION_STATUS_IDLE)
-            this.sqlServerObject.check_needed_confirmations_before_closing_panel();
-
-          self.loadingScreen.setMessage('Loading data from the database server and rendering...');
-
-          self.sqlServerObject.call_render_after_poll(httpMessage.data.data);
-          if ('notifies' in httpMessage.data.data)
-            self.sqlServerObject.update_notifications(httpMessage.data.data.notifies);
-        } else if (ExecuteQuery.isQueryStillRunning(httpMessage)) {
-          // If status is Busy then poll the result by recursive call to the poll function
-          this.delayedPoll();
-          self.sqlServerObject.setIsQueryRunning(true);
-          if (httpMessage.data.data.result) {
-            self.sqlServerObject.update_msg_history(httpMessage.data.data.status, httpMessage.data.data.result, false);
-          }
-        } else if (ExecuteQuery.isConnectionToServerLostWhilePolling(httpMessage)) {
-          self.loadingScreen.hide();
-          // Enable/Disable query tool button only if is_query_tool is true.
-          if (self.sqlServerObject.is_query_tool) {
-            self.enableSQLEditorButtons();
-          }
-          self.sqlServerObject.update_msg_history(false, httpMessage.data.data.result, true);
-        } else if (ExecuteQuery.isQueryCancelled(httpMessage)) {
-          self.loadingScreen.hide();
-          self.sqlServerObject.update_msg_history(false, 'Execution Cancelled!', true);
-        }
-        // Enable connection list drop down again for query tool only
-        if(self.sqlServerObject.is_query_tool && !ExecuteQuery.isQueryStillRunning(httpMessage)){
-          SqlEditorUtils.disable_connection_dropdown(false);
-        }
-      }
-    ).catch(
-      error => {
-        // Enable/Disable query tool button only if is_query_tool is true.
-        self.sqlServerObject.resetQueryHistoryObject(self.sqlServerObject);
-
-        self.loadingScreen.hide();
-        self.sqlServerObject.setIsQueryRunning(false);
-        if (self.sqlServerObject.is_query_tool) {
-          self.enableSQLEditorButtons();
-          SqlEditorUtils.disable_connection_dropdown(false);
-        }
-
-        if(error.response) {
-          if(ExecuteQuery.wasConnectionLostToPythonServer(error.response)) {
-            self.handleConnectionToServerLost();
-            return;
-          }
-          const errorData = error.response.data;
-
-          if (self.userManagement.isPgaLoginRequired(errorData)) {
-            return self.userManagement.pgaLogin();
-          }
-
-          let msg = ExecuteQuery.extractErrorMessage(errorData);
-
-          self.sqlServerObject.update_msg_history(false, msg);
-          // Highlight the error in the sql panel
-          self.sqlServerObject._highlight_error(msg);
-        } else if(error.request) {
-          self.handleConnectionToServerLost();
-          return;
-        } else {
-          console.error(error);
-        }
-      });
-  }
-
-  initializeExecutionOnSqlEditor(sqlStatement) {
-    this.loadingScreen.show(gettext('Running query...'));
-
-    $('#btn-flash').prop('disabled', true);
-    this.disableDownloadButton();
-
-    this.sqlServerObject.query_start_time = new Date();
-    if (typeof sqlStatement === 'object') {
-      this.sqlServerObject.query = sqlStatement['sql'];
-    } else {
-      this.sqlServerObject.query = sqlStatement;
-    }
-
-    this.sqlServerObject.rows_affected = 0;
-    this.sqlServerObject._init_polling_flags();
-    this.disableSQLEditorButtons();
-    this.disableTransactionButtons();
-  }
-
-  static prepareAnalyzeSql(sqlStatement, analyzeSql) {
-    return {
-      sql: sqlStatement,
-      explain_plan: analyzeSql,
-    };
-  }
-
-  onExecuteHTTPError(httpMessage) {
-    this.loadingScreen.hide();
-    this.enableSQLEditorButtons();
-
-    if (ExecuteQuery.wasConnectionLostToPythonServer(httpMessage.response)) {
-      this.handleConnectionToServerLost();
-      return;
-    }
-
-    if (this.userManagement.isPgaLoginRequired(httpMessage.response)) {
-      this.sqlServerObject.saveState('check_data_changes_to_execute_query', [this.explainPlan]);
-      this.userManagement.pgaLogin();
-    }
-
-    if (httpErrorHandler.httpResponseRequiresNewTransaction(httpMessage.response)) {
-      this.sqlServerObject.saveState('check_data_changes_to_execute_query', [this.explainPlan]);
-      this.sqlServerObject.initTransaction();
-    }
-
-    if (this.wasDatabaseConnectionLost(httpMessage)) {
-      this.sqlServerObject.saveState('check_data_changes_to_execute_query', [this.explainPlan]);
-      this.sqlServerObject.handle_connection_lost(false, httpMessage);
-    }
-
-    if(this.isCryptKeyMissing(httpMessage)) {
-      this.sqlServerObject.saveState('check_data_changes_to_execute_query', [this.explainPlan]);
-      this.sqlServerObject.handle_cryptkey_missing();
-      return;
-    }
-
-    let msg = httpMessage.response.data.errormsg;
-    this.sqlServerObject.update_msg_history(false, msg);
-  }
-
-  wasDatabaseConnectionLost(httpMessage) {
-    return httpMessage.response.status === 503 &&
-      httpMessage.response.data.info !== undefined &&
-      httpMessage.response.data.info === 'CONNECTION_LOST';
-  }
-
-  isCryptKeyMissing(httpMessage) {
-    return httpMessage.response.status === 503 &&
-      httpMessage.response.data.info !== undefined &&
-      httpMessage.response.data.info === 'CRYPTKEY_MISSING';
-  }
-
-  removeGridViewMarker() {
-    if (this.sqlServerObject.gridView.marker) {
-      this.sqlServerObject.gridView.marker.clear();
-      delete this.sqlServerObject.gridView.marker;
-      this.sqlServerObject.gridView.marker = null;
-
-      // Remove already existing marker
-      this.sqlServerObject.gridView.query_tool_obj.removeLineClass(this.sqlServerObject.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
-    }
-  }
-
-  disableDownloadButton() {
-    this.sqlServerObject.enable_disable_download_btn(true);
-  }
-
-  enableSQLEditorButtons() {
-    this.sqlServerObject.disable_tool_buttons(false);
-  }
-
-  disableSQLEditorButtons() {
-    this.sqlServerObject.disable_tool_buttons(true);
-  }
-
-  enableTransactionButtons() {
-    this.sqlServerObject.disable_transaction_buttons(false);
-  }
-
-  disableTransactionButtons() {
-    this.sqlServerObject.special_sql = undefined;
-    this.sqlServerObject.disable_transaction_buttons(true);
-  }
-
-  static wasConnectionLostToPythonServer(httpResponse) {
-    return _.isUndefined(httpResponse) || _.isUndefined(httpResponse.data);
-  }
-
-  handleConnectionToServerLost() {
-    this.sqlServerObject.update_msg_history(false,
-      gettext('Not connected to the server or the connection to the server has been closed.')
-    );
-  }
-
-  updateSqlEditorStateWithInformationFromServer(messageData) {
-    this.sqlServerObject.can_edit = messageData.can_edit;
-    this.sqlServerObject.can_filter = messageData.can_filter;
-    this.sqlServerObject.info_notifier_timeout = messageData.info_notifier_timeout;
-  }
-
-  updateSqlEditorLastTransactionStatus(transactionStatus) {
-    this.sqlServerObject.last_transaction_status = transactionStatus;
-  }
-
-  static isSqlCorrect(httpMessageData) {
-    return httpMessageData.data.status;
-  }
-
-  static extractErrorMessage(httpMessage) {
-    let msg = httpMessage.errormsg;
-    if (httpMessage.responseJSON !== undefined &&
-      httpMessage.responseJSON.errormsg !== undefined)
-      msg = httpMessage.responseJSON.errormsg;
-
-    return msg;
-  }
-
-  static isQueryFinished(httpMessage) {
-    return httpMessage.data.data.status === 'Success';
-  }
-
-  static isQueryStillRunning(httpMessage) {
-    return httpMessage.data.data.status === 'Busy';
-  }
-
-  static isQueryCancelled(httpMessage) {
-    return httpMessage.data.data.status === 'Cancel';
-  }
-
-  static isConnectionToServerLostWhilePolling(httpMessage) {
-    return httpMessage.data.data.status === 'NotConnected';
-  }
-}
-
-module.exports = {
-  ExecuteQuery: ExecuteQuery,
-};
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog.js b/web/pgadmin/static/js/sqleditor/filter_dialog.js
deleted file mode 100644
index 76a187b71..000000000
--- a/web/pgadmin/static/js/sqleditor/filter_dialog.js
+++ /dev/null
@@ -1,278 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import url_for from 'sources/url_for';
-import $ from 'jquery';
-import Alertify from 'pgadmin.alertifyjs';
-import pgAdmin from 'sources/pgadmin';
-import Backform from 'pgadmin.backform';
-import axios from 'axios';
-var queryToolActions = require('sources/sqleditor/query_tool_actions');
-import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
-import {handleQueryToolAjaxError} from 'sources/sqleditor/query_tool_http_error_handler';
-import Notify from '../../../static/js/helpers/Notifier';
-
-let FilterDialog = {
-  geturl: function(transId, reconnect) {
-    let url = url_for('sqleditor.get_filter_data', {
-      'trans_id': transId,
-    });
-
-    if(reconnect) {
-      url += '?connect=1';
-    }
-
-    return url;
-  },
-
-  'dialog': function(handler, reconnect) {
-    let title = gettext('Sort/Filter options');
-
-    $.ajax({
-      url: this.geturl(handler.transId, reconnect),
-      headers: {
-        'Cache-Control' : 'no-cache',
-      },
-    })
-      .done(function (res) {
-        let response = res.data.result;
-
-        // Check the alertify dialog already loaded then delete it to clear
-        // the cache
-        if (Alertify.filterDialog) {
-          delete Alertify.filterDialog;
-        }
-
-        // Create Dialog
-        Alertify.dialog('filterDialog', function factory() {
-          let $container = $('<div class=\'data_sorting_dialog\'></div>');
-          return {
-            main: function() {
-              this.set('title', gettext('Sort/Filter options'));
-            },
-            build: function() {
-              this.elements.content.appendChild($container.get(0));
-              Alertify.pgDialogBuild.apply(this);
-            },
-            setup: function() {
-              return {
-                buttons: [{
-                  text: '',
-                  key: 112,
-                  className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
-                  attrs: {
-                    name: 'dialog_help',
-                    type: 'button',
-                    label: gettext('Help'),
-                    'aria-label': gettext('Help'),
-                    url: url_for('help.static', {
-                      'filename': 'editgrid.html',
-                    }),
-                  },
-                }, {
-                  text: gettext('Cancel'),
-                  key: 27,
-                  className: 'btn btn-secondary fa fa-times pg-alertify-button',
-                  'data-btn-name': 'cancel',
-                }, {
-                  text: gettext('OK'),
-                  className: 'btn btn-primary fa fa-check pg-alertify-button',
-                  'data-btn-name': 'ok',
-                }],
-                // Set options for dialog
-                options: {
-                  title: title,
-                  //disable both padding and overflow control.
-                  padding: !1,
-                  overflow: !1,
-                  model: 0,
-                  resizable: true,
-                  maximizable: true,
-                  pinnable: false,
-                  closableByDimmer: false,
-                  modal: false,
-                  autoReset: false,
-                },
-              };
-            },
-            hooks: {
-            // triggered when the dialog is closed
-              onclose: function() {
-                if (this.view) {
-                  this.filterCollectionModel.stopSession();
-                  this.view.model.stopSession();
-                  this.view.remove({
-                    data: true,
-                    internal: true,
-                    silent: true,
-                  });
-                }
-              },
-            },
-            prepare: function() {
-              let self = this;
-              $container.html('');
-              // Disable Ok button
-              this.__internal.buttons[2].element.disabled = true;
-
-              // Status bar
-              this.statusBar = $(
-                '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
-              '  <div class="error-in-footer"> ' +
-              '    <div class="d-flex px-2 py-1"> ' +
-              '      <div class="pr-2"> ' +
-              '        <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
-              '      </div> ' +
-              '      <div class="alert-text" role="alert"></div> ' +
-              '    </div> ' +
-              '  </div> ' +
-              '</div>').appendTo($container);
-
-              // To show progress on filter Saving/Updating on AJAX
-              this.showFilterProgress = $(
-                `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
-                  <div class="pg-sp-content">
-                      <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
-                      <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
-                  </div>
-              </div>`
-              ).appendTo($container);
-              $(
-                self.showFilterProgress[0]
-              ).removeClass('d-none');
-
-              self.filterCollectionModel = filterDialogModel(response);
-
-              let fields = Backform.generateViewSchema(
-                null, self.filterCollectionModel, 'create', null, null, true
-              );
-
-              let view = this.view = new Backform.Dialog({
-                el: '<div></div>',
-                model: self.filterCollectionModel,
-                schema: fields,
-              });
-
-              $(this.elements.body.childNodes[0]).addClass(
-                'alertify_tools_dialog_properties obj_properties'
-              );
-
-              $container.append(view.render().$el);
-
-              // Enable/disable save button and show/hide statusbar based on session
-              view.listenTo(view.model, 'pgadmin-session:start', function() {
-                view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
-                  self.statusBar.removeClass('d-none');
-                  $(self.statusBar.find('.alert-text')).html(msg);
-                  // Disable Okay button
-                  self.__internal.buttons[2].element.disabled = true;
-                });
-
-                view.listenTo(view.model, 'pgadmin-session:valid', function() {
-                  self.statusBar.addClass('d-none');
-                  $(self.statusBar.find('.alert-text')).html('');
-                  // Enable Okay button
-                  self.__internal.buttons[2].element.disabled = false;
-                });
-              });
-
-              view.listenTo(view.model, 'pgadmin-session:stop', function() {
-                view.stopListening(view.model, 'pgadmin-session:invalid');
-                view.stopListening(view.model, 'pgadmin-session:valid');
-              });
-
-              // Starts monitoring changes to model
-              view.model.startNewSession();
-
-              // Set data in collection
-              let viewDataSortingModel = view.model.get('data_sorting');
-              viewDataSortingModel.add(response['data_sorting']);
-
-              // Hide Progress ...
-              $(
-                self.showFilterProgress[0]
-              ).addClass('d-none');
-
-            },
-            // Callback functions when click on the buttons of the Alertify dialogs
-            callback: function(e) {
-              let self = this;
-
-              if (e.button.element.name == 'dialog_help') {
-                e.cancel = true;
-                pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
-                  null, null);
-              } else if (e.button['data-btn-name'] === 'ok') {
-                e.cancel = true; // Do not close dialog
-
-                let filterCollectionModel = this.filterCollectionModel.toJSON();
-
-                // Show Progress ...
-                $(
-                  self.showFilterProgress[0]
-                ).removeClass('d-none');
-
-                axios.put(
-                  url_for('sqleditor.set_filter_data', {
-                    'trans_id': handler.transId,
-                  }),
-                  filterCollectionModel
-                ).then(function (result) {
-                // Hide Progress ...
-                  $(
-                    self.showFilterProgress[0]
-                  ).addClass('d-none');
-
-                  let filterResponse = result.data.data;
-
-                  if (filterResponse.status) {
-                    setTimeout(
-                      function() {
-                        self.close(); // Close the dialog now
-                        Notify.success(gettext('Filter updated successfully'));
-                        queryToolActions.executeQuery(handler);
-                      }, 10
-                    );
-                  } else {
-                    Notify.alert(
-                      gettext('Validation Error'),
-                      filterResponse.result
-                    );
-                  }
-
-                }).catch(function (error) {
-                // Hide Progress ...
-                  $(
-                    self.showFilterProgress[0]
-                  ).addClass('d-none');
-                  handler.onExecuteHTTPError(error);
-
-                  setTimeout(
-                    function() {
-                      Notify.error(error);
-                    }, 10
-                  );
-                });
-              } else {
-                self.close();
-              }
-            },
-          };
-        });
-
-        Alertify.filterDialog(title).resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
-      })
-      .fail(function(e) {
-        handleQueryToolAjaxError(pgAdmin, handler, e, '_show_filter', [], true);
-      });
-  },
-};
-
-module.exports = FilterDialog;
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
deleted file mode 100644
index f5a66e34a..000000000
--- a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
+++ /dev/null
@@ -1,137 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import _ from 'underscore';
-import pgAdmin from 'sources/pgadmin';
-import Backgrid from 'pgadmin.backgrid';
-import Backform from 'pgadmin.backform';
-
-export default function filterDialogModel(response) {
-
-  let order_mapping = {
-    'asc': gettext('ASC'),
-    'desc': gettext('DESC'),
-  };
-
-  let DataSortingModel = pgAdmin.Browser.DataModel.extend({
-    idAttribute: 'name',
-    defaults: {
-      name: undefined,
-      order: 'asc',
-    },
-    schema: [{
-      id: 'name',
-      name: 'name',
-      label: gettext('Column'),
-      cell: 'select2',
-      editable: true,
-      cellHeaderClasses: 'width_percent_60',
-      headerCell: Backgrid.Extension.CustomHeaderCell,
-      disabled: false,
-      control: 'select2',
-      select2: {
-        allowClear: false,
-      },
-      options: function() {
-        return _.map(response.column_list, (obj) => {
-          return {
-            value: obj,
-            label: obj,
-          };
-        });
-      },
-    },
-    {
-      id: 'order',
-      name: 'order',
-      label: gettext('Order'),
-      control: 'select2',
-      cell: 'select2',
-      cellHeaderClasses: 'width_percent_40',
-      headerCell: Backgrid.Extension.CustomHeaderCell,
-      editable: true,
-      deps: ['type'],
-      select2: {
-        allowClear: false,
-      },
-      options: function() {
-        return _.map(order_mapping, (val, key) => {
-          return {
-            value: key,
-            label: val,
-          };
-        });
-      },
-    },
-    ],
-    validate: function() {
-      let msg = null;
-      this.errorModel.clear();
-      if (_.isUndefined(this.get('name')) ||
-        _.isNull(this.get('name')) ||
-        String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
-        msg = gettext('Please select a column.');
-        this.errorModel.set('name', msg);
-        return msg;
-      } else if (_.isUndefined(this.get('order')) ||
-        _.isNull(this.get('order')) ||
-        String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
-        msg = gettext('Please select the order.');
-        this.errorModel.set('order', msg);
-        return msg;
-      }
-      return null;
-    },
-  });
-
-  let FilterCollectionModel = pgAdmin.Browser.DataModel.extend({
-    idAttribute: 'sql',
-    defaults: {
-      sql: response.sql || null,
-    },
-    schema: [{
-      id: 'sql',
-      label: gettext('SQL Filter'),
-      cell: 'string',
-      type: 'text', mode: ['create'],
-      control: Backform.SqlFieldControl.extend({
-        render: function() {
-          let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
-          // We need to set focus on editor after the dialog renders
-          setTimeout(() => {
-            obj.sqlCtrl.focus();
-          }, 1000);
-          return obj;
-        },
-      }),
-      extraClasses:['custom_height_css_class'],
-    },{
-      id: 'data_sorting',
-      name: 'data_sorting',
-      label: gettext('Data Sorting'),
-      model: DataSortingModel,
-      editable: true,
-      type: 'collection',
-      mode: ['create'],
-      control: 'unique-col-collection',
-      uniqueCol: ['name'],
-      canAdd: true,
-      canEdit: false,
-      canDelete: true,
-      visible: true,
-      version_compatible: true,
-    }],
-    validate: function() {
-      return null;
-    },
-  });
-
-  return new FilterCollectionModel();
-}
diff --git a/web/pgadmin/static/js/sqleditor/geometry_viewer.js b/web/pgadmin/static/js/sqleditor/geometry_viewer.js
deleted file mode 100644
index 23a1d3843..000000000
--- a/web/pgadmin/static/js/sqleditor/geometry_viewer.js
+++ /dev/null
@@ -1,434 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import {Buffer} from 'buffer';
-import $ from 'jquery';
-
-var L = null;
-var Geometry = null;
-let GeometryViewer = {
-  panel_closed: true,
-
-  go_for_render: function(handler, items, columns, columnIndex) {
-    let self = this;
-
-    if (!self.map_component) {
-      self.map_component = initMapComponent();
-    }
-
-    if (self.panel_closed) {
-      let wcDocker = window.wcDocker;
-      let geometry_viewer_panel = handler.gridView.geometry_viewer =
-        handler.gridView.docker.addPanel('geometry_viewer',
-          wcDocker.DOCK.STACKED, handler.gridView.data_output_panel);
-      $('#geometry_viewer_panel')[0].appendChild(self.map_component.mapContainer.get(0));
-      self.panel_closed = false;
-
-      geometry_viewer_panel.on(wcDocker.EVENT.CLOSED, function () {
-        $('#geometry_viewer_panel').empty();
-        self.map_component.clearMap();
-        self.panel_closed = true;
-      });
-
-      geometry_viewer_panel.on(wcDocker.EVENT.RESIZE_ENDED, function () {
-        if (geometry_viewer_panel.isVisible()) {
-          self.map_component.resizeMap();
-        }
-      });
-
-      geometry_viewer_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function (visible) {
-        if (visible) {
-          self.map_component.resizeMap();
-        } else {
-          self.map_component.loseFocus();
-        }
-      });
-    }
-
-    handler.gridView.geometry_viewer.focus();
-    self.map_component.clearMap();
-    let dataObj = parseData(items, columns, columnIndex, Geometry);
-    self.map_component.renderMap(dataObj);
-  },
-
-  render_geometries: function (handler, items, columns, columnIndex) {
-    let self = this;
-    require.ensure(['leaflet', 'wkx'], function(require) {
-      L = require('leaflet');
-      Geometry = require('wkx').Geometry;
-      self.go_for_render(handler, items, columns, columnIndex);
-    }, function(error){
-      throw(error);
-    }, 'geometry');
-  },
-
-  add_header_button: function (columnDefinition) {
-    columnDefinition.header = {
-      buttons: [
-        {
-          cssClass: 'div-view-geometry-column',
-          tooltip: 'View all geometries in this column',
-          showOnHover: false,
-          command: 'view-geometries',
-          content: '<button class="btn-xs btn-primary"><i class="fa fa-eye" aria-hidden="true"></i></button>',
-        },
-      ],
-    };
-  },
-
-  parse_data: parseData,
-};
-
-function initMapComponent() {
-  const geojsonMarkerOptions = {
-    radius: 4,
-    weight: 3,
-  };
-  const geojsonStyle = {
-    weight: 2,
-  };
-  const popupOption = {
-    closeButton: false,
-    minWidth: 260,
-    maxWidth: 300,
-    maxHeight: 300,
-  };
-
-  let mapContainer = $('<div class="geometry-viewer-container"></div>');
-  let lmap = L.map(mapContainer.get(0), {
-    preferCanvas: true,
-  }).setZoom(0);
-
-  // update default attribution
-  lmap.attributionControl.setPrefix('');
-
-  let vectorLayer = L.geoJSON([], {
-    style: geojsonStyle,
-    pointToLayer: function (feature, latlng) {
-      return L.circleMarker(latlng, geojsonMarkerOptions);
-    },
-  });
-  vectorLayer.addTo(lmap);
-  let baseLayersObj = {
-    'Empty': L.tileLayer(''),
-    'Street': L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
-      {
-        maxZoom: 19,
-        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>',
-      }),
-    'Topography': L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
-      {
-        maxZoom: 17,
-        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,' +
-          ' &copy; <a href="http://viewfinderpanoramas.org" target="_blank">SRTM</a>,' +
-          ' &copy; <a href="https://opentopomap.org" target="_blank">OpenTopoMap</a>',
-      }),
-    'Gray Style': L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png',
-      {
-        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,' +
-          ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>',
-        subdomains: 'abcd',
-        maxZoom: 19,
-      }),
-    'Light Color': L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager/{z}/{x}/{y}{r}.png',
-      {
-        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,' +
-          ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>',
-        subdomains: 'abcd',
-        maxZoom: 19,
-      }),
-    'Dark Matter': L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png',
-      {
-        attribution: '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,' +
-          ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>',
-        subdomains: 'abcd',
-        maxZoom: 19,
-      }),
-  };
-  let layerControl = L.control.layers(baseLayersObj);
-  let defaultBaseLayer = baseLayersObj.Street;
-  let baseLayers = _.values(baseLayersObj);
-
-  let infoControl = L.control({position: 'topright'});
-  infoControl.onAdd = function () {
-    this._div = L.DomUtil.create('div', 'geometry-viewer-info-control');
-    return this._div;
-  };
-  infoControl.update = function (content) {
-    this._div.innerHTML = content;
-  };
-
-  let setEPSG3857 = function () {
-    if (lmap.options.crs !== L.CRS.EPSG3857) {
-      lmap.options.crs = L.CRS.EPSG3857;
-      layerControl.addTo(lmap);
-      lmap.addLayer(defaultBaseLayer);
-      mapContainer.addClass('geometry-viewer-container-plain-background');
-      lmap.setMinZoom(0);
-    }
-  };
-
-  let setSimpleCRS = function () {
-    if (lmap.options.crs !== L.CRS.Simple) {
-      lmap.options.crs = L.CRS.Simple;
-      layerControl.remove();
-      _.each(baseLayers, function (layer) {
-        if (lmap.hasLayer(layer)) {
-          defaultBaseLayer = layer;
-          layer.remove();
-        }
-      });
-      mapContainer.removeClass('geometry-viewer-container-plain-background');
-    }
-  };
-
-  setSimpleCRS();
-  setEPSG3857();
-
-  return {
-    'mapContainer': mapContainer,
-    'clearMap': function () {
-      lmap.closePopup();
-      infoControl.remove();
-      vectorLayer.clearLayers();
-    },
-
-    'loseFocus': function() {
-      lmap.fire('blur');
-    },
-
-    'renderMap': function (dataObj) {
-      let geoJSONs = dataObj.geoJSONs,
-        SRID = dataObj.selectedSRID,
-        getPopupContent = dataObj.getPopupContent,
-        infoList = dataObj.infoList;
-
-      let isEmpty = false;
-      if (geoJSONs.length === 0) {
-        isEmpty = true;
-      }
-
-      try {
-        vectorLayer.addData(geoJSONs);
-      } catch (e) {
-        // Invalid LatLng object: (NaN, NaN)
-        infoList.push('An error occurred while rendering data.');
-        isEmpty = true;
-      }
-
-      let bounds = vectorLayer.getBounds();
-      if (!bounds.isValid()) {
-        isEmpty = true;
-      }
-
-      if (infoList.length > 0) {
-        if (lmap.options.crs === L.CRS.EPSG3857) {
-          layerControl.remove();
-          infoControl.addTo(lmap);
-          layerControl.addTo(lmap);
-        } else {
-          infoControl.addTo(lmap);
-        }
-        let infoContent = generateInfoContent(infoList);
-        infoControl.update(infoContent);
-      }
-
-      if (isEmpty) {
-        setSimpleCRS();
-        lmap.setView([0, 0], lmap.getZoom());
-        return;
-      }
-
-      if (typeof getPopupContent === 'function') {
-        let addPopup = function (layer) {
-          layer.bindPopup(function () {
-            return getPopupContent(layer.feature.geometry);
-          }, popupOption);
-        };
-        vectorLayer.eachLayer(addPopup);
-      }
-
-      bounds = bounds.pad(0.1);
-      let maxLength = Math.max(bounds.getNorth() - bounds.getSouth(),
-        bounds.getEast() - bounds.getWest());
-      if (SRID === 4326) {
-        setEPSG3857();
-      } else {
-        setSimpleCRS();
-        if (maxLength >= 180) {
-          // calculate the min zoom level to enable the map to fit the whole geometry.
-          let minZoom = Math.floor(Math.log2(360 / maxLength)) - 2;
-          lmap.setMinZoom(minZoom);
-        } else {
-          lmap.setMinZoom(0);
-        }
-      }
-
-      if (maxLength > 0) {
-        lmap.fitBounds(bounds);
-      } else {
-        lmap.setView(bounds.getCenter(), lmap.getZoom());
-      }
-    },
-
-    'resizeMap': function () {
-      setTimeout(function () {
-        lmap.invalidateSize();
-      }, 10);
-    },
-
-  };
-}
-
-function parseData(items, columns, columnIndex, GeometryLib) {
-  const maxRenderByteLength = 20 * 1024 * 1024; //render geometry data up to 20MB
-  const maxRenderGeometries = 100000; // render geometries up to 100000
-  let field = columns[columnIndex].field;
-  let geometries3D = [],
-    supportedGeometries = [],
-    unsupportedItems = [],
-    infoList = [],
-    geometryItemMap = new Map(),
-    mixedSRID = false,
-    geometryTotalByteLength = 0,
-    tooLargeDataSize = false,
-    tooManyGeometries = false;
-
-  if (items.length === 0) {
-    infoList.push('Empty row.');
-    return {
-      'geoJSONs': [],
-      'selectedSRID': 0,
-      'getPopupContent': undefined,
-      'infoList': infoList,
-    };
-  }
-
-  // parse ewkb data
-  _.every(items, function (item) {
-    try {
-      let value = item[field];
-      let buffer = Buffer.from(value, 'hex');
-      let geometry = GeometryLib.parse(buffer);
-      if (geometry.hasZ) {
-        geometries3D.push(geometry);
-      } else {
-        geometryTotalByteLength += buffer.byteLength;
-        if (geometryTotalByteLength > maxRenderByteLength) {
-          tooLargeDataSize = true;
-          return false;
-        }
-        if (supportedGeometries.length >= maxRenderGeometries) {
-          tooManyGeometries = true;
-          return false;
-        }
-
-        if (!geometry.srid) {
-          geometry.srid = 0;
-        }
-        supportedGeometries.push(geometry);
-        geometryItemMap.set(geometry, item);
-      }
-    } catch (e) {
-      unsupportedItems.push(item);
-    }
-    return true;
-  });
-
-  // generate map info content
-  if (tooLargeDataSize || tooManyGeometries) {
-    infoList.push(supportedGeometries.length + ' of ' + items.length + ' geometries rendered.');
-  }
-  if (geometries3D.length > 0) {
-    infoList.push(gettext('3D geometries not rendered.'));
-  }
-  if (unsupportedItems.length > 0) {
-    infoList.push(gettext('Unsupported geometries not rendered.'));
-  }
-
-  if (supportedGeometries.length === 0) {
-    return {
-      'geoJSONs': [],
-      'selectedSRID': 0,
-      'getPopupContent': undefined,
-      'infoList': infoList,
-    };
-  }
-
-  // group geometries by SRID
-  let geometriesGroupBySRID = _.groupBy(supportedGeometries, 'srid');
-  let SRIDGeometriesPairs = _.pairs(geometriesGroupBySRID);
-  if (SRIDGeometriesPairs.length > 1) {
-    mixedSRID = true;
-  }
-  // select the largest group
-  let selectedPair = _.max(SRIDGeometriesPairs, function (pair) {
-    return pair[1].length;
-  });
-  let selectedSRID = parseInt(selectedPair[0]);
-  let selectedGeometries = selectedPair[1];
-
-  let geoJSONs = _.map(selectedGeometries, function (geometry) {
-    return geometry.toGeoJSON();
-  });
-
-  let getPopupContent;
-  if (columns.length >= 3) {
-    // add popup when geometry has properties
-    getPopupContent = function (geojson) {
-      let geometry = selectedGeometries[geoJSONs.indexOf(geojson)];
-      let item = geometryItemMap.get(geometry);
-      return itemToTable(item, columns, columnIndex);
-    };
-  }
-
-  if (mixedSRID) {
-    infoList.push(gettext('Geometries with non-SRID %s not rendered.', selectedSRID));
-  }
-
-  return {
-    'geoJSONs': geoJSONs,
-    'selectedSRID': selectedSRID,
-    'getPopupContent': getPopupContent,
-    'infoList': infoList,
-  };
-}
-
-function itemToTable(item, columns, ignoredColumnIndex) {
-  let content = '<table class="table table-bordered table-striped view-geometry-property-table"><tbody>';
-
-  // start from 1 because columns[0] is empty
-  for (let i = 1; i < columns.length; i++) {
-    if (i !== ignoredColumnIndex) {
-      let columnDef = columns[i];
-      content += '<tr><th>' + columnDef.display_name + '</th>';
-
-      let value = item[columnDef.field];
-      if (_.isUndefined(value) && columnDef.has_default_val) {
-        content += '<td class="td-disabled">[default]</td>';
-      } else if ((_.isUndefined(value) && columnDef.not_null) ||
-        (_.isUndefined(value) || value === null)) {
-        content += '<td class="td-disabled">[null]</td>';
-      } else {
-        content += '<td>' + value + '</td>';
-      }
-
-      content += '</tr>';
-    }
-  }
-  content += '</tbody></table>';
-  return content;
-}
-
-function generateInfoContent(infoList) {
-  return infoList.join('<br>');
-}
-
-module.exports = GeometryViewer;
diff --git a/web/pgadmin/static/js/sqleditor/history/history_collection.js b/web/pgadmin/static/js/sqleditor/history/history_collection.js
deleted file mode 100644
index 647d1d829..000000000
--- a/web/pgadmin/static/js/sqleditor/history/history_collection.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-export default class HistoryCollection {
-
-  constructor(history_model) {
-    this.historyList = _.sortBy(history_model, o=>o.start_time);
-    this.onAdd(() => {/*This is intentional (SonarQube)*/});
-  }
-
-  length() {
-    return this.historyList.length;
-  }
-
-  add(object) {
-    /* add object in sorted order */
-    let pushAt = _.sortedIndex(this.historyList, object, o=>o.start_time);
-    this.historyList.splice(pushAt, 0, object);
-    this.onAddHandler(object);
-  }
-
-  reset() {
-    this.historyList = [];
-    this.onResetHandler(this.historyList);
-  }
-
-  onAdd(onAddHandler) {
-    this.onAddHandler = onAddHandler;
-  }
-
-  onReset(onResetHandler) {
-    this.onResetHandler = onResetHandler;
-  }
-}
diff --git a/web/pgadmin/static/js/sqleditor/history/query_history.js b/web/pgadmin/static/js/sqleditor/history/query_history.js
deleted file mode 100644
index e8e4e4d45..000000000
--- a/web/pgadmin/static/js/sqleditor/history/query_history.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import QueryHistoryDetails from './query_history_details';
-import { QueryHistoryEntries } from './query_history_entries';
-import Split from 'split.js';
-import gettext from 'sources/gettext';
-import $ from 'jquery';
-
-export default class QueryHistory {
-  constructor(parentNode, histModel) {
-    this.parentNode = parentNode;
-    this.histCollection = histModel;
-    this.editorPref = {};
-
-    this.onCopyToEditorHandler = ()=>{/*This is intentional (SonarQube)*/};
-    this.histCollection.onAdd(this.onAddEntry.bind(this));
-    this.histCollection.onReset(this.onResetEntries.bind(this));
-  }
-
-  focus() {
-    if (this.queryHistEntries) {
-      this.queryHistEntries.focus();
-    }
-  }
-
-  onAddEntry(entry) {
-    if (this.histCollection.length() == 1) {
-      this.render();
-    } else if (this.queryHistEntries) {
-      this.queryHistEntries.addEntry(entry);
-    }
-  }
-
-  onResetEntries() {
-    this.isRendered = false;
-    this.queryHistEntries = null;
-    this.queryHistDetails = null;
-    this.render();
-  }
-
-  onCopyToEditorClick(onCopyToEditorHandler) {
-    this.onCopyToEditorHandler = onCopyToEditorHandler;
-
-    if(this.queryHistDetails) {
-      this.queryHistDetails.onCopyToEditorClick(this.onCopyToEditorHandler);
-    }
-  }
-
-  setEditorPref(editorPref) {
-    this.editorPref = {
-      ...this.editorPref,
-      ...editorPref,
-    };
-    if(this.queryHistDetails) {
-      this.queryHistDetails.setEditorPref(this.editorPref);
-    }
-  }
-
-  render() {
-    if (this.histCollection.length() == 0) {
-      this.parentNode.empty()
-        .removeClass('d-flex')
-        .append(
-          '<div role="status" class="pg-panel-message">' +
-            gettext('No history found') +
-          '</div>'
-        );
-    } else {
-      this.parentNode.empty().addClass('d-flex');
-      let $histEntries = $('<div><div>').appendTo(this.parentNode);
-      let $histDetails = $('<div><div>').appendTo(this.parentNode);
-
-      Split([$histEntries[0], $histDetails[0]], {
-        gutterSize: 1,
-        cursor: 'ew-resize',
-      });
-
-      $histDetails.css('overflow', 'auto');
-
-      this.queryHistDetails = new QueryHistoryDetails($histDetails);
-      this.queryHistDetails.setEditorPref(this.editorPref);
-      this.queryHistDetails.onCopyToEditorClick(this.onCopyToEditorHandler);
-      this.queryHistDetails.render();
-
-      this.queryHistEntries = new QueryHistoryEntries($histEntries);
-      this.queryHistEntries.onSelectedChange(
-        (entry => {
-          this.queryHistDetails.setEntry(entry);
-        }).bind(this)
-      );
-      this.queryHistEntries.render();
-
-      this.histCollection.historyList.map((entry)=>{
-        this.queryHistEntries.addEntry(entry);
-      });
-    }
-  }
-}
diff --git a/web/pgadmin/static/js/sqleditor/history/query_history_details.js b/web/pgadmin/static/js/sqleditor/history/query_history_details.js
deleted file mode 100644
index e77b71097..000000000
--- a/web/pgadmin/static/js/sqleditor/history/query_history_details.js
+++ /dev/null
@@ -1,216 +0,0 @@
-import CodeMirror from 'bundled_codemirror';
-import clipboard from 'sources/selection/clipboard';
-import gettext from 'sources/gettext';
-import $ from 'jquery';
-import _ from 'underscore';
-
-export default class QueryHistoryDetails {
-  constructor(parentNode) {
-    this.parentNode = parentNode;
-    this.isCopied = false;
-    this.timeout = null;
-    this.isRendered = false;
-    this.sqlFontSize = null;
-    this.onCopyToEditorHandler = ()=>{/*This is intentional (SonarQube)*/};
-
-    this.editorPref = {
-      'sql_font_size': '1em',
-      'copy_to_editor': true,
-    };
-  }
-
-  setEntry(entry) {
-    this.entry = entry;
-    if (this.isRendered) {
-      this.selectiveRender();
-    } else {
-      this.render();
-    }
-  }
-
-  setEditorPref(editorPref={}) {
-    this.editorPref = {
-      ...this.editorPref,
-      ...editorPref,
-    };
-
-    if(this.query_codemirror && !_.isUndefined(editorPref.sql_font_size)) {
-      $(this.query_codemirror.getWrapperElement()).css(
-        'font-size',this.editorPref.sql_font_size
-      );
-
-      this.query_codemirror.refresh();
-    }
-
-    if(this.$copyToEditor && !_.isUndefined(editorPref.copy_to_editor)) {
-      if(editorPref.copy_to_editor) {
-        this.$copyToEditor.removeClass('d-none');
-      } else {
-        this.$copyToEditor.addClass('d-none');
-      }
-    }
-  }
-
-  parseErrorMessage(message) {
-    return message.match(/ERROR:\s*([^\n\r]*)/i)
-      ? message.match(/ERROR:\s*([^\n\r]*)/i)[1]
-      : message;
-  }
-
-  formatDate(date) {
-    return date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
-  }
-
-  copyAllHandler() {
-    clipboard.copyTextToClipboard(this.entry.query);
-
-    this.clearPreviousTimeout();
-
-    this.updateCopyButton(true);
-
-    this.timeout = setTimeout(() => {
-      this.updateCopyButton(false);
-    }, 1500);
-  }
-
-  onCopyToEditorClick(onCopyToEditorHandler) {
-    this.onCopyToEditorHandler = onCopyToEditorHandler;
-  }
-
-  clearPreviousTimeout() {
-    if (this.timeout) {
-      clearTimeout(this.timeout);
-      this.timeout = null;
-    }
-  }
-
-  updateCopyButton(copied) {
-    if (copied) {
-      this.$copyBtn.addClass('was-copied').removeClass('copy-all');
-      this.$copyBtn.text(gettext('Copied!'));
-    } else {
-      this.$copyBtn.addClass('copy-all').removeClass('was-copied');
-      this.$copyBtn.text(gettext('Copy'));
-    }
-  }
-
-  updateQueryMetaData() {
-    let itemTemplate = (data, description) => {
-      if(data)
-        return `<div class='item'>
-                  <span class='value'>${data}</span>
-                  <span class='description'>${description}</span>
-              </div>`;
-      else
-        return '';
-    };
-
-    this.$metaData.empty().append(
-      '<div class="metadata">' +
-      itemTemplate(this.formatDate(this.entry.start_time), gettext('Date')) +
-      itemTemplate(
-        this.entry.row_affected.toLocaleString(),
-        gettext('Rows Affected')
-      ) +
-      itemTemplate(this.entry.total_time, gettext('Duration')) +
-      '</div>'
-    );
-  }
-
-  updateMessageContent() {
-    this.$message_content
-      .empty()
-      .append(`<pre class='content-value'>${_.escape(this.entry.message)}</pre>`);
-  }
-
-  updateErrorMessage() {
-    if (!this.entry.status) {
-      this.$errMsgBlock.removeClass('d-none');
-      this.$errMsgBlock.empty().append(
-        `<div class='history-error-text'>
-            <span>` + gettext('Error Message') + `</span> ${_.escape(this.parseErrorMessage(this.entry.message))}
-        </div>`
-      );
-    } else {
-      this.$errMsgBlock.addClass('d-none');
-      this.$errMsgBlock.empty();
-    }
-  }
-
-  updateInfoMessage() {
-    if (this.entry.info) {
-      this.$infoMsgBlock.removeClass('d-none');
-      this.$infoMsgBlock.empty().append(
-        `<div class='history-info-text'>
-            ${this.entry.info}
-        </div>`
-      );
-    } else {
-      this.$infoMsgBlock.addClass('d-none');
-      this.$infoMsgBlock.empty();
-    }
-  }
-
-  selectiveRender() {
-    this.updateErrorMessage();
-    this.updateInfoMessage();
-    this.updateCopyButton(false);
-    this.updateQueryMetaData();
-    this.query_codemirror.setValue(this.entry.query);
-    this.updateMessageContent();
-  }
-
-  render() {
-    if (this.entry) {
-      this.parentNode.empty().append(
-        `<div id='query_detail' class='query-detail'>
-            <div class='error-message-block'></div>
-            <div class='info-message-block'></div>
-            <div class='metadata-block'></div>
-            <div class='query-statement-block'>
-              <div id='history-detail-query'>
-                <button class='btn-copy' tabindex=0 accesskey='y'></button>
-                <button class='btn-copy-editor copy-to-editor' tabindex=0 accesskey='y'>` + gettext('Copy to Query Editor') + `</button>
-                <div></div>
-              </div>
-            </div>
-            <div>
-              <hr class='block-divider'/>
-            </div>
-            <div class='message-block'>
-              <div class='message'>
-                <div class='message-header'>` + gettext('Messages') + `</div>
-                <div class='content'></div>
-              </div>
-            </div>
-        </div>`
-      );
-
-      this.$errMsgBlock = this.parentNode.find('.error-message-block');
-      this.$infoMsgBlock = this.parentNode.find('.info-message-block');
-      this.$copyBtn = this.parentNode.find('#history-detail-query .btn-copy');
-      this.$copyBtn.off('click').on('click', this.copyAllHandler.bind(this));
-      this.$copyToEditor = this.parentNode.find('#history-detail-query .btn-copy-editor');
-      this.$copyToEditor.off('click').on('click', () => {
-        this.onCopyToEditorHandler(this.entry.query);
-      });
-      this.$copyToEditor.addClass(this.editorPref.copy_to_editor?'':'d-none');
-      this.$metaData = this.parentNode.find('.metadata-block');
-      this.query_codemirror = CodeMirror(
-        this.parentNode.find('#history-detail-query div')[0],
-        {
-          tabindex: -1,
-          mode: 'text/x-pgsql',
-          readOnly: true,
-        }
-      );
-      $(this.query_codemirror.getWrapperElement()).css(
-        'font-size',this.editorPref.sql_font_size
-      );
-      this.$message_content = this.parentNode.find('.message-block .content');
-
-      this.isRendered = true;
-      this.selectiveRender();
-    }
-  }
-}
diff --git a/web/pgadmin/static/js/sqleditor/history/query_history_entries.js b/web/pgadmin/static/js/sqleditor/history/query_history_entries.js
deleted file mode 100644
index 41d5cc310..000000000
--- a/web/pgadmin/static/js/sqleditor/history/query_history_entries.js
+++ /dev/null
@@ -1,296 +0,0 @@
-import moment from 'moment';
-import $ from 'jquery';
-import _ from 'underscore';
-import 'bootstrap.toggle';
-import gettext from 'sources/gettext';
-
-const ARROWUP = 38;
-const ARROWDOWN = 40;
-
-export class QueryHistoryEntryDateGroup {
-  constructor(date, groupKey) {
-    this.date = date;
-    this.formatString = 'MMM DD YYYY';
-    this.groupKey = groupKey;
-  }
-
-  getDatePrefix() {
-    let prefix = '';
-    if (this.isDaysBefore(0)) {
-      prefix = 'Today - ';
-    } else if (this.isDaysBefore(1)) {
-      prefix = 'Yesterday - ';
-    }
-    return prefix;
-  }
-
-  getDateFormatted(date) {
-    return date.toLocaleDateString();
-  }
-
-  isDaysBefore(before) {
-    return (
-      this.getDateFormatted(this.date) ===
-      this.getDateFormatted(moment().subtract(before, 'days').toDate())
-    );
-  }
-
-  render() {
-    return $(`<div class='query-group' data-key='${this.groupKey}'>
-            <div class='date-label'>${this.getDatePrefix()}${this.getDateFormatted(this.date)}</div>
-            <ul class='query-entries'></ul>
-        </div>`);
-  }
-}
-
-export class QueryHistoryItem {
-  constructor(entry) {
-    this.entry = entry;
-    this.$el = null;
-    this.onClickHandler = null;
-  }
-
-  onClick(onClickHandler) {
-    this.onClickHandler = onClickHandler;
-    if (this.$el) {
-      this.$el.off('click').on('click', e => {
-        this.onClickHandler($(e.currentTarget));
-      });
-    }
-  }
-
-  formatDate(date) {
-    return moment(date).format('HH:mm:ss');
-  }
-
-  dataKey() {
-    return this.formatDate(this.entry.start_time);
-  }
-
-  render(is_pgadmin_queries_shown) {
-    this.$el = $(
-      `<li class='list-item' tabindex='0' data-key='${this.dataKey()}'>
-          <div class='entry ${this.entry.status ? '' : 'error'}'>
-              <div class='query'>
-                  <span id="query_source_icon" class="query-history-icon sql-icon-lg"></span>
-                  ${_.escape(this.entry.query)}
-              </div>
-              <div class='other-info'>
-              <div class='timestamp'>${this.formatDate(this.entry.start_time)}</div>
-              </div>
-          </div>
-      </li>`
-    )
-      .data('entrydata', this.entry)
-      .on('click', e => {
-        this.onClickHandler($(e.currentTarget));
-      });
-
-    let query_source = this.entry.query_source;
-    if(query_source)
-      this.$el.find('#query_source_icon')
-        .addClass(query_source.ICON_CSS_CLASS)
-        .attr('role', 'img');
-
-    if(this.entry.is_pgadmin_query) {
-      this.$el.addClass('pgadmin-query-history-entry');
-      if(!is_pgadmin_queries_shown)
-        this.$el.addClass('d-none');
-    }
-  }
-}
-
-export class QueryHistoryEntries {
-  constructor(parentNode) {
-    this.parentNode = parentNode;
-    this.$selectedItem = null;
-    this.groupKeyFormat = 'YYYY MM DD';
-
-    this.$el = null;
-    this.is_pgadmin_queries_shown = null;
-  }
-
-  onSelectedChange(onSelectedChangeHandler) {
-    this.onSelectedChangeHandler = onSelectedChangeHandler;
-  }
-
-  focus() {
-    if (!this.$selectedItem) {
-      this.setSelectedListItem(this.$entriesEl.find('.list-item').first());
-    }
-    this.$selectedItem.trigger('click');
-    this.$entriesEl.focus();
-  }
-
-  isArrowDown(event) {
-    return (event.keyCode || event.which) === ARROWDOWN;
-  }
-
-  isArrowUp(event) {
-    return (event.keyCode || event.which) === ARROWUP;
-  }
-
-  navigateUpAndDown(event) {
-    let arrowKeys = [ARROWUP, ARROWDOWN];
-    let key = event.keyCode || event.which;
-    if (arrowKeys.indexOf(key) > -1) {
-      event.preventDefault();
-      this.onKeyDownHandler(event);
-      return false;
-    }
-    return true;
-  }
-
-  onKeyDownHandler(event) {
-    if (this.isArrowDown(event)) {
-      if (this.$selectedItem.next().length > 0) {
-        this.setSelectedListItem(this.$selectedItem.next());
-      } else {
-        /* if last, jump to next group */
-        let $group = this.$selectedItem.closest('.query-group');
-        if ($group.next().length > 0) {
-          this.setSelectedListItem(
-            $group.next().find('.list-item').first()
-          );
-        }
-      }
-    } else if (this.isArrowUp(event)) {
-      if (this.$selectedItem.prev().length > 0) {
-        this.setSelectedListItem(this.$selectedItem.prev());
-      } else {
-        /* if first, jump to prev group */
-        let $group = this.$selectedItem.closest('.query-group');
-        if ($group.prev().length > 0) {
-          this.setSelectedListItem(
-            $group.prev().find('.list-item').last()
-          );
-        }
-      }
-    }
-  }
-
-  onSelectListItem(event) {
-    this.setSelectedListItem($(event.currentTarget));
-  }
-
-  dateAsGroupKey(date) {
-    return moment(date).format(this.groupKeyFormat);
-  }
-
-  setSelectedListItem($listItem) {
-    if (this.$selectedItem) {
-      this.$selectedItem.removeClass('selected');
-    }
-    $listItem.addClass('selected');
-    this.$selectedItem = $listItem;
-
-    this.$selectedItem[0].scrollIntoView({block: 'center'});
-
-    if (this.onSelectedChangeHandler) {
-      this.onSelectedChangeHandler(this.$selectedItem.data('entrydata'));
-    }
-  }
-
-  addEntry(entry) {
-    /* Add the entry in respective date group in descending sorted order. */
-    let groups = this.$entriesEl.find('.query-group');
-    let groupsKeys = $.map(groups, group => {
-      return $(group).attr('data-key');
-    });
-    let entryGroupKey = this.dateAsGroupKey(entry.start_time);
-    let groupIdx = _.indexOf(groupsKeys, entryGroupKey);
-
-    let $groupEl = null;
-    /* if no groups present */
-    if (groups.length == 0) {
-      $groupEl = new QueryHistoryEntryDateGroup(
-        entry.start_time,
-        entryGroupKey
-      ).render();
-      this.$entriesEl.prepend($groupEl);
-    } else if (groupIdx < 0 && groups.length != 0) {
-      /* if groups are present, but this is a new group */
-      $groupEl = new QueryHistoryEntryDateGroup(
-        entry.start_time,
-        entryGroupKey
-      ).render();
-
-      let i=0;
-      while(i<groupsKeys.length){
-        if(entryGroupKey > groupsKeys[i]) {
-          $groupEl.insertBefore(groups[i]);
-          break;
-        }
-        i++;
-      }
-      if(i == groupsKeys.length) {
-        this.$entriesEl.append($groupEl);
-      }
-    } else if (groupIdx >= 0) {
-      /* if the group is present */
-      $groupEl = $(groups[groupIdx]);
-    }
-
-    let newItem = new QueryHistoryItem(entry);
-    newItem.onClick(this.setSelectedListItem.bind(this));
-    newItem.render(this.is_pgadmin_queries_shown);
-
-    if (!_.isUndefined($groupEl)){
-      let entries = $groupEl.find('.query-entries').find('.list-item');
-      let i=0;
-      if(entries.length > 0)
-      {
-        while(i<entries.length){
-          if(newItem.$el.attr('data-key') > $(entries[i]).attr('data-key')) {
-            $(newItem.$el).insertBefore(entries[i]);
-            break;
-          }else{
-            $(newItem.$el).insertAfter(entries[i]);
-          }
-          i++;
-        }
-      } else{
-        $groupEl.find('.query-entries').append(newItem.$el);
-      }
-    }
-    this.setSelectedListItem(newItem.$el);
-  }
-
-  toggleGeneratedQueries() {
-    this.$el.find('.pgadmin-query-history-entry').each(function() {
-      $(this).toggleClass('d-none');
-    });
-    this.is_pgadmin_queries_shown = !this.is_pgadmin_queries_shown;
-  }
-
-  render() {
-    let self = this;
-    self.$el = $(`
-        <div class="toggle-and-history-container">
-            <div class="query-history-toggle">
-                <label class="control-label" for="generated-queries-toggle">
-                    ` + gettext('Show queries generated internally by pgAdmin?') + `
-                </label>
-                <input id="generated-queries-toggle" type="checkbox"
-                  class="pgadmin-controls" data-style="quick"
-                  data-size="mini" data-on="` + gettext('Yes') + '" data-off="' + gettext('No') + `"
-                  data-onstyle="success" data-offstyle="ternary" checked>
-            </div>
-            <div id='query_list' class='query-history' tabindex='0'></div>
-        </div>
-    `);
-
-    self.$entriesEl = self.$el.find('#query_list');
-    self.$entriesEl.on('keydown', this.navigateUpAndDown.bind(this));
-
-    self.is_pgadmin_queries_shown = true;
-
-    self.$el.find('#generated-queries-toggle').bootstrapToggle().change(
-      function() {
-        self.toggleGeneratedQueries();
-      }
-    );
-
-    self.parentNode.empty().append(self.$el);
-  }
-}
diff --git a/web/pgadmin/static/js/sqleditor/history/query_sources.js b/web/pgadmin/static/js/sqleditor/history/query_sources.js
deleted file mode 100644
index a3e3a3792..000000000
--- a/web/pgadmin/static/js/sqleditor/history/query_sources.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-/* This file contains the source of the queries in the history and their
-   respective icons css classes */
-
-export const QuerySources = {
-  EXECUTE: {
-    ICON_CSS_CLASS: 'fa fa-play',
-  },
-  EXPLAIN: {
-    ICON_CSS_CLASS: 'fa fa-hand-pointer',
-  },
-  EXPLAIN_ANALYZE: {
-    ICON_CSS_CLASS: 'fa fa-list-alt',
-  },
-  COMMIT: {
-    ICON_CSS_CLASS: 'pg-font-icon icon-commit',
-  },
-  ROLLBACK: {
-    ICON_CSS_CLASS: 'pg-font-icon icon-rollback',
-  },
-  SAVE_DATA: {
-    ICON_CSS_CLASS: 'pg-font-icon icon-save_data_changes',
-  },
-  VIEW_DATA: {
-    ICON_CSS_CLASS: 'pg-font-icon picon-view_data',
-  },
-};
diff --git a/web/pgadmin/static/js/sqleditor/macro.js b/web/pgadmin/static/js/sqleditor/macro.js
deleted file mode 100644
index ff470c9d5..000000000
--- a/web/pgadmin/static/js/sqleditor/macro.js
+++ /dev/null
@@ -1,341 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import url_for from 'sources/url_for';
-import $ from 'jquery';
-import _ from 'underscore';
-import Alertify from 'pgadmin.alertifyjs';
-import pgAdmin from 'sources/pgadmin';
-import Backform from 'pgadmin.backform';
-import macroModel from 'sources/sqleditor/macro_model';
-import axios from 'axios';
-import Notify from '../../../static/js/helpers/Notifier';
-
-let MacroDialog = {
-  'dialog': function(handler) {
-    let title = gettext('Manage Macros');
-
-    // Check the alertify dialog already loaded then delete it to clear
-    // the cache
-    if (Alertify.macroDialog) {
-      delete Alertify.macroDialog;
-    }
-
-    // Create Dialog
-    Alertify.dialog('macroDialog', function factory() {
-      let $container = $('<div class=\'macro_dialog\'></div>');
-      return {
-        main: function() {
-          this.set('title', '<i class="fa fa-scroll sql-icon-lg" aria-hidden="true" role="img"></i> ' + gettext('Manage Macros'));
-        },
-        build: function() {
-          this.elements.content.appendChild($container.get(0));
-          Alertify.pgDialogBuild.apply(this);
-        },
-        setup: function() {
-          return {
-            buttons: [{
-              text: '',
-              key: 112,
-              className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
-              attrs: {
-                name: 'dialog_help',
-                type: 'button',
-                label: gettext('Help'),
-                'aria-label': gettext('Help'),
-                url: url_for('help.static', {
-                  'filename': 'query_tool.html',
-                }),
-              },
-            }, {
-              text: gettext('Cancel'),
-              key: 27,
-              className: 'btn btn-secondary fa fa-times pg-alertify-button',
-              'data-btn-name': 'cancel',
-            }, {
-              text: gettext('Save'),
-              className: 'btn btn-primary fa fa-save pg-alertify-button',
-              'data-btn-name': 'ok',
-            }],
-            focus: {
-              element: function(){
-              /*
-              returning false will focus nothing, but it will make
-              contents behind the modal accessible via Tab key,
-              so focus the dialog itself instead.
-              */
-                return $(this.elements.dialog).find('.header-icon-cell button')[0];
-              },
-              select: true,
-            },
-            // Set options for dialog
-            options: {
-              title: title,
-              //disable both padding and overflow control.
-              padding: !1,
-              overflow: !1,
-              model: 0,
-              resizable: true,
-              maximizable: true,
-              pinnable: false,
-              closableByDimmer: false,
-              modal: false,
-              autoReset: false,
-            },
-          };
-        },
-        hooks: {
-        // triggered when the dialog is closed
-          onclose: function() {
-            if (this.view) {
-              this.macroCollectionModel.stopSession();
-              this.view.model.stopSession();
-              this.view.remove({
-                data: true,
-                internal: true,
-                silent: true,
-              });
-            }
-          },
-        },
-        prepare: function() {
-          let self = this;
-          $container.html('');
-          // Status bar
-          this.statusBar = $(
-            '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
-          '  <div class="error-in-footer"> ' +
-          '    <div class="d-flex px-2 py-1"> ' +
-          '      <div class="pr-2"> ' +
-          '        <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
-          '      </div> ' +
-          '      <div class="alert-text" role="alert"></div> ' +
-          '    </div> ' +
-          '  </div> ' +
-          '</div>').appendTo($container);
-
-          // To show progress on filter Saving/Updating on AJAX
-          this.showFilterProgress = $(
-            `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
-              <div class="pg-sp-content">
-                  <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
-                  <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
-              </div>
-          </div>`
-          ).appendTo($container);
-
-          self.macroCollectionModel = macroModel(handler.transId);
-
-          let fields = Backform.generateViewSchema(
-            null, self.macroCollectionModel, 'edit', null, null, true
-          );
-
-          let ManageMacroDialog = Backform.Dialog.extend({
-            template: {
-              'panel': _.template(
-                '<div role="tabpanel" tabindex="-1" class="tab-pane <%=label%> <%=tabPanelCodeClass%> pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12 fade" id="<%=cId%>"></div>'
-              ),
-            },
-            render: function() {
-              this.cleanup();
-
-              var  m = this.model,
-                controls = this.controls,
-                tmpls = this.template,
-                dialog_obj = this,
-                idx = (this.tabIndex * 100),
-                evalF = function(f, d, model) {
-                  return (_.isFunction(f) ? !!f.apply(d, [model]) : !!f);
-                };
-
-              this.$el
-                .empty()
-                .attr('role', 'tabpanel')
-                .attr('class', _.result(this, 'tabPanelClassName'));
-              m.panelEl = this.$el;
-
-              var tabContent = $('<div class="tab-content pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12 macro-tab"></div>')
-                .appendTo(this.$el);
-
-              _.each(this.schema, function(o) {
-                idx++;
-                if (!o.version_compatible || !evalF(o.visible, o, m)) {
-                  return;
-                }
-                var el = $((tmpls['panel'])(_.extend(o, {
-                  'tabIndex': idx,
-                  'tabPanelCodeClass': o.tabPanelCodeClass ? o.tabPanelCodeClass : '',
-                })))
-                  .appendTo(tabContent)
-                  .removeClass('collapse').addClass('collapse');
-
-                o.fields.each(function(f) {
-                  var cntr = new(f.get('control'))({
-                    field: f,
-                    model: m,
-                    dialog: dialog_obj,
-                    tabIndex: idx,
-                  });
-                  el.append(cntr.render().$el);
-                  controls.push(cntr);
-                });
-              });
-
-              tabContent.find('.tab-pane').first().addClass('active show');
-
-              return this;
-            },
-          });
-
-          let view = self.view = new ManageMacroDialog({
-            el: '<div></div>',
-            model: self.macroCollectionModel,
-            schema: fields,
-          });
-
-          self.macroCollectionModel.fetch({
-            success: function() {
-
-              // We got the latest attributes of the object. Render the view
-              // now.
-              $container.append(self.view.render().$el);
-              self.__internal.buttons[2].element.disabled = true;
-
-
-              // Enable/disable save button and show/hide statusbar based on session
-              self.view.listenTo(self.view.model, 'pgadmin-session:start', function() {
-                self.view.listenTo(self.view.model, 'pgadmin-session:invalid', function(msg) {
-                  self.statusBar.removeClass('d-none');
-                  $(self.statusBar.find('.alert-text')).html(msg);
-                  // Disable Okay button
-                  self.__internal.buttons[2].element.disabled = true;
-                });
-
-                view.listenTo(self.view.model, 'pgadmin-session:valid', function() {
-                  self.statusBar.addClass('d-none');
-                  $(self.statusBar.find('.alert-text')).html('');
-                  // Enable Okay button
-                  self.__internal.buttons[2].element.disabled = false;
-                });
-              });
-
-              view.listenTo(self.view.model, 'pgadmin-session:stop', function() {
-                view.stopListening(self.view.model, 'pgadmin-session:invalid');
-                view.stopListening(self.view.model, 'pgadmin-session:valid');
-              });
-
-              // Starts monitoring changes to model
-              self.view.model.startNewSession();
-
-            }});
-
-          $(this.elements.body.childNodes[0]).addClass(
-            'alertify_tools_dialog_properties obj_properties'
-          );
-
-
-
-        },
-        // Callback functions when click on the buttons of the Alertify dialogs
-        callback: function(e) {
-          let self = this;
-
-          if (e.button.element.name == 'dialog_help') {
-            e.cancel = true;
-            pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
-              null, null);
-          } else if (e.button['data-btn-name'] === 'ok') {
-            e.cancel = true; // Do not close dialog
-
-            let data = self.view.model.get('macro').toJSON(true);
-
-            if (data == undefined || data == null) {
-              self.close();
-              return;
-            }
-
-            axios.put(
-              url_for('sqleditor.set_macros', {
-                'trans_id': handler.transId,
-              }),
-              data
-            ).then(function (result) {
-            // Hide Progress ...
-              $(
-                self.showFilterProgress[0]
-              ).addClass('d-none');
-
-              let macroResponse = result;
-
-              if (macroResponse.status) {
-                setTimeout(
-                  function() {
-                    // Update Macro Menu
-                    let macros = self.view.model.get('macro').toJSON().filter(m => m.name !== undefined || m.name !== null);
-                    handler.macros = macros;
-                    var str = `
-                      <li>
-                          <a class="dropdown-item" id="btn-manage-macros" href="#" tabindex="0">
-                              <span> ${gettext('Manage Macros...')} </span>
-                          </a>
-                      </li>`;
-
-                    let macro_list_tmpl = '';
-                    _.each(macros, function(m) {
-                      if (m.name) {
-                        macro_list_tmpl += `<li>
-                        <a class="dropdown-item btn-macro" data-macro-id="${m.id}" href="#" tabindex="0">
-                            <span> ${_.escape(m.name)} </span>
-                            <span> (${m.key_label}) </span>
-                        </a>
-                       </li>`;
-                      }
-                    });
-
-                    if (macro_list_tmpl.length > 0) str += '<li class="dropdown-divider"></li>' + macro_list_tmpl;
-                    $($.find('div.btn-group.mr-1.user_macros ul.dropdown-menu')).html($(str));
-
-                    self.close(); // Close the dialog now
-                    Notify.success(gettext('Macro updated successfully'));
-                  }, 10
-                );
-              } else {
-                Notify.alert(
-                  gettext('Validation Error'),
-                  macroResponse.result
-                );
-              }
-
-            }).catch(function (error) {
-            // Hide Progress ...
-              $(
-                self.showFilterProgress[0]
-              ).addClass('d-none');
-
-              setTimeout(
-                function() {
-                  Notify.error(error.response.data.errormsg);
-                }, 10
-              );
-            });
-
-          } else {
-            self.close();
-          }
-        },
-      };
-    });
-
-    Alertify.macroDialog(title).resizeTo(pgAdmin.Browser.stdW.calc(pgAdmin.Browser.stdW.lg),
-      pgAdmin.Browser.stdH.calc(pgAdmin.Browser.stdH.lg));
-  },
-};
-
-module.exports = MacroDialog;
diff --git a/web/pgadmin/static/js/sqleditor/macro_model.js b/web/pgadmin/static/js/sqleditor/macro_model.js
deleted file mode 100644
index a9c316e68..000000000
--- a/web/pgadmin/static/js/sqleditor/macro_model.js
+++ /dev/null
@@ -1,225 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import pgAdmin from 'sources/pgadmin';
-import Backform from 'pgadmin.backform';
-import Backgrid from 'pgadmin.backgrid';
-import url_for from 'sources/url_for';
-import $ from 'jquery';
-import _ from 'underscore';
-import Notify from '../helpers/Notifier';
-
-export default function macroModel(transId) {
-
-  let MacroModel = pgAdmin.Browser.DataModel.extend({
-    idAttribute: 'id',
-    defaults: {
-      id: undefined,
-      key: undefined,
-      name: undefined,
-      sql: undefined,
-      key_label: undefined,
-    },
-    schema: [{
-      id: 'key_label',
-      name: 'key_label',
-      label: gettext('Key'),
-      type: 'text',
-      cell: 'string',
-      editable: false,
-      cellHeaderClasses: 'width_percent_10',
-      headerCell: Backgrid.Extension.CustomHeaderCell,
-      disabled: false,
-    }, {
-      id: 'name',
-      name: 'name',
-      label: gettext('Name'),
-      cell: 'string',
-      type: 'text',
-      editable: true,
-      cellHeaderClasses: 'width_percent_20',
-      headerCell: Backgrid.Extension.CustomHeaderCell,
-      disabled: false,
-    }, {
-      id: 'sql',
-      name: 'sql',
-      label: gettext('SQL'),
-      cell: Backgrid.Extension.SqlCell,
-      type: 'multiline',
-      control: Backform.SqlCodeControl,
-      editable: true,
-      cellHeaderClasses: 'width_percent_70',
-      headerCell: Backgrid.Extension.CustomHeaderCell,
-      disabled: false,
-    },
-    ],
-    validate: function() {
-      let msg = null;
-      this.errorModel.clear();
-      if (_.isEmpty(this.get('name')) &&  !(_.isEmpty(this.get('sql')))) {
-        msg = gettext('Please enter macro name.');
-        this.errorModel.set('name', msg);
-        return msg;
-      } else if (_.isEmpty(this.get('sql')) &&  !(_.isEmpty(this.get('name')))) {
-        msg = gettext('Please enter macro sql.');
-        this.errorModel.set('sql', msg);
-        return msg;
-      }
-      return null;
-    },
-  });
-
-  let MacroCollectionModel = pgAdmin.Browser.DataModel.extend({
-    defaults: {
-      macro: undefined,
-    },
-    urlRoot: url_for('sqleditor.get_macros', {'trans_id': transId}),
-    schema: [{
-      id: 'macro',
-      name: 'macro',
-      label: gettext('Macros'),
-      model: MacroModel,
-      editable: true,
-      type: 'collection',
-      control: Backform.SubNodeCollectionControl.extend({
-        showGridControl: function(data) {
-          var self = this,
-            gridBody = $('<div class=\'pgadmin-control-group backgrid form-group pg-el-12 object subnode\'></div>');
-
-          var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype,
-            gridSchema = Backform.generateGridColumnsFromModel(
-              data.node_info, subnode, this.field.get('mode'), data.columns, data.schema_node
-            );
-
-          // Clean up existing grid if any (in case of re-render)
-          if (self.grid) {
-            self.grid.remove();
-          }
-
-          // Set visibility of Add button
-          if (data.disabled || data.canAdd == false) {
-            $(gridBody).find('button.add').remove();
-          }
-
-          // Insert Clear Cell into Grid
-          gridSchema.columns.unshift({
-            name: 'pg-backform-clear',
-            label: '<i aria-label="' + gettext('Clear row') + '" class="fa fa-eraser" title="' + gettext('Clear row') + '"></i>',
-            cell: Backgrid.Extension.ClearCell,
-            editable: false,
-            cell_priority: -1,
-            sortable: false,
-            headerCell: Backgrid.Extension.CustomHeaderCell.extend({
-              className: 'header-icon-cell',
-              events: {
-                'click': 'clearrAll',
-              },
-              clearrAll: function(e) {
-                e.preventDefault();
-                var that = this;
-                // We will check if row is deletable or not
-
-                let macros = that.collection.toJSON().filter(m => m.name !== undefined && m.name !== null);
-
-                if (macros.length > 0) {
-                  Notify.confirm(
-                    gettext('Clear All Rows'),
-                    gettext('Are you sure you wish to clear all rows?'),
-                    function() {
-                      _.each(that.collection.toJSON(), function(m) {
-                        that.collection.get(m.id).set({'name': null, 'sql': null});
-                      });
-                    },
-                    function() {
-                      return true;
-                    }
-                  );
-                }
-              },
-              render: function() {
-                this.$el.empty();
-                var column = this.column;
-                var label = $('<button type="button" title="' + gettext('Clear row') + '" aria-label="Clear row" aria-expanded="false" tabindex="0">').html(column.get('label')).append('<span class=\'sort-caret\' aria-hidden=\'true\'></span>');
-
-                this.$el.append(label);
-                this.$el.addClass(column.get('name'));
-                this.$el.addClass(column.get('direction'));
-                this.$el.attr('role', 'columnheader');
-                this.$el.attr('aria-label', 'columnheader');
-                this.$el.attr('alt', 'columnheader');
-                this.delegateEvents();
-                return this;
-
-              },
-            }),
-          });
-
-
-          var collection = self.model.get(data.name);
-
-          if (!collection) {
-            collection = new(pgAdmin.Browser.Node.Collection)(null, {
-              handler: self.model.handler || self.model,
-              model: data.model,
-              top: self.model.top || self.model,
-              silent: true,
-            });
-            self.model.set(data.name, collection, {
-              silent: true,
-            });
-          }
-
-          var cellEditing = function(args) {
-            var ctx = this,
-              cell = args[0];
-            // Search for any other rows which are open.
-            this.each(function(m) {
-              // Check if row which we are about to close is not current row.
-              if (cell.model != m) {
-                var idx = ctx.indexOf(m);
-                if (idx > -1) {
-                  var row = grid.body.rows[idx],
-                    rowEditCell = row.$el.find('.subnode-edit-in-process').parent();
-                  // Only close row if it's open.
-                  if (rowEditCell.length > 0) {
-                    var event = new Event('click');
-                    rowEditCell[0].dispatchEvent(event);
-                  }
-                }
-              }
-            });
-          };
-          // Listen for any row which is about to enter in edit mode.
-          collection.on('enteringEditMode', cellEditing, collection);
-
-          // Initialize a new Grid instance
-          var grid = self.grid = new Backgrid.Grid({
-            columns: gridSchema.columns,
-            collection: collection,
-            row: this.row,
-            className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
-          });
-
-          // Render subNode grid
-          var subNodeGrid = grid.render().$el;
-
-          return gridBody.append(subNodeGrid);
-        },
-      }),
-      columns: ['key_label', 'name', 'sql'],
-      visible: true,
-    }],
-    validate: function() {
-      return null;
-    },
-  });
-
-  return new MacroCollectionModel();
-}
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
deleted file mode 100644
index 6752ec3d4..000000000
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog.js
+++ /dev/null
@@ -1,289 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import url_for from 'sources/url_for';
-import $ from 'jquery';
-import Alertify from 'pgadmin.alertifyjs';
-import pgAdmin from 'sources/pgadmin';
-import Backform from 'pgadmin.backform';
-import newConnectionDialogModel from 'sources/sqleditor/new_connection_dialog_model';
-import Notify from 'static/js/helpers/Notifier';
-
-
-let NewConnectionDialog = {
-  'dialog': function(handler, reconnect, preferences) {
-    let url = url_for('sqleditor.get_new_connection_data', {
-      'sid': handler.url_params.sid,
-      'sgid': handler.url_params.sgid,
-    });
-
-    if(reconnect) {
-      url += '?connect=1';
-    }
-
-    let title = gettext('Connect to server');
-
-    $.ajax({
-      url: url,
-      headers: {
-        'Cache-Control' : 'no-cache',
-      },
-    }).done(function (res) {
-      let response = res.data.result;
-      response.database_list = [];
-      response.user_list = [];
-      if (Alertify.newConnectionDialog) {
-        delete Alertify.newConnectionDialog;
-      }
-
-      // Create Dialog
-      Alertify.dialog('newConnectionDialog', function factory() {
-        let $container = $('<div class=\'new-connection-dialog\'></div>');
-        return {
-          main: function(message) {
-            this.msg = message;
-          },
-          build: function() {
-            this.elements.content.appendChild($container.get(0));
-            Alertify.pgDialogBuild.apply(this);
-          },
-          setup: function(){
-            return {
-              buttons: [
-                {
-                  text: '',
-                  key: 112,
-                  className: 'btn btn-primary-icon pull-left fa fa-question pg-alertify-icon-button',
-                  attrs: {
-                    name: 'dialog_help',
-                    type: 'button',
-                    label: gettext('Help'),
-                    'aria-label': gettext('Help'),
-                    url: url_for('help.static', {
-                      'filename': 'query_tool.html',
-                    }),
-                  },
-                },
-                {
-                  text: gettext('Cancel'),
-                  key: 27,
-                  className: 'btn btn-secondary fa fa-times pg-alertify-button',
-                  'data-btn-name': 'cancel',
-                }, {
-                  text: gettext('OK'),
-                  key: 13,
-                  className: 'btn btn-primary fa fa-check pg-alertify-button',
-                  'data-btn-name': 'ok',
-                },
-              ],
-              focus: {
-                element: function(){
-                /*
-                returning false will focus nothing, but it will make
-                contents behind the modal accessible via Tab key,
-                so focus the dialog itself instead.
-                */
-                  return $(this.elements.dialog).find('.new-connection-dialog .nav-link.active.show');
-                },
-                select: true,
-              },
-              // Set options for dialog
-              options: {
-                title: title,
-                //disable both padding and overflow control.
-                padding: !1,
-                overflow: !1,
-                model: 0,
-                resizable: true,
-                maximizable: false,
-                pinnable: false,
-                closableByDimmer: false,
-                modal: false,
-                autoReset: false,
-                closable: false,
-              },
-            };
-          },
-          prepare: function() {
-            let self = this;
-            $container.html('');
-            // Disable Ok button
-            this.__internal.buttons[2].element.disabled = true;
-
-            // Status bar
-            this.statusBar = $(
-              '<div class=\'pg-prop-status-bar pg-el-xs-12 d-none\'>' +
-            '  <div class="error-in-footer"> ' +
-            '    <div class="d-flex px-2 py-1"> ' +
-            '      <div class="pr-2"> ' +
-            '        <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> ' +
-            '      </div> ' +
-            '      <div class="alert-text" role="alert"></div> ' +
-            '    </div> ' +
-            '  </div> ' +
-            '</div>').appendTo($container);
-
-            // To show progress on filter Saving/Updating on AJAX
-            this.showNewConnectionProgress = $(
-              `<div id="show_filter_progress" class="pg-sp-container sql-editor-busy-fetching d-none">
-                <div class="pg-sp-content">
-                    <div class="row"><div class="col-12 pg-sp-icon sql-editor-busy-icon"></div></div>
-                    <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">` + gettext('Loading data...') + `</div></div>
-                </div>
-            </div>`
-            ).appendTo($container);
-            $(
-              self.showNewConnectionProgress[0]
-            ).removeClass('d-none');
-
-            self.newConnCollectionModel = newConnectionDialogModel(response, handler.url_params.sgid, handler.url_params.sid, handler, self);
-            let fields = Backform.generateViewSchema(null, self.newConnCollectionModel, 'create', null, null, true);
-
-            let view = this.view = new Backform.Dialog({
-              el: '<div></div>',
-              model: self.newConnCollectionModel,
-              schema: fields,
-            });
-
-            $(this.elements.body.childNodes[0]).addClass(
-              'alertify_tools_dialog_properties obj_properties'
-            );
-
-            $container.append(view.render().$el);
-
-            // Enable/disable save button and show/hide statusbar based on session
-            view.listenTo(view.model, 'pgadmin-session:start', function() {
-              view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
-                self.statusBar.removeClass('d-none');
-                $(self.statusBar.find('.alert-text')).html(msg);
-                // Disable Okay button
-                if(self.__internal){
-                  self.__internal.buttons[2].element.disabled = true;
-                }
-              });
-
-              view.listenTo(view.model, 'pgadmin-session:valid', function() {
-                self.statusBar.addClass('d-none');
-                $(self.statusBar.find('.alert-text')).html('');
-                // Enable Okay button
-                if(self.__internal) {
-                  self.__internal.buttons[2].element.disabled = false;
-                }
-              });
-            });
-
-            view.listenTo(view.model, 'pgadmin-session:stop', function() {
-              view.stopListening(view.model, 'pgadmin-session:invalid');
-              view.stopListening(view.model, 'pgadmin-session:valid');
-            });
-
-            // Starts monitoring changes to model
-            view.model.startNewSession();
-
-            // Hide Progress ...
-            $(
-              self.showNewConnectionProgress[0]
-            ).addClass('d-none');
-          },
-          get_title: function(qt_title_placeholder, selected_database_name, newConnCollectionModel, tmpResponse) {
-            qt_title_placeholder = qt_title_placeholder.replace(new RegExp('%DATABASE%'), selected_database_name);
-            if(newConnCollectionModel['role']) {
-              qt_title_placeholder = qt_title_placeholder.replace(new RegExp('%USERNAME%'), newConnCollectionModel['role']);
-            } else {
-              qt_title_placeholder = qt_title_placeholder.replace(new RegExp('%USERNAME%'), newConnCollectionModel['user']);
-            }
-
-            qt_title_placeholder = qt_title_placeholder.replace(new RegExp('%SERVER%'), tmpResponse.server_name);
-            return qt_title_placeholder;
-          },
-          callback: function(e) {
-            let self = this;
-            if (e.button.element.name == 'dialog_help') {
-              e.cancel = true;
-              pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
-                null, null);
-            } else if (e.button['data-btn-name'] === 'ok') {
-              e.cancel = true; // Do not close dialog
-              let newConnCollectionModel = this.newConnCollectionModel.toJSON();
-
-              let selected_database_name = null;
-              response.database_list.forEach(function(data){
-                if(newConnCollectionModel['database'] == data['value']) {
-                  selected_database_name = data['label'];
-                  return false;
-                }
-              });
-              let tab_title = '';
-              var qt_title_placeholder = preferences['qt_tab_title_placeholder'];
-
-              tab_title = this.get_title(qt_title_placeholder, selected_database_name, newConnCollectionModel, response);
-              let qt_default_placeholder = pgAdmin['qt_default_placeholder'];
-              let conn_title = this.get_title(qt_default_placeholder, selected_database_name, newConnCollectionModel, response);
-
-              if(!newConnCollectionModel['role']) {
-                newConnCollectionModel['role'] = null;
-              }
-
-              let is_create_connection = true;
-
-              handler.gridView.connection_list.forEach(function(connection_data) {
-                if(parseInt(connection_data['server']) == newConnCollectionModel['server']
-                && parseInt(connection_data['database']) == newConnCollectionModel['database']
-                && connection_data['user'] == newConnCollectionModel['user'] && connection_data['role'] == newConnCollectionModel['role']) {
-                  is_create_connection = false;
-                  return false;
-                }
-              });
-              if(!is_create_connection) {
-                let errmsg = 'Connection with this configuration already present.';
-                Notify.info(errmsg);
-              } else {
-                let connection_details = {
-                  'server_group': handler.gridView.handler.url_params.sgid,
-                  'server': newConnCollectionModel['server'],
-                  'database': newConnCollectionModel['database'],
-                  'title': _.escape(tab_title),
-                  'conn_title': _.escape(conn_title),
-                  'user': newConnCollectionModel['user'],
-                  'role': newConnCollectionModel['role'],
-                  'server_name': _.escape(response.server_name),
-                  'database_name': _.escape(selected_database_name),
-                  'password': response.password,
-                  'is_selected': false,
-                };
-                handler.gridView.on_change_connection(connection_details, self);
-              }
-            } else {
-              Alertify.newConnectionDialog().destroy();
-            }
-          },
-        };
-      });
-
-      setTimeout(function(){
-        Alertify.newConnectionDialog('Connect to server.').resizeTo(pgAdmin.Browser.stdW.md,pgAdmin.Browser.stdH.md);
-      }, 500);
-    }).fail(function() {
-      Notify.alert().setting({
-        'title': gettext('Connection lost'),
-        'label':gettext('OK'),
-        'message': gettext('Connection to the server has been lost.'),
-        'onok': function(){
-          //Close the window after connection is lost
-          window.close();
-        },
-      }).show();
-    });
-
-  },
-
-};
-
-module.exports = NewConnectionDialog;
diff --git a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js b/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
deleted file mode 100644
index 6ee34ff86..000000000
--- a/web/pgadmin/static/js/sqleditor/new_connection_dialog_model.js
+++ /dev/null
@@ -1,477 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import _ from 'underscore';
-import $ from 'jquery';
-import pgAdmin from 'sources/pgadmin';
-import Backform from 'pgadmin.backform';
-import url_for from 'sources/url_for';
-import alertify from 'pgadmin.alertifyjs';
-import Notify from '../helpers/Notifier';
-
-
-export default function newConnectionDialogModel(response, sgid, sid, handler, conn_self) {
-
-  let server_name = '';
-  let database_name = '';
-
-  let NewConnectionSelect2Control = Backform.Select2Control.extend({
-    fetchData: function(){
-      let self = this;
-      let url = self.field.get('url');
-
-      url = url_for(url, {
-        'sid': self.model.attributes.server,
-        'sgid': sgid,
-      });
-
-      $.ajax({
-        url: url,
-        headers: {
-          'Cache-Control' : 'no-cache',
-        },
-      }).done(function (res) {
-        var transform = self.field.get('transform');
-        if(res.data.status){
-          let data = res.data.result.data;
-
-          if (transform && _.isFunction(transform)) {
-            self.field.set('options', transform.bind(self, data));
-          } else {
-            self.field.set('options', data);
-          }
-        } else {
-          if (transform && _.isFunction(transform)) {
-            self.field.set('options', transform.bind(self, []));
-          } else {
-            self.field.set('options', []);
-          }
-        }
-        Backform.Select2Control.prototype.render.apply(self, arguments);
-      }).fail(function(e){
-        let msg = '';
-        if(e.status == 404) {
-          msg = 'Unable to find url.';
-        } else {
-          msg = e.responseJSON.errormsg;
-        }
-        Notify.error(msg);
-      });
-    },
-    render: function() {
-      this.fetchData();
-      return Backform.Select2Control.prototype.render.apply(this, arguments);
-    },
-    onChange: function() {
-      Backform.Select2Control.prototype.onChange.apply(this, arguments);
-    },
-  });
-
-  var formatNode = function (opt) {
-    if (!opt.id) {
-      return opt.text;
-    }
-
-    var optimage = $(opt.element).data('image');
-
-    if (!optimage) {
-      return opt.text;
-    } else {
-      return $('<span></span>').append(
-        $('<span></span>', {
-          class: 'wcTabIcon ' + optimage,
-        })
-      ).append($('<span></span>').text(opt.text));
-    }
-  };
-
-  let newConnectionModel = pgAdmin.Browser.DataModel.extend({
-    idAttribute: 'name',
-    defaults: {
-      server: parseInt(sid),
-      database: null,
-      user: null,
-      server_name: server_name,
-      database_name: database_name,
-    },
-    schema: [
-      {
-        id: 'server',
-        name: 'server',
-        label: gettext('Server'),
-        type: 'text',
-        editable: true,
-        disabled: false,
-        select2: {
-          allowClear: false,
-          width: 'style',
-          templateResult: formatNode,
-          templateSelection: formatNode,
-        },
-        events: {
-          'focus select': 'clearInvalid',
-          'keydown :input': 'processTab',
-          'select2:select': 'onSelect',
-          'select2:selecting': 'beforeSelect',
-          'select2:clear': 'onChange',
-        },
-        transform: function(data) {
-          let group_template_options = [];
-          for (let key in data) {
-            if (data.hasOwnProperty(key)) {
-              group_template_options.push({'group': key, 'optval': data[key]});
-            }
-          }
-          return group_template_options;
-        },
-        control: Backform.Select2Control.extend({
-          template: _.template([
-            '<% if(label == false) {} else {%>',
-            '  <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
-            '<% }%>',
-            '<div class="<%=controlsClassName%>">',
-            ' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
-            '  name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
-            '  <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
-            '  <%=select2.first_empty ? " <option></option>" : ""%>',
-            '  <% for (var i=0; i < options.length; i++) {%>',
-            '   <% if (options[i].group) { %>',
-            '     <% var group = options[i].group; %>',
-            '     <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
-            '      <optgroup label="<%=group%>">',
-            '      <% for (var subindex=0; subindex < option_length; subindex++) {%>',
-            '        <% var option = options[i].optval[subindex]; %>',
-            '        <option ',
-            '        <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
-            '        <% if (option.connected) { %> data-connected=connected <%}%>',
-            '        value=<%- formatter.fromRaw(option.value) %>',
-            '        <% if (option.selected) {%>selected="selected"<%} else {%>',
-            '        <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
-            '        <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
-            '        <%}%>',
-            '        <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
-            '      <%}%>',
-            '      </optgroup>',
-            '     <%}%>',
-            '   <%} else {%>',
-            '     <% var option = options[i]; %>',
-            '     <option ',
-            '     <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
-            '     <% if (option.connected) { %> data-connected=connected <%}%>',
-            '     value=<%- formatter.fromRaw(option.value) %>',
-            '     <% if (option.selected) {%>selected="selected"<%} else {%>',
-            '     <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
-            '     <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
-            '     <%}%>',
-            '     <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
-            '   <%}%>',
-            '  <%}%>',
-            ' </select>',
-            ' <% if (helpMessage && helpMessage.length) { %>',
-            ' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
-            ' <% } %>',
-            '</div>',
-          ].join('\n')),
-          beforeSelect: function() {
-            var selVal = arguments[0].params.args.data.id;
-
-            if(this.field.get('connect') && this.$el.find('option[value="'+selVal+'"]').attr('data-connected') !== 'connected') {
-              this.field.get('connect').apply(this, [selVal, this.changeIcon.bind(this)]);
-            } else {
-              $(this.$sel).trigger('change');
-              setTimeout(function(){ this.onChange.apply(this); }.bind(this), 200);
-            }
-          },
-          changeIcon: function(data) {
-            let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
-              selSpan = this.$el.find('option:selected');
-
-            if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
-              let icon = (data.icon) ? data.icon : 'icon-pg';
-              span.removeClass('icon-server-not-connected');
-              span.addClass(icon);
-              span.attr('data-connected', 'connected');
-
-              selSpan.data().image = icon;
-              selSpan.attr('data-connected', 'connected');
-              alertify.connectServer().destroy();
-              this.onChange.apply(this);
-            }
-            else if (span.hasClass('icon-database-not-connected')) {
-              let icon = (data.icon) ? data.icon : 'pg-icon-database';
-
-              span.removeClass('icon-database-not-connected');
-              span.addClass(icon);
-              span.attr('data-connected', 'connected');
-
-              selSpan.removeClass('icon-database-not-connected');
-              selSpan.data().image = icon;
-              selSpan.attr('data-connected', 'connected');
-              alertify.connectServer().destroy();
-              this.onChange.apply(this);
-            }
-          },
-          connect: function(self) {
-            let local_self = self;
-
-            if(alertify.connectServer) {
-              delete alertify.connectServer;
-            }
-
-            alertify.dialog('connectServer', function factory() {
-              return {
-                main: function(
-                  title, message, server_id, submit_password=true, connect_server=null,
-                ) {
-                  this.set('title', title);
-                  this.message = message;
-                  this.server_id = server_id;
-                  this.submit_password = submit_password;
-                  this.connect_server = connect_server;
-                },
-                setup:function() {
-                  return {
-                    buttons:[{
-                      text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
-                      key: 27,
-                    },{
-                      text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
-                    }],
-                    focus: {element: '#password', select: true},
-                    options: {
-                      modal: 0, resizable: false, maximizable: false, pinnable: false,
-                    },
-                  };
-                },
-                build:function() { /*This is intentional (SonarQube)*/
-                },
-                prepare:function() {
-                  this.setContent(this.message);
-                },
-                callback: function(closeEvent) {
-                  var loadingDiv = $('#show_filter_progress');
-                  if (closeEvent.button.text == gettext('OK')) {
-                    if(this.submit_password) {
-                      var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
-
-                      loadingDiv.removeClass('d-none');
-                      $.ajax({
-                        type: 'POST',
-                        timeout: 30000,
-                        url: _url,
-                        data: $('#frmPassword').serialize(),
-                      })
-                        .done(function(res) {
-
-                          local_self.model.attributes.database = null;
-                          local_self.changeIcon(res.data);
-                          local_self.model.attributes.user = null;
-                          local_self.model.attributes.role = null;
-                          Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
-                          Object.keys(response.server_list).forEach(key => {
-                            response.server_list[key].forEach(option => {
-                              if (option.value == local_self.getValueFromDOM()) {
-                                response.server_name = option.label;
-                              }
-                            });
-                          });
-
-                          loadingDiv.addClass('d-none');
-                          alertify.connectServer().destroy();
-                        })
-                        .fail(function(xhr) {
-                          loadingDiv.addClass('d-none');
-                          alertify.connectServer().destroy();
-                          alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
-                        });
-                    } else {
-                      if(Object.keys(this.connect_server).length > 0) {
-                        this.connect_server['password'] = $('#password').val();
-                        this.connect_server['is_selected'] = false;
-                        handler.gridView.on_change_connection(this.connect_server, conn_self, false);
-                        loadingDiv = $('#fetching_data');
-                        loadingDiv.addClass('d-none');
-                      } else {
-                        response.password = $('#password').val();
-                        loadingDiv.addClass('d-none');
-                      }
-                    }
-                  } else {
-                    local_self.model.attributes.database = null;
-                    local_self.model.attributes.user = null;
-                    local_self.model.attributes.role = null;
-                    Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
-                    loadingDiv.addClass('d-none');
-                    alertify.connectServer().destroy();
-
-                  }
-                  closeEvent.close = true;
-                },
-              };
-            });
-          },
-          render: function() {
-            let self = this;
-            self.connect(self);
-            Object.keys(response.server_list).forEach(key => {
-              response.server_list[key].forEach(option => {
-                if (option.value == parseInt(sid)) {
-                  response.server_name = option.label;
-                }
-              });
-            });
-            var transform = self.field.get('transform') || self.defaults.transform;
-            if (transform && _.isFunction(transform)) {
-              self.field.set('options', transform.bind(self, response.server_list));
-            } else {
-              self.field.set('options', response.server_list);
-            }
-            return Backform.Select2Control.prototype.render.apply(self, arguments);
-          },
-          onChange: function() {
-            this.model.attributes.database = null;
-            this.model.attributes.user = null;
-            let self = this;
-            self.connect(self);
-
-            let url = url_for('sqleditor.connect_server', {
-              'sid': self.getValueFromDOM(),
-              'usr': self.model.attributes.user,
-            });
-            var loadingDiv = $('#show_filter_progress');
-            loadingDiv.removeClass('d-none');
-            $.ajax({
-              url: url,
-              type: 'POST',
-              headers: {
-                'Cache-Control' : 'no-cache',
-              },
-            }).done(function () {
-              Backform.Select2Control.prototype.onChange.apply(self, arguments);
-              Object.keys(response.server_list).forEach(key => {
-                response.server_list[key].forEach(option => {
-                  if (option.value == self.getValueFromDOM()) {
-                    response.server_name = option.label;
-                  }
-                });
-              });
-              loadingDiv.addClass('d-none');
-            }).fail(function(xhr){
-              loadingDiv.addClass('d-none');
-              alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
-            });
-
-          },
-        }),
-      },
-      {
-        id: 'database',
-        name: 'database',
-        label: gettext('Database'),
-        type: 'text',
-        editable: true,
-        disabled: function(m) {
-          let self_local = this;
-          if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
-            && m.get('server') !== '') {
-            setTimeout(function() {
-              if(self_local.options.length) {
-                self_local.options.forEach(db => {
-                  if (db.selected) {
-                    m.set('database', db.value);
-                  }
-                });
-              }
-            }, 10);
-            return false;
-          }
-
-          return true;
-        },
-        deps: ['server'],
-        url: 'sqleditor.get_new_connection_database',
-        select2: {
-          allowClear: false,
-          width: '100%',
-          first_empty: true,
-          select_first: false,
-        },
-        extraClasses:['new-connection-dialog-style'],
-        control: NewConnectionSelect2Control,
-        transform: function(data) {
-          response.database_list = data;
-          return data;
-        },
-      },
-      {
-        id: 'user',
-        name: 'user',
-        label: gettext('User'),
-        type: 'text',
-        editable: true,
-        deps: ['server'],
-        select2: {
-          allowClear: false,
-          width: '100%',
-        },
-        control: NewConnectionSelect2Control,
-        url: 'sqleditor.get_new_connection_user',
-        disabled: function(m) {
-          let self_local = this;
-          if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
-            && m.get('server') !== '') {
-            setTimeout(function() {
-              if(self_local.options.length) {
-                self_local.options.forEach(user => {
-                  if (user.selected) {
-                    m.set('user', user.value);
-                  }
-                });
-              }
-            }, 10);
-            return false;
-          }
-          return true;
-        },
-      },{
-        id: 'role',
-        name: 'role',
-        label: gettext('Role'),
-        type: 'text',
-        editable: true,
-        deps: ['server'],
-        select2: {
-          allowClear: false,
-          width: '100%',
-          first_empty: true,
-        },
-        control: NewConnectionSelect2Control,
-        url: 'sqleditor.get_new_connection_role',
-        disabled: false,
-      },
-    ],
-    validate: function() {
-      let msg = null;
-      this.errorModel.clear();
-      if(_.isUndefined(this.get('database')) || _.isNull(this.get('database'))){
-        msg = gettext('Please select database');
-        this.errorModel.set('database', msg);
-        return msg;
-      } else if(_.isUndefined(this.get('database')) || _.isUndefined(this.get('user'))|| _.isNull(this.get('user'))) {
-        msg = gettext('Please select user');
-        this.errorModel.set('user', msg);
-        return msg;
-      }
-      return null;
-    },
-  });
-
-  return new newConnectionModel();
-}
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_actions.js b/web/pgadmin/static/js/sqleditor/query_tool_actions.js
deleted file mode 100644
index 379c8c4d1..000000000
--- a/web/pgadmin/static/js/sqleditor/query_tool_actions.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import $ from 'jquery';
-import pgWindow from 'sources/window';
-
-let queryToolActions = {
-  _verbose: function () {
-    return !$('.explain-verbose').hasClass('visibility-hidden');
-  },
-
-  _costsEnabled: function () {
-    return !$('.explain-costs').hasClass('visibility-hidden');
-  },
-
-  _buffers: function () {
-    return !$('.explain-buffers').hasClass('visibility-hidden');
-  },
-
-  _timing: function () {
-    return !$('.explain-timing').hasClass('visibility-hidden');
-  },
-
-  _summary: function () {
-    return !$('.explain-summary').hasClass('visibility-hidden');
-  },
-
-  _settings: function () {
-    return !$('.explain-settings').hasClass('visibility-hidden');
-  },
-
-  _clearMessageTab: function () {
-    $('.sql-editor-message').html('');
-  },
-
-  executeMacro: function (sqlEditorController, MacroId) {
-    this._clearMessageTab();
-    sqlEditorController.check_data_changes_to_execute_query(null, false, MacroId);
-  },
-
-  executeQuery: function (sqlEditorController) {
-    this._clearMessageTab();
-    sqlEditorController.check_data_changes_to_execute_query();
-  },
-
-  explainAnalyze: function (sqlEditorController) {
-    const explainObject = {
-      format: 'json',
-      analyze: true,
-      verbose: this._verbose(),
-      costs: this._costsEnabled(),
-      buffers: this._buffers(),
-      timing: this._timing(),
-      summary: this._summary(),
-      settings: this._settings(),
-    };
-    this._clearMessageTab();
-    sqlEditorController.check_data_changes_to_execute_query(explainObject);
-  },
-
-  explain: function (sqlEditorController) {
-    const explainObject = {
-      format: 'json',
-      analyze: false,
-      verbose: this._verbose(),
-      costs: this._costsEnabled(),
-      buffers: false,
-      timing: false,
-      summary: this._summary(),
-      settings: this._settings(),
-    };
-    this._clearMessageTab();
-    sqlEditorController.check_data_changes_to_execute_query(explainObject);
-  },
-
-  download: function (sqlEditorController) {
-
-    let extension = sqlEditorController.preferences.csv_field_separator === ',' ? '.csv': '.txt';
-    let filename = 'data-' + new Date().getTime() + extension;
-
-    if (!sqlEditorController.is_query_tool) {
-      filename = sqlEditorController.table_name + extension;
-    }
-
-    sqlEditorController.trigger_csv_download(filename);
-  },
-
-  commentBlockCode: function (sqlEditorController) {
-    let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-    if (!codeMirrorObj.getValue()) return;
-
-    codeMirrorObj.toggleComment(codeMirrorObj.getCursor(true), codeMirrorObj.getCursor(false));
-  },
-
-  commentLineCode: function (sqlEditorController) {
-    let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-    if (!codeMirrorObj.getValue()) return;
-
-    codeMirrorObj.lineComment(codeMirrorObj.getCursor(true),
-      codeMirrorObj.getCursor(false),
-      {lineComment: '--'}
-    );
-  },
-
-  uncommentLineCode: function (sqlEditorController) {
-    let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-    if (!codeMirrorObj.getValue()) return;
-
-    codeMirrorObj.uncomment(codeMirrorObj.getCursor(true),
-      codeMirrorObj.getCursor(false),
-      {lineComment: '--'}
-    );
-  },
-
-  formatSql: function (sqlEditorController) {
-    sqlEditorController.gridView.on_format_sql();
-  },
-
-  focusOut: function () {
-    document.activeElement.blur();
-    pgWindow.document.activeElement.blur();
-  },
-
-  toggleCaseOfSelectedText: function (sqlEditorController) {
-    let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-    let selectedText = codeMirrorObj.getSelection();
-
-    if (!selectedText) return;
-
-    if (selectedText === selectedText.toUpperCase()) {
-      codeMirrorObj.replaceSelection(selectedText.toLowerCase());
-    } else {
-      codeMirrorObj.replaceSelection(selectedText.toUpperCase());
-    }
-  },
-
-  executeCommit: function (sqlEditorController) {
-    var self = this;
-    sqlEditorController.special_sql = 'COMMIT;';
-    self.executeQuery(sqlEditorController);
-  },
-
-  executeRollback: function (sqlEditorController) {
-    var self = this;
-    sqlEditorController.special_sql = 'ROLLBACK;';
-    self.executeQuery(sqlEditorController);
-  },
-
-  saveDataChanges: function (sqlEditorController) {
-    sqlEditorController.close_on_save = false;
-    sqlEditorController.save_data();
-  },
-
-  openQueryTool: function (sqlEditorController) {
-    sqlEditorController.gridView.handler.trigger(
-      'pgadmin-sqleditor:button:show_query_tool',
-      sqlEditorController.gridView,
-      sqlEditorController.gridView.handler
-    );
-  },
-};
-
-module.exports = queryToolActions;
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_http_error_handler.js b/web/pgadmin/static/js/sqleditor/query_tool_http_error_handler.js
deleted file mode 100644
index c5060434e..000000000
--- a/web/pgadmin/static/js/sqleditor/query_tool_http_error_handler.js
+++ /dev/null
@@ -1,78 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-import gettext from 'sources/gettext';
-
-export function httpResponseRequiresNewTransaction(xhr) {
-  /* If responseJSON is undefined then it could be object of
-   * axios(Promise HTTP) response, so we should check accordingly.
-   */
-  if (xhr.responseJSON === undefined && xhr.data !== undefined) {
-    return xhr.status === 404 && xhr.data &&
-              xhr.data.info &&
-              xhr.data.info === 'DATAGRID_TRANSACTION_REQUIRED';
-  }
-
-  return xhr.status === 404 && xhr.responseJSON &&
-    xhr.responseJSON.info &&
-    xhr.responseJSON.info === 'DATAGRID_TRANSACTION_REQUIRED';
-}
-
-// Allow us to redirect to login dialog and if required then re-initiate the transaction
-export function handleLoginRequiredAndTransactionRequired(
-  pgAdmin, handler, exception, stateToSave, stateParameters, checkTransaction
-) {
-  stateParameters = stateParameters && stateParameters.length > 0 ? stateParameters : [];
-  if (pgAdmin.Browser.UserManagement.isPgaLoginRequired(exception)) {
-    if (stateToSave) {
-      handler.saveState(stateToSave, stateParameters);
-    }
-    return pgAdmin.Browser.UserManagement.pgaLogin();
-  }
-
-  if(checkTransaction && httpResponseRequiresNewTransaction(exception)) {
-    if (stateToSave) {
-      handler.saveState(stateToSave, stateParameters);
-    }
-    return handler.initTransaction();
-  }
-}
-
-// Allow us to handle the AJAX error from Query tool
-export function handleQueryToolAjaxError(
-  pgAdmin, handler, exception, stateToSave, stateParameters, checkTransaction
-) {
-  if (exception.readyState === 0) {
-    return gettext('Not connected to the server or the connection to the server has been closed.');
-  }
-
-  handleLoginRequiredAndTransactionRequired(
-    pgAdmin, handler, exception, stateToSave, stateParameters, checkTransaction
-  );
-
-  let msg = exception.responseText;
-  if (exception.responseJSON !== undefined) {
-    if(exception.responseJSON.errormsg !== undefined) {
-      msg = exception.responseJSON.errormsg;
-    }
-
-    if(exception.status === 503 && exception.responseJSON.info !== undefined &&
-        exception.responseJSON.info == 'CONNECTION_LOST') {
-      // We will display re-connect dialog, no need to display error message again
-      msg = null;
-      setTimeout(function() {
-        if (stateToSave) {
-          handler.saveState(stateToSave, stateParameters);
-        }
-        handler.handle_connection_lost(false, exception);
-      });
-    }
-  }
-
-  return msg;
-}
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_notifications.js b/web/pgadmin/static/js/sqleditor/query_tool_notifications.js
deleted file mode 100644
index ddf3ad852..000000000
--- a/web/pgadmin/static/js/sqleditor/query_tool_notifications.js
+++ /dev/null
@@ -1,133 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import gettext from 'sources/gettext';
-import Backgrid from 'pgadmin.backgrid';
-import Backbone from 'backbone';
-import Notify from '../helpers/Notifier';
-
-let NotificationsModel = Backbone.Model.extend({
-  defaults: {
-    recorded_time: undefined,
-    event: undefined,
-    pid: undefined,
-    payload: undefined,
-  },
-  schema: [{
-    id: 'recorded_time',
-    label: gettext('Recorded time'),
-    cell: 'string',
-    type: 'text',
-    editable: false,
-    cellHeaderClasses: 'width_percent_20',
-    headerCell: Backgrid.Extension.CustomHeaderCell,
-  },{
-    id: 'channel',
-    label: gettext('Event'),
-    cell: 'string',
-    type: 'text',
-    editable: false,
-    cellHeaderClasses: 'width_percent_20',
-    headerCell: Backgrid.Extension.CustomHeaderCell,
-  },{
-    id: 'pid',
-    label: gettext('Process ID'),
-    cell: 'string',
-    type: 'text',
-    editable: false,
-    cellHeaderClasses: 'width_percent_20',
-    headerCell: Backgrid.Extension.CustomHeaderCell,
-  },{
-    id: 'payload',
-    label: gettext('Payload'),
-    cell: 'string',
-    type: 'text',
-    editable: false,
-    cellHeaderClasses: 'width_percent_40',
-    headerCell: Backgrid.Extension.CustomHeaderCell,
-  }],
-});
-
-let NotificationCollection = Backbone.Collection.extend({
-  model: NotificationsModel,
-});
-
-let queryToolNotifications = {
-
-  collection: null,
-
-  /* This function is responsible to create and render the
-   * new backgrid for the notification tab.
-   */
-  renderNotificationsGrid: function(notifications_panel) {
-    if (!queryToolNotifications.collection)
-      queryToolNotifications.collection = new NotificationCollection();
-
-    let gridCols = [{
-      name: 'recorded_time',
-      label: gettext('Recorded time'),
-      type: 'text',
-      editable: false,
-      cell: 'string',
-    }, {
-      name: 'channel',
-      label: gettext('Event'),
-      type: 'text',
-      editable: false,
-      cell: 'string',
-    }, {
-      name: 'pid',
-      label: gettext('Process ID'),
-      type: 'text',
-      editable: false,
-      cell: 'string',
-    }, {
-      name: 'payload',
-      label: gettext('Payload'),
-      type: 'text',
-      editable: false,
-      cell: 'string',
-    }];
-
-    // Set up the grid
-    let notifications_grid = new Backgrid.Grid({
-      emptyText: gettext('No data found'),
-      columns: gridCols,
-      collection: queryToolNotifications.collection,
-      className: 'backgrid presentation table table-bordered table-hover table-noouter-border table-bottom-border',
-    });
-
-    // Render the grid
-    if (notifications_panel)
-      notifications_panel.$container.find('.sql-editor-notifications').append(notifications_grid.render().el);
-  },
-
-  // This function is used to raise notify messages and update the
-  // notification grid.
-  updateNotifications: function(notify_messages) {
-    if (notify_messages != null && notify_messages.length > 0) {
-      for (let i in notify_messages) {
-        let notify_msg = '';
-        if (notify_messages[i].payload != '') {
-          notify_msg = gettext('Asynchronous notification "%s" with payload "%s" received from server process with PID %s', notify_messages[i].channel, notify_messages[i].payload, notify_messages[i].pid);
-        }
-        else {
-          notify_msg = gettext('Asynchronous notification "%s" received from server process with PID %s', notify_messages[i].channel, notify_messages[i].pid);
-        }
-
-        Notify.info(notify_msg);
-      }
-
-      // Add notify messages to the collection.
-      queryToolNotifications.collection.add(notify_messages);
-    }
-  },
-};
-
-module.exports = queryToolNotifications;
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
deleted file mode 100644
index d4cf5ec6a..000000000
--- a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
+++ /dev/null
@@ -1,241 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {shortcut_key, shortcut_accesskey_title, shortcut_title}
-  from 'sources/keyboard_shortcuts';
-import * as SqlEditorUtils from 'sources/sqleditor_utils';
-import $ from 'jquery';
-import gettext from 'sources/gettext';
-
-function updateUIPreferences(sqlEditor) {
-  let $el = sqlEditor.$el,
-    preferences = sqlEditor.preferences;
-
-  if(sqlEditor.handler.slickgrid) {
-    sqlEditor.handler.slickgrid.CSVOptions = {
-      quoting: sqlEditor.preferences.results_grid_quoting,
-      quote_char: sqlEditor.preferences.results_grid_quote_char,
-      field_separator: sqlEditor.preferences.results_grid_field_separator,
-    };
-  }
-
-  /* Accessed using accesskey direct w/o ctrl,atl,shift */
-  $el.find('#btn-load-file')
-    .attr('title', shortcut_accesskey_title(gettext('Open File'),preferences.btn_open_file))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Open File'),preferences.btn_open_file))
-    .attr('accesskey', shortcut_key(preferences.btn_open_file));
-
-  $el.find('#btn-save-file')
-    .attr('title', shortcut_accesskey_title(gettext('Save File'),preferences.btn_save_file))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Save File'),preferences.btn_save_file))
-    .attr('accesskey', shortcut_key(preferences.btn_save_file));
-
-  $el.find('#btn-find-menu-dropdown')
-    .attr('title', shortcut_accesskey_title(gettext('Find'),preferences.btn_find_options))
-    .attr('aria-label',shortcut_accesskey_title(gettext('Find'),preferences.btn_find_options))
-    .attr('accesskey', shortcut_key(preferences.btn_find_options));
-
-  $el.find('#btn-copy-row')
-    .attr('title', shortcut_accesskey_title(gettext('Copy'),preferences.btn_copy_row))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Copy'),preferences.btn_copy_row))
-    .attr('accesskey', shortcut_key(preferences.btn_copy_row));
-
-  $el.find('#btn-paste-row')
-    .attr('title', shortcut_accesskey_title(gettext('Paste'),preferences.btn_paste_row))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Paste'),preferences.btn_paste_row))
-    .attr('accesskey', shortcut_key(preferences.btn_paste_row));
-
-  $el.find('#btn-delete-row')
-    .attr('title', shortcut_accesskey_title(gettext('Delete'),preferences.btn_delete_row))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Delete'),preferences.btn_delete_row))
-    .attr('accesskey', shortcut_key(preferences.btn_delete_row));
-
-  $el.find('#btn-filter')
-    .attr('title', shortcut_accesskey_title(gettext('Filter'),preferences.btn_filter_dialog))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Filter'),preferences.btn_filter_dialog))
-    .attr('accesskey', shortcut_key(preferences.btn_filter_dialog));
-
-  $el.find('#btn-filter-dropdown')
-    .attr('title', shortcut_accesskey_title(gettext('Filter options'),preferences.btn_filter_options))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Filter options'),preferences.btn_filter_options))
-    .attr('accesskey', shortcut_key(preferences.btn_filter_options));
-
-  $el.find('#btn-rows-limit')
-    .attr('title', shortcut_accesskey_title(gettext('Rows limit'),preferences.btn_rows_limit))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Rows limit'),preferences.btn_rows_limit))
-    .attr('accesskey', shortcut_key(preferences.btn_rows_limit));
-
-  $el.find('#btn-query-dropdown')
-    .attr('title', shortcut_accesskey_title(gettext('Execute options'),preferences.btn_execute_options))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Execute options'),preferences.btn_execute_options))
-    .attr('accesskey', shortcut_key(preferences.btn_execute_options));
-
-  $el.find('#btn-cancel-query')
-    .attr('title', shortcut_accesskey_title(gettext('Cancel query'),preferences.btn_cancel_query))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Cancel query'),preferences.btn_cancel_query))
-    .attr('accesskey', shortcut_key(preferences.btn_cancel_query));
-
-  $el.find('#btn-clear-dropdown')
-    .attr('title', shortcut_accesskey_title(gettext('Clear'),preferences.btn_clear_options))
-    .attr('aria-label', shortcut_accesskey_title(gettext('Clear'),preferences.btn_clear_options))
-    .attr('accesskey', shortcut_key(preferences.btn_clear_options));
-
-  $el.find('#btn-conn-status')
-    .attr('accesskey', shortcut_key(preferences.btn_conn_status))
-    .find('i')
-    .attr('title',
-      shortcut_accesskey_title(gettext('Connection status (click for details)'),
-        preferences.btn_conn_status));
-
-  /* Accessed using ctrl,atl,shift and key */
-  $el.find('#btn-flash')
-    .attr('title',
-      shortcut_title(gettext('Execute/Refresh'),preferences.execute_query))
-    .attr('aria-label',
-      shortcut_title(gettext('Execute/Refresh'),preferences.execute_query));
-
-  $el.find('#btn-explain')
-    .attr('title',
-      shortcut_title(gettext('Explain'),preferences.explain_query))
-    .attr('aria-label',
-      shortcut_title(gettext('Explain'),preferences.explain_query));
-
-  $el.find('#btn-explain-analyze')
-    .attr('title',
-      shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query))
-    .attr('aria-label',
-      shortcut_title(gettext('Explain Analyze'),preferences.explain_analyze_query));
-
-  $el.find('#btn-save-results-to-file')
-    .attr('title',
-      shortcut_title(gettext('Save results to file'),preferences.download_results))
-    .attr('aria-label',
-      shortcut_title(gettext('Save results to file'),preferences.download_results));
-
-  $el.find('#btn-save-data')
-    .attr('title',
-      shortcut_title(gettext('Save Data Changes'),preferences.save_data))
-    .attr('aria-label',
-      shortcut_title(gettext('Save Data Changes'),preferences.save_data));
-
-  $el.find('#btn-commit')
-    .attr('title',
-      shortcut_title(gettext('Commit'),preferences.commit_transaction))
-    .attr('aria-label',
-      shortcut_title(gettext('Commit'),preferences.commit_transaction));
-
-  $el.find('#btn-rollback')
-    .attr('title',
-      shortcut_title(gettext('Rollback'),preferences.rollback_transaction))
-    .attr('aria-label',
-      shortcut_title(gettext('Rollback'),preferences.rollback_transaction));
-
-  $el.find('#btn-show-query-tool')
-    .attr('title',
-      shortcut_title(gettext('Query tool'),preferences.show_query_tool))
-    .attr('aria-label',
-      shortcut_title(gettext('Query tool'),preferences.show_query_tool));
-
-  /* Set explain options on query editor */
-  if (preferences.explain_verbose){
-    $el.find('.explain-verbose').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-verbose').addClass('visibility-hidden');
-  }
-
-  if (preferences.explain_costs){
-    $el.find('.explain-costs').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-costs').addClass('visibility-hidden');
-  }
-
-  if (preferences.explain_buffers){
-    $el.find('.explain-buffers').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-buffers').addClass('visibility-hidden');
-  }
-
-  if (preferences.explain_timing) {
-    $el.find('.explain-timing').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-timing').addClass('visibility-hidden');
-  }
-
-  if (preferences.explain_summary) {
-    $el.find('.explain-summary').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-summary').addClass('visibility-hidden');
-  }
-
-  if (preferences.explain_settings) {
-    $el.find('.explain-settings').removeClass('visibility-hidden');
-  }
-  else {
-    $el.find('.explain-settings').addClass('visibility-hidden');
-  }
-
-  /* Connection status check */
-  /* remove the status checker if present */
-  if(sqlEditor.connIntervalId != null) {
-    clearInterval(sqlEditor.connIntervalId);
-    sqlEditor.connIntervalId = null;
-  }
-  if (preferences.connection_status) {
-    let $conn_status = $el.find('#btn-conn-status'),
-      $status_el = $conn_status.find('i');
-    $conn_status.popover();
-
-    $conn_status.removeClass('connection-status-hide');
-
-    // To set initial connection
-    SqlEditorUtils.fetchConnectionStatus(sqlEditor.handler, $conn_status, $status_el);
-
-    // Calling it again in specified interval
-    sqlEditor.connIntervalId =  setInterval(
-      SqlEditorUtils.fetchConnectionStatus.bind(null, sqlEditor.handler, $conn_status, $status_el),
-      preferences.connection_status_fetch_time * 1000
-    );
-  }
-  else {
-    $el.find('#btn-conn-status').addClass('connection-status-hide');
-  }
-
-  /* Code Mirror Preferences */
-  let sql_font_size = SqlEditorUtils.calcFontSize(preferences.sql_font_size);
-  $(sqlEditor.query_tool_obj.getWrapperElement()).css('font-size', sql_font_size);
-
-  if(preferences.plain_editor_mode) {
-    sqlEditor.query_tool_obj.setOption('mode', 'text/plain');
-    /* Although not required, setting explicitly as codemirror will remove code folding only on next edit */
-    sqlEditor.query_tool_obj.setOption('foldGutter', false);
-  } else {
-    sqlEditor.query_tool_obj.setOption('mode', 'text/x-pgsql');
-    sqlEditor.query_tool_obj.setOption('foldGutter', preferences.code_folding);
-  }
-  sqlEditor.query_tool_obj.setOption('foldGutter', preferences.code_folding);
-  sqlEditor.query_tool_obj.setOption('indentWithTabs', !preferences.use_spaces);
-  sqlEditor.query_tool_obj.setOption('indentUnit', preferences.use_spaces ? preferences.tab_size : 4);
-  sqlEditor.query_tool_obj.setOption('tabSize', preferences.use_spaces ? preferences.tab_size : 4);
-  sqlEditor.query_tool_obj.setOption('lineWrapping', preferences.wrap_code);
-  sqlEditor.query_tool_obj.setOption('autoCloseBrackets', preferences.insert_pair_brackets);
-  sqlEditor.query_tool_obj.setOption('matchBrackets', preferences.brace_matching);
-  sqlEditor.query_tool_obj.refresh();
-
-  /* Render history to reflect Font size change */
-  sqlEditor.historyComponent.setEditorPref({
-    'sql_font_size' : sql_font_size,
-  });
-}
-
-export {updateUIPreferences};
diff --git a/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js b/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
deleted file mode 100644
index 075264296..000000000
--- a/web/pgadmin/static/js/sqleditor/query_txn_status_constants.js
+++ /dev/null
@@ -1,11 +0,0 @@
-/* psycopg2 transaction status constants
- * http://initd.org/psycopg/docs/extensions.html#transaction-status-constants
- */
-
-module.exports = {
-  TRANSACTION_STATUS_IDLE: 0,
-  TRANSACTION_STATUS_ACTIVE: 1,
-  TRANSACTION_STATUS_INTRANS: 2,
-  TRANSACTION_STATUS_INERROR: 3,
-  TRANSACTION_STATUS_UNKNOWN: 5,
-};
diff --git a/web/pgadmin/static/js/sqleditor_utils.js b/web/pgadmin/static/js/sqleditor_utils.js
index 663745c9f..fa63a9ab2 100644
--- a/web/pgadmin/static/js/sqleditor_utils.js
+++ b/web/pgadmin/static/js/sqleditor_utils.js
@@ -8,251 +8,15 @@
 //////////////////////////////////////////////////////////////////////////
 // This file contains common utilities functions used in sqleditor modules
 
-define(['jquery', 'underscore', 'sources/gettext', 'sources/url_for', 'pgadmin.alertifyjs'],
-  function ($, _, gettext, url_for, alertify) {
-    var sqlEditorUtils = {
-      /* Reference link http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript
-       * Modified as per requirement.
-       */
-      epicRandomString: function(length) {
-        var s = [];
-        var hexDigits = '0123456789abcdef';
-        for (var i = 0; i < 36; i++) {
-          s[i] = hexDigits.substr(
-            Math.floor(Math.random() * 0x10), 1
-          );
-        }
-        // bits 12-15 of the time_hi_and_version field to 0010
-        s[14] = '4';
-        // bits 6-7 of the clock_seq_hi_and_reserved to 01
-        s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1);
-        s[8] = s[13] = s[18] = s[23] = '-';
-
-        var uuid = s.join('');
-        return uuid.replace(/-/g, '').substr(0, length);
-      },
-
-      // Returns a unique hash for input string
-      getHash: function(input) {
-        var hash = 0, len = input.length;
-        for (var i = 0; i < len; i++) {
-          hash  = ((hash << 5) - hash) + input.charCodeAt(i);
-          hash |= 0; // to 32bit integer
-        }
-        return hash;
-      },
-      calculateColumnWidth: function (text) {
-        // Calculate column header width based on column name or type
-        // Create a temporary element with given label, append to body
-        // calculate its width and remove the element.
-        $('body').append(
-          '<span id="pg_text" style="visibility: hidden;">'+ text + '</span>'
-        );
-        var width = $('#pg_text').width() + 23;
-        $('#pg_text').remove(); // remove element
-
-        return width;
-      },
-      capitalizeFirstLetter: function (string) {
-        return string.charAt(0).toUpperCase() + string.slice(1);
-      },
-
-      // Disables arrow to change connection
-      disable_connection_dropdown: function (status) {
-        if (status){
-          $('.conn-info-dd').prop('disabled',true);
-          $('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'});
-        }else{
-          $('.conn-info-dd').prop('disabled',false);
-          $('.connection-data').css({pointerEvents: 'auto', cursor: 'pointer'});
-        }
-      },
-
-      // Status flag
-      previousStatus: null,
-
-      // This function will fetch the connection status via ajax
-      fetchConnectionStatus: function(target, $el, $status_el) {
-        // If user has switch the browser Tab or Minimized window or
-        // if wcDocker panel is not in focus then don't fire AJAX
-        var url = url_for('sqleditor.connection_status', {
-          'trans_id': target.transId,
-        });
-
-        if (document.visibilityState !== 'visible' ||
-              $el.data('panel-visible') !== 'visible' ) {
-          return;
-        }
-
-        if($status_el.hasClass('obtaining-conn')){
-          return;
-        }
-        let sqleditor_obj = target;
-
-        // Start polling..
-        $.ajax({
-          url: url,
-          method: 'GET',
-        })
-          .done(function (res) {
-            if(res && res.data) {
-              var status = res.data.status,
-                msg = res.data.message,
-                is_status_changed = false;
-
-              // Raise notify messages comes from database server.
-              sqleditor_obj.update_notifications(res.data.notifies);
-
-              // Inject CSS as required
-              switch(status) {
-              // Busy
-              case 1:
-                // if received busy status more than once then only
-                if(status == sqlEditorUtils.previousStatus &&
-                        !$status_el.hasClass('fa-hourglass-half')) {
-                  $status_el.removeClass()
-                    .addClass('fa fa-hourglass-half');
-                  is_status_changed = true;
-                }
-                break;
-                // Idle in transaction
-              case 2:
-                if(sqlEditorUtils.previousStatus != status &&
-                        !$status_el.hasClass('fa-clock')) {
-                  $status_el.removeClass()
-                    .addClass('fa fa-clock');
-                  is_status_changed = true;
-                }
-                break;
-                // Failed in transaction
-              case 3:
-                if(sqlEditorUtils.previousStatus != status &&
-                        !$status_el.hasClass('fa-exclamation-circle')) {
-                  $status_el.removeClass()
-                    .addClass('fa fa-exclamation-circle');
-                  is_status_changed = true;
-                }
-                break;
-                // Failed in transaction with unknown server side error
-              case 4:
-                if(sqlEditorUtils.previousStatus != status &&
-                        !$status_el.hasClass('fa-exclamation-triangle')) {
-                  $status_el.removeClass()
-                    .addClass('fa fa-exclamation-triangle');
-                  is_status_changed = true;
-                }
-                break;
-              default:
-                if(sqlEditorUtils.previousStatus != status &&
-                        !$status_el.hasClass('fa-query_tool_connected')) {
-                  $status_el.removeClass()
-                    .addClass('pg-font-icon icon-connected');
-                  is_status_changed = true;
-                }
-              }
-
-              sqlEditorUtils.previousStatus = status;
-              // Set bootstrap popover message
-              if(is_status_changed) {
-                $el.popover('hide');
-                $el.attr('data-content', msg);
-              }
-            } else {
-            // We come here means we did not receive expected response
-            // from server, we need to error out
-              sqlEditorUtils.previousStatus = -99;
-              msg = gettext('An unexpected error occurred - ' +
-                          'ensure you are logged into the application.');
-              $el.attr('data-content', msg);
-              if(!$status_el.hasClass('icon-disconnected')) {
-                $el.popover('hide');
-                $status_el.removeClass()
-                  .addClass('pg-icon-font icon-disconnected');
-              }
-            }
-          })
-          .fail(function (e) {
-            sqlEditorUtils.previousStatus = -1;
-            var msg = gettext('Transaction status check failed.');
-            if (e.readyState == 0) {
-              msg = gettext('Not connected to the server or the connection to ' +
-                          'the server has been closed.');
-            } else if (e.responseJSON && e.responseJSON.errormsg) {
-              msg = e.responseJSON.errormsg;
-            }
-
-            // Set bootstrap popover
-            $el.attr('data-content', msg);
-            // Add error class
-            if(!$status_el.hasClass('icon-disconnected')) {
-              $el.popover('hide');
-              $status_el.removeClass()
-                .addClass('pg-font-icon icon-disconnected');
-            }
-          });
-      },
-
-      // Updates the flag for connection status poll
-      updateConnectionStatusFlag: function(status) {
-        var $el = $('.connection_status');
-        if ($el.data('panel-visible') != status) {
-          $el.data('panel-visible', status);
-        }
-      },
-
-      calcFontSize: function(fontSize) {
-        if(fontSize) {
-          fontSize = parseFloat((Math.round(parseFloat(fontSize + 'e+2')) + 'e-2'));
-          let rounded = Number(fontSize);
-          if(rounded > 0) {
-            return rounded + 'em';
-          }
-        }
-        return '1em';
-      },
-
-      addEditableIcon: function(columnDefinition, is_editable) {
-        /* This uses Slickgrid.HeaderButtons plugin to add an icon to the
-        columns headers. Instead of a button, an icon is created */
-        let content = null;
-        if(is_editable) {
-          content = '<i class="fa fa-pencil-alt"></i>';
-        }
-        else {
-          content = '<i class="fa fa-lock"></i>';
-        }
-        let button = {
-          cssClass: 'editable-column-header-icon',
-          content: content,
-        };
-        // Check for existing buttons
-        if(!_.isUndefined(columnDefinition.header) &&
-           !_.isUndefined(columnDefinition.header.buttons)) {
-          columnDefinition.header.buttons.push(button);
-        }
-        else {
-          columnDefinition.header = {
-            buttons: [button],
-          };
-        }
-      },
-
-      isModalOpen: function(dialog_list) {
-        /* check the modals inside the sqleditor are open or not */
-        if(Array.isArray(dialog_list)) {
-          for(let d of dialog_list) {
-            try {
-              if(alertify.dialog(d) && alertify.dialog(d).isOpen())
-                return true;
-            }
-            catch (err) {
-              // Do nothing
-              console.warn(err.stack || err);
-            }
-          }
-        }
-        return false;
-      },
-    };
-    return sqlEditorUtils;
-  });
+module.exports = {
+  calcFontSize: function(fontSize) {
+    if(fontSize) {
+      fontSize = parseFloat((Math.round(parseFloat(fontSize + 'e+2')) + 'e-2'));
+      let rounded = Number(fontSize);
+      if(rounded > 0) {
+        return rounded + 'em';
+      }
+    }
+    return '1em';
+  },
+};
diff --git a/web/pgadmin/static/js/utils.js b/web/pgadmin/static/js/utils.js
index 9d6716533..2221997a2 100644
--- a/web/pgadmin/static/js/utils.js
+++ b/web/pgadmin/static/js/utils.js
@@ -302,7 +302,7 @@ export function sprintf(i_str) {
 
 // Modified ref: http://stackoverflow.com/a/1293163/2343 to suite pgAdmin.
 // This will parse a delimited string into an array of arrays.
-export function CSVToArray( strData, strDelimiter, quoteChar){
+export function CSVToArray(strData, strDelimiter, quoteChar){
   strDelimiter = strDelimiter || ',';
   quoteChar = quoteChar || '"';
 
@@ -323,6 +323,12 @@ export function CSVToArray( strData, strDelimiter, quoteChar){
   // a default empty first row.
   var arrData = [[]];
 
+  // The regex doesn't handle and skips start value if
+  // string starts with delimiter
+  if(strData.startsWith(strDelimiter)) {
+    arrData[ arrData.length - 1 ].push(null);
+  }
+
   // Create an array to hold our individual pattern
   // matching groups.
   var arrMatches = null;
diff --git a/web/pgadmin/static/scss/_codemirror.overrides.scss b/web/pgadmin/static/scss/_codemirror.overrides.scss
index fd449ccf4..14c08a5f9 100644
--- a/web/pgadmin/static/scss/_codemirror.overrides.scss
+++ b/web/pgadmin/static/scss/_codemirror.overrides.scss
@@ -167,6 +167,9 @@
       background: $sql-hint-active-bg;
       color: $sql-hint-active-fg;
     }
+    & .sqleditor-hint {
+      padding-left: 20px;
+    }
   }
 }
 
@@ -208,3 +211,5 @@
   background-color: $sql-editor-disable-bg !important;
 }
 
+
+
diff --git a/web/pgadmin/static/scss/_pgadmin.style.scss b/web/pgadmin/static/scss/_pgadmin.style.scss
index e75fa7261..7a7e89e08 100644
--- a/web/pgadmin/static/scss/_pgadmin.style.scss
+++ b/web/pgadmin/static/scss/_pgadmin.style.scss
@@ -1160,3 +1160,7 @@ select:-webkit-autofill:focus {
 .text-wrap {
   word-break: break-word;
 }
+
+.sql_textarea {
+  height: 100%;
+}
diff --git a/web/pgadmin/static/scss/resources/_default.variables.scss b/web/pgadmin/static/scss/resources/_default.variables.scss
index dbc7e12ab..9e1583ee9 100644
--- a/web/pgadmin/static/scss/resources/_default.variables.scss
+++ b/web/pgadmin/static/scss/resources/_default.variables.scss
@@ -197,7 +197,7 @@ $color-editor-comment: #a50 !default;
 $color-editor-bracket: #737373 !default;
 $color-editor-operator: $color-fg !default;
 $color-editor-foldmarker: #0000FF !default;
-$color-editor-activeline: #50B0F0 !default;
+$color-editor-activeline: #EDF9FF !default;
 $color-editor-activeline-light: #EDF9FF !default;
 $color-editor-activeline-border-color: #BCDEF3 !default;
 
diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py
deleted file mode 100644
index b3f1371b4..000000000
--- a/web/pgadmin/tools/datagrid/__init__.py
+++ /dev/null
@@ -1,617 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-"""A blueprint module implementing the datagrid frame."""
-
-import simplejson as json
-import pickle
-import random
-
-from threading import Lock
-from flask import Response, url_for, session, request, make_response
-from werkzeug.useragents import UserAgent
-from flask import current_app as app, render_template
-from flask_babel import gettext
-from flask_security import login_required, current_user
-from pgadmin.tools.sqleditor.command import ObjectRegistry, SQLFilter
-from pgadmin.tools.sqleditor import check_transaction_status
-from pgadmin.utils import PgAdminModule
-from pgadmin.utils.ajax import make_json_response, bad_request, \
-    internal_server_error, unauthorized
-
-from config import PG_DEFAULT_DRIVER, ALLOW_SAVE_PASSWORD
-from pgadmin.model import Server, User
-from pgadmin.utils.driver import get_driver
-from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost
-from pgadmin.utils.preferences import Preferences
-from pgadmin.settings import get_setting
-from pgadmin.browser.utils import underscore_unescape
-from pgadmin.utils.exception import ObjectGone
-from pgadmin.tools.sqleditor.utils.macros import get_user_macros
-from pgadmin.utils.constants import MIMETYPE_APP_JS, UNAUTH_REQ
-
-MODULE_NAME = 'datagrid'
-
-query_tool_close_session_lock = Lock()
-
-
-class DataGridModule(PgAdminModule):
-    """
-    class DataGridModule(PgAdminModule)
-
-        A module class for Edit Grid derived from PgAdminModule.
-    """
-
-    LABEL = "Data Grid"
-
-    def get_own_menuitems(self):
-        return {}
-
-    def get_own_javascripts(self):
-        return [{
-            'name': 'pgadmin.datagrid',
-            'path': url_for('datagrid.index') + "datagrid",
-            'when': None
-        }]
-
-    def get_panels(self):
-        return []
-
-    def get_exposed_url_endpoints(self):
-        """
-        Returns:
-            list: URL endpoints for backup module
-        """
-        return [
-            'datagrid.initialize_datagrid',
-            'datagrid.initialize_query_tool',
-            'datagrid.initialize_query_tool_with_did',
-            'datagrid.filter_validate',
-            'datagrid.filter',
-            'datagrid.panel',
-            'datagrid.close',
-            'datagrid.update_query_tool_connection'
-        ]
-
-    def on_logout(self, user):
-        """
-        This is a callback function when user logout from pgAdmin
-        :param user:
-        :return:
-        """
-        with query_tool_close_session_lock:
-            if 'gridData' in session:
-                for trans_id in session['gridData']:
-                    close_query_tool_session(trans_id)
-
-                # Delete all grid data from session variable
-                del session['gridData']
-
-
-blueprint = DataGridModule(MODULE_NAME, __name__, static_url_path='/static')
-
-
-@blueprint.route("/")
-@login_required
-def index():
-    return bad_request(
-        errormsg=gettext('This URL cannot be requested directly.')
-    )
-
-
-@blueprint.route("/css/datagrid.css")
-def datagrid_css():
-    return make_response(
-        render_template('datagrid/css/datagrid.css'),
-        200, {'Content-Type': 'text/css'}
-    )
-
-
-@blueprint.route("/filter", endpoint='filter')
-@login_required
-def show_filter():
-    return render_template(MODULE_NAME + '/filter.html')
-
-
-@blueprint.route(
-    '/initialize/datagrid/<int:trans_id>/<int:cmd_type>/<obj_type>/'
-    '<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
-    methods=["PUT", "POST"],
-    endpoint="initialize_datagrid"
-)
-@login_required
-def initialize_datagrid(trans_id, cmd_type, obj_type, sgid, sid, did, obj_id):
-    """
-    This method is responsible for creating an asynchronous connection.
-    After creating the connection it will instantiate and initialize
-    the object as per the object type. It will also create a unique
-    transaction id and store the information into session variable.
-
-    Args:
-        cmd_type: Contains value for which menu item is clicked.
-        obj_type: Contains type of selected object for which data grid to
-        be render
-        sgid: Server group Id
-        sid: Server Id
-        did: Database Id
-        obj_id: Id of currently selected object
-    """
-
-    if request.data:
-        filter_sql = json.loads(request.data, encoding='utf-8')
-    else:
-        filter_sql = request.args or request.form
-
-    # Create asynchronous connection using random connection id.
-    conn_id = str(random.randint(1, 9999999))
-    try:
-        manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
-        # default_conn is same connection which is created when user connect to
-        # database from tree
-        default_conn = manager.connection(did=did)
-        conn = manager.connection(did=did, conn_id=conn_id,
-                                  auto_reconnect=False,
-                                  use_binary_placeholder=True,
-                                  array_to_string=True)
-    except (ConnectionLost, SSHTunnelConnectionLost):
-        raise
-    except Exception as e:
-        app.logger.error(e)
-        return internal_server_error(errormsg=str(e))
-
-    status, msg = default_conn.connect()
-    if not status:
-        app.logger.error(msg)
-        return internal_server_error(errormsg=str(msg))
-
-    status, msg = conn.connect()
-    if not status:
-        app.logger.error(msg)
-        return internal_server_error(errormsg=str(msg))
-    try:
-        # if object type is partition then it is nothing but a table.
-        if obj_type == 'partition':
-            obj_type = 'table'
-
-        # Get the object as per the object type
-        command_obj = ObjectRegistry.get_object(
-            obj_type, conn_id=conn_id, sgid=sgid, sid=sid,
-            did=did, obj_id=obj_id, cmd_type=cmd_type,
-            sql_filter=filter_sql
-        )
-    except ObjectGone:
-        raise
-    except Exception as e:
-        app.logger.error(e)
-        return internal_server_error(errormsg=str(e))
-
-    if 'gridData' not in session:
-        sql_grid_data = dict()
-    else:
-        sql_grid_data = session['gridData']
-
-    # Use pickle to store the command object which will be used later by the
-    # sql grid module.
-    sql_grid_data[str(trans_id)] = {
-        # -1 specify the highest protocol version available
-        'command_obj': pickle.dumps(command_obj, -1)
-    }
-
-    # Store the grid dictionary into the session variable
-    session['gridData'] = sql_grid_data
-
-    return make_json_response(
-        data={
-            'conn_id': conn_id
-        }
-    )
-
-
-@blueprint.route(
-    '/panel/<int:trans_id>',
-    methods=["POST"],
-    endpoint='panel'
-)
-def panel(trans_id):
-    """
-    This method calls index.html to render the data grid.
-
-    Args:
-        trans_id: unique transaction id
-    """
-
-    url_params = None
-    if request.args:
-        url_params = {k: v for k, v in request.args.items()}
-
-    if request.form:
-        url_params['title'] = request.form['title']
-        if 'sql_filter' in request.form:
-            url_params['sql_filter'] = request.form['sql_filter']
-        if 'query_url' in request.form:
-            url_params['query_url'] = request.form['query_url']
-
-    # We need client OS information to render correct Keyboard shortcuts
-    user_agent = UserAgent(request.headers.get('User-Agent'))
-
-    """
-    Animations and transitions are not automatically GPU accelerated and by
-    default use browser's slow rendering engine. We need to set 'translate3d'
-    value of '-webkit-transform' property in order to use GPU. After applying
-    this property under linux, Webkit calculates wrong position of the
-    elements so panel contents are not visible. To make it work, we need to
-    explicitly set '-webkit-transform' property to 'none' for .ajs-notifier,
-    .ajs-message, .ajs-modal classes.
-
-    This issue is only with linux runtime application and observed in Query
-    tool and debugger. When we open 'Open File' dialog then whole Query tool
-    panel content is not visible though it contains HTML element in back end.
-
-    The port number should have already been set by the runtime if we're
-    running in desktop mode.
-    """
-    is_linux_platform = False
-
-    from sys import platform as _platform
-    if "linux" in _platform:
-        is_linux_platform = True
-
-    # Fetch the server details
-    bgcolor = None
-    fgcolor = None
-
-    s = Server.query.filter_by(id=url_params['sid']).first()
-    if s and s.bgcolor:
-        # If background is set to white means we do not have to change
-        # the title background else change it as per user specified
-        # background
-        if s.bgcolor != '#ffffff':
-            bgcolor = s.bgcolor
-        fgcolor = s.fgcolor or 'black'
-
-    layout = get_setting('SQLEditor/Layout')
-
-    macros = get_user_macros()
-
-    return render_template(
-        "datagrid/index.html",
-        _=gettext,
-        uniqueId=trans_id,
-        is_desktop_mode=app.PGADMIN_RUNTIME,
-        is_linux=is_linux_platform,
-        title=underscore_unescape(url_params['title']),
-        url_params=json.dumps(url_params),
-        client_platform=user_agent.platform,
-        bgcolor=bgcolor,
-        fgcolor=fgcolor,
-        layout=layout,
-        requirejs=True,
-        basejs=True,
-        macros=macros
-    )
-
-
-@blueprint.route(
-    '/initialize/query_tool/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
-    methods=["POST"], endpoint='initialize_query_tool_with_did'
-)
-@blueprint.route(
-    '/initialize/query_tool/<int:trans_id>/<int:sgid>/<int:sid>',
-    methods=["POST"], endpoint='initialize_query_tool'
-)
-@login_required
-def initialize_query_tool(trans_id, sgid, sid, did=None):
-    """
-    This method is responsible for instantiating and initializing
-    the query tool object. It will also create a unique
-    transaction id and store the information into session variable.
-
-    Args:
-        sgid: Server group Id
-        sid: Server Id
-        did: Database Id
-    """
-    connect = True
-    # Read the data if present. Skipping read may cause connection
-    # reset error if data is sent from the client
-    if request.data:
-        _ = request.data
-
-    req_args = request.args
-    if ('recreate' in req_args and
-            req_args['recreate'] == '1'):
-        connect = False
-
-    is_error, errmsg, conn_id, version = _init_query_tool(trans_id, connect,
-                                                          sgid, sid, did)
-    if is_error:
-        return errmsg
-
-    return make_json_response(
-        data={
-            'connId': str(conn_id),
-            'serverVersion': version,
-        }
-    )
-
-
-def _connect(conn, **kwargs):
-    """
-    Connect the database.
-    :param conn: Connection instance.
-    :param kwargs: user, role and password data from user.
-    :return:
-    """
-    user = None
-    role = None
-    password = None
-    is_ask_password = False
-    if 'user' in kwargs and 'role' in kwargs:
-        user = kwargs['user']
-        role = kwargs['role'] if kwargs['role'] else None
-        password = kwargs['password'] if kwargs['password'] else None
-        is_ask_password = True
-    if user:
-        status, msg = conn.connect(user=user, role=role,
-                                   password=password)
-    else:
-        status, msg = conn.connect()
-
-    return status, msg, is_ask_password, user, role, password
-
-
-def _init_query_tool(trans_id, connect, sgid, sid, did, **kwargs):
-    # Create asynchronous connection using random connection id.
-    conn_id = str(random.randint(1, 9999999))
-
-    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
-
-    if did is None:
-        did = manager.did
-    try:
-        command_obj = ObjectRegistry.get_object(
-            'query_tool', conn_id=conn_id, sgid=sgid, sid=sid, did=did
-        )
-    except Exception as e:
-        app.logger.error(e)
-        return True, internal_server_error(errormsg=str(e)), '', ''
-
-    try:
-        conn = manager.connection(did=did, conn_id=conn_id,
-                                  auto_reconnect=False,
-                                  use_binary_placeholder=True,
-                                  array_to_string=True)
-
-        if connect:
-            status, msg, is_ask_password, user, role, password = _connect(
-                conn, **kwargs)
-            if not status:
-                app.logger.error(msg)
-                if is_ask_password:
-                    server = Server.query.filter_by(id=sid).first()
-                    return True, make_json_response(
-                        success=0,
-                        status=428,
-                        result=render_template(
-                            'servers/password.html',
-                            server_label=server.name,
-                            username=user,
-                            errmsg=msg,
-                            _=gettext,
-                            allow_save_password=True if
-                            ALLOW_SAVE_PASSWORD and
-                            session['allow_save_password'] else False,
-                        )
-                    ), '', ''
-                else:
-                    return True, internal_server_error(
-                        errormsg=str(msg)), '', ''
-    except (ConnectionLost, SSHTunnelConnectionLost) as e:
-        app.logger.error(e)
-        raise
-    except Exception as e:
-        app.logger.error(e)
-        return True, internal_server_error(errormsg=str(e)), '', ''
-
-    if 'gridData' not in session:
-        sql_grid_data = dict()
-    else:
-        sql_grid_data = session['gridData']
-
-    # Set the value of auto commit and auto rollback specified in Preferences
-    pref = Preferences.module('sqleditor')
-    command_obj.set_auto_commit(pref.preference('auto_commit').get())
-    command_obj.set_auto_rollback(pref.preference('auto_rollback').get())
-
-    # Use pickle to store the command object which will be used
-    # later by the sql grid module.
-    sql_grid_data[str(trans_id)] = {
-        # -1 specify the highest protocol version available
-        'command_obj': pickle.dumps(command_obj, -1)
-    }
-
-    # Store the grid dictionary into the session variable
-    session['gridData'] = sql_grid_data
-
-    return False, '', conn_id, manager.version
-
-
-@blueprint.route(
-    '/initialize/query_tool/update_connection/<int:trans_id>/'
-    '<int:sgid>/<int:sid>/<int:did>',
-    methods=["POST"], endpoint='update_query_tool_connection'
-)
-def update_query_tool_connection(trans_id, sgid, sid, did):
-    # Remove transaction Id.
-    with query_tool_close_session_lock:
-        data = json.loads(request.data, encoding='utf-8')
-
-        if 'gridData' not in session:
-            return make_json_response(data={'status': True})
-
-        grid_data = session['gridData']
-
-        # Return from the function if transaction id not found
-        if str(trans_id) not in grid_data:
-            return make_json_response(data={'status': True})
-
-        connect = True
-
-        req_args = request.args
-        if ('recreate' in req_args and
-                req_args['recreate'] == '1'):
-            connect = False
-
-        new_trans_id = str(random.randint(1, 9999999))
-        kwargs = {
-            'user': data['user'],
-            'role': data['role'] if 'role' in data else None,
-            'password': data['password'] if 'password' in data else None
-        }
-
-        is_error, errmsg, conn_id, version = _init_query_tool(
-            new_trans_id, connect, sgid, sid, did, **kwargs)
-
-        if is_error:
-            return errmsg
-        else:
-            try:
-                # Check the transaction and connection status
-                status, error_msg, conn, trans_obj, session_obj = \
-                    check_transaction_status(trans_id)
-
-                status, error_msg, new_conn, new_trans_obj, new_session_obj = \
-                    check_transaction_status(new_trans_id)
-
-                new_session_obj['primary_keys'] = session_obj[
-                    'primary_keys'] if 'primary_keys' in session_obj else None
-                new_session_obj['columns_info'] = session_obj[
-                    'columns_info'] if 'columns_info' in session_obj else None
-                new_session_obj['client_primary_key'] = session_obj[
-                    'client_primary_key'] if 'client_primary_key'\
-                                             in session_obj else None
-
-                close_query_tool_session(trans_id)
-                # Remove the information of unique transaction id from the
-                # session variable.
-                grid_data.pop(str(trans_id), None)
-                session['gridData'] = grid_data
-            except Exception as e:
-                app.logger.error(e)
-
-    return make_json_response(
-        data={
-            'connId': str(conn_id),
-            'serverVersion': version,
-            'tran_id': new_trans_id
-        }
-    )
-
-
-@blueprint.route('/close/<int:trans_id>', methods=["DELETE"], endpoint='close')
-def close(trans_id):
-    """
-    This method is used to close the asynchronous connection
-    and remove the information of unique transaction id from
-    the session variable.
-
-    Args:
-        trans_id: unique transaction id
-    """
-    with query_tool_close_session_lock:
-        if 'gridData' not in session:
-            return make_json_response(data={'status': True})
-
-        grid_data = session['gridData']
-        # Return from the function if transaction id not found
-        if str(trans_id) not in grid_data:
-            return make_json_response(data={'status': True})
-
-        try:
-            close_query_tool_session(trans_id)
-            # Remove the information of unique transaction id from the
-            # session variable.
-            grid_data.pop(str(trans_id), None)
-            session['gridData'] = grid_data
-        except Exception as e:
-            app.logger.error(e)
-            return internal_server_error(errormsg=str(e))
-
-    return make_json_response(data={'status': True})
-
-
-@blueprint.route(
-    '/filter/validate/<int:sid>/<int:did>/<int:obj_id>',
-    methods=["PUT", "POST"], endpoint='filter_validate'
-)
-@login_required
-def validate_filter(sid, did, obj_id):
-    """
-    This method is used to validate the sql filter.
-
-    Args:
-        sid: Server Id
-        did: Database Id
-        obj_id: Id of currently selected object
-    """
-    if request.data:
-        filter_sql = json.loads(request.data, encoding='utf-8')
-    else:
-        filter_sql = request.args or request.form
-
-    try:
-        # Create object of SQLFilter class
-        sql_filter_obj = SQLFilter(sid=sid, did=did, obj_id=obj_id)
-
-        # Call validate_filter method to validate the SQL.
-        status, res = sql_filter_obj.validate_filter(filter_sql)
-    except ObjectGone:
-        raise
-    except Exception as e:
-        app.logger.error(e)
-        return internal_server_error(errormsg=str(e))
-
-    return make_json_response(data={'status': status, 'result': res})
-
-
-@blueprint.route("/datagrid.js")
-@login_required
-def script():
-    """render the required javascript"""
-    return Response(
-        response=render_template("datagrid/js/datagrid.js", _=gettext),
-        status=200,
-        mimetype=MIMETYPE_APP_JS
-    )
-
-
-def close_query_tool_session(trans_id):
-    """
-    This function is used to cancel the transaction and release the connection.
-
-    :param trans_id: Transaction id
-    :return:
-    """
-    if 'gridData' in session and str(trans_id) in session['gridData']:
-        cmd_obj_str = session['gridData'][str(trans_id)]['command_obj']
-        # Use pickle.loads function to get the command object
-        cmd_obj = pickle.loads(cmd_obj_str)
-
-        # if connection id is None then no need to release the connection
-        if cmd_obj.conn_id is not None:
-            manager = get_driver(
-                PG_DEFAULT_DRIVER).connection_manager(cmd_obj.sid)
-            if manager is not None:
-                conn = manager.connection(
-                    did=cmd_obj.did, conn_id=cmd_obj.conn_id)
-
-                # Release the connection
-                if conn.connected():
-                    conn.cancel_transaction(cmd_obj.conn_id, cmd_obj.did)
-                    manager.release(did=cmd_obj.did, conn_id=cmd_obj.conn_id)
diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js
deleted file mode 100644
index 4438f8a5f..000000000
--- a/web/pgadmin/tools/datagrid/static/js/datagrid.js
+++ /dev/null
@@ -1,370 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-define('pgadmin.datagrid', [
-  'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
-  'pgadmin.alertifyjs', 'sources/pgadmin', 'bundled_codemirror',
-  'sources/sqleditor_utils', 'backbone',
-  'tools/datagrid/static/js/show_data',
-  'tools/datagrid/static/js/show_query_tool', 'pgadmin.browser.toolbar',
-  'tools/datagrid/static/js/datagrid_panel_title', 'sources/utils', 'wcdocker',
-], function(
-  gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils,
-  Backbone, showData, showQueryTool, toolBar, panelTitleFunc, commonUtils
-) {
-  // Some scripts do export their object in the window only.
-  // Generally the one, which do no have AMD support.
-  var wcDocker = window.wcDocker,
-    pgBrowser = pgAdmin.Browser;
-
-  /* Return back, this has been called more than once */
-  if (pgAdmin.DataGrid)
-    return pgAdmin.DataGrid;
-
-  pgAdmin.DataGrid =
-  _.extend(
-    {
-      init: function() {
-        if (this.initialized)
-          return;
-        this.initialized = true;
-        this.title_index = 1;
-
-
-        let self = this;
-        /* Cache may take time to load for the first time
-         * Keep trying till available
-         */
-        let cacheIntervalId = setInterval(function() {
-          if(pgBrowser.preference_version() > 0) {
-            self.preferences = pgBrowser.get_preferences_for_module('sqleditor');
-            clearInterval(cacheIntervalId);
-          }
-        },0);
-
-        pgBrowser.onPreferencesChange('sqleditor', function() {
-          self.preferences = pgBrowser.get_preferences_for_module('sqleditor');
-        });
-
-        // Define list of nodes on which view data option appears
-        var supported_nodes = [
-            'table', 'view', 'mview',
-            'foreign_table', 'catalog_object', 'partition',
-          ],
-
-          /* Enable/disable View data menu in tools based
-         * on node selected. if selected node is present
-         * in supported_nodes, menu will be enabled
-         * otherwise disabled.
-         */
-          view_menu_enabled = function(obj) {
-            var isEnabled = (() => {
-              if (!_.isUndefined(obj) && !_.isNull(obj))
-                return (_.indexOf(supported_nodes, obj._type) !== -1 ? true : false);
-              else
-                return false;
-            })();
-
-            toolBar.enable(gettext('View Data'), isEnabled);
-            toolBar.enable(gettext('Filtered Rows'), isEnabled);
-            return isEnabled;
-          },
-
-          /* Enable/disable Query tool menu in tools based
-         * on node selected. if selected node is present
-         * in unsupported_nodes, menu will be disabled
-         * otherwise enabled.
-         */
-          query_tool_menu_enabled = function(obj) {
-            var isEnabled = (() => {
-              if (!_.isUndefined(obj) && !_.isNull(obj)) {
-                if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
-                  if (obj._type == 'database' && obj.allowConn) {
-                    return true;
-                  } else if (obj._type != 'database') {
-                    return true;
-                  } else {
-                    return false;
-                  }
-                } else {
-                  return false;
-                }
-              } else {
-                return false;
-              }
-            })();
-
-            toolBar.enable(gettext('Query Tool'), isEnabled);
-            return isEnabled;
-          };
-
-        // Define the nodes on which the menus to be appear
-        var menus = [{
-          name: 'query_tool',
-          module: this,
-          applies: ['tools'],
-          callback: 'show_query_tool',
-          enable: query_tool_menu_enabled,
-          priority: 1,
-          label: gettext('Query Tool'),
-          icon: 'pg-font-icon icon-query_tool',
-          data:{
-            applies: 'tools',
-            data_disabled: gettext('Please select a database from the browser tree to access Query Tool.'),
-          },
-        }];
-
-        // Create context menu
-        for (let node_val of supported_nodes) {
-          menus.push({
-            name: 'view_all_rows_context_' + node_val,
-            node: node_val,
-            module: this,
-            data: {
-              mnuid: 3,
-            },
-            applies: ['context', 'object'],
-            callback: 'show_data_grid',
-            enable: view_menu_enabled,
-            category: 'view_data',
-            priority: 101,
-            label: gettext('All Rows'),
-          }, {
-            name: 'view_first_100_rows_context_' + node_val,
-            node: node_val,
-            module: this,
-            data: {
-              mnuid: 1,
-            },
-            applies: ['context', 'object'],
-            callback: 'show_data_grid',
-            enable: view_menu_enabled,
-            category: 'view_data',
-            priority: 102,
-            label: gettext('First 100 Rows'),
-          }, {
-            name: 'view_last_100_rows_context_' + node_val,
-            node: node_val,
-            module: this,
-            data: {
-              mnuid: 2,
-            },
-            applies: ['context', 'object'],
-            callback: 'show_data_grid',
-            enable: view_menu_enabled,
-            category: 'view_data',
-            priority: 103,
-            label: gettext('Last 100 Rows'),
-          }, {
-            name: 'view_filtered_rows_context_' + node_val,
-            node: node_val,
-            module: this,
-            data: {
-              mnuid: 4,
-            },
-            applies: ['context', 'object'],
-            callback: 'show_filtered_row',
-            enable: view_menu_enabled,
-            category: 'view_data',
-            priority: 104,
-            label: gettext('Filtered Rows...'),
-          });
-        }
-
-        pgAdmin.Browser.add_menu_category('view_data', gettext('View/Edit Data'), 100, '');
-        pgAdmin.Browser.add_menus(menus);
-
-        // Creating a new pgAdmin.Browser frame to show the data.
-        var dataGridFrameType = new pgAdmin.Browser.Frame({
-          name: 'frm_datagrid',
-          showTitle: true,
-          isCloseable: true,
-          isRenamable: true,
-          isPrivate: true,
-          url: 'about:blank',
-        });
-
-        // Load the newly created frame
-        dataGridFrameType.load(pgBrowser.docker);
-        this.on('pgadmin-datagrid:transaction:created', function(trans_obj) {
-          this.launch_grid(trans_obj);
-        });
-      },
-
-      // This is a callback function to show data when user click on menu item.
-      show_data_grid: function(data, i) {
-        const transId = commonUtils.getRandomInt(1, 9999999);
-        showData.showDataGrid(this, pgBrowser, alertify, data, i, transId);
-      },
-
-      // This is a callback function to show filtered data when user click on menu item.
-      show_filtered_row: function(data, i) {
-        const transId = commonUtils.getRandomInt(1, 9999999);
-        showData.showDataGrid(this, pgBrowser, alertify, data, i, transId, true, this.preferences);
-      },
-
-      // This is a callback function to show query tool when user click on menu item.
-      show_query_tool: function(url, aciTreeIdentifier) {
-        const transId = commonUtils.getRandomInt(1, 9999999);
-        var t = pgBrowser.tree,
-          i = aciTreeIdentifier || t.selected(),
-          d = i ? t.itemData(i) : undefined;
-
-        //Open query tool with create script if copy_sql_to_query_tool is true else open blank query tool
-        var preference = pgBrowser.get_preference('sqleditor', 'copy_sql_to_query_tool');
-        if(preference.value && !d._type.includes('coll-') && (url === '' || url['applies'] === 'tools')){
-          var stype = d._type.toLowerCase();
-          var data = {
-            'script': stype,
-            data_disabled: gettext('The selected tree node does not support this option.'),
-          };
-          pgBrowser.Node.callbacks.show_script(data);
-        }else{
-          if(d._type.includes('coll-')){
-            url = '';
-          }
-          showQueryTool.showQueryTool(this, pgBrowser, url, aciTreeIdentifier, transId);
-        }
-      },
-
-      launch_grid: function(trans_id, panel_url, is_query_tool, panel_title, sURL=null, sql_filter=null) {
-
-        let queryToolForm = `
-          <form id="queryToolForm" action="${panel_url}" method="post">
-            <input id="title" name="title" hidden />`;
-
-        if(sURL){
-          queryToolForm +=`<input name="query_url" value="${sURL}" hidden />`;
-        }
-        if(sql_filter) {
-          queryToolForm +=`<textarea name="sql_filter" hidden>${sql_filter}</textarea>`;
-        }
-
-        /* Escape backslashes as it is stripped by back end */
-        queryToolForm +=`
-          </form>
-            <script>
-              document.getElementById("title").value = "${_.escape(panel_title.replace('\\', '\\\\'))}";
-              document.getElementById("queryToolForm").submit();
-            </script>
-          `;
-
-        var browser_preferences = pgBrowser.get_preferences_for_module('browser');
-        var open_new_tab = browser_preferences.new_browser_tab_open;
-        if (open_new_tab && open_new_tab.includes('qt')) {
-          var newWin = window.open('', '_blank');
-          if(newWin) {
-            newWin.document.write(queryToolForm);
-            newWin.document.title = panel_title;
-            // Send the signal to runtime, so that proper zoom level will be set.
-            setTimeout(function() {
-              pgBrowser.send_signal_to_runtime('Runtime new window opened');
-            }, 500);
-          } else {
-            return false;
-          }
-        } else {
-          /* On successfully initialization find the dashboard panel,
-           * create new panel and add it to the dashboard panel.
-           */
-          var propertiesPanel = pgBrowser.docker.findPanels('properties');
-          var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]);
-          queryToolPanel.trans_id = trans_id;
-          showQueryTool._set_dynamic_tab(pgBrowser, browser_preferences['dynamic_tabs']);
-
-          // Set panel title and icon
-          panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, is_query_tool, _.unescape(panel_title));
-          queryToolPanel.focus();
-
-          // Listen on the panel closed event.
-          if (queryToolPanel.isVisible()) {
-            queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
-              $.ajax({
-                url: url_for('datagrid.close', {'trans_id': trans_id}),
-                method: 'DELETE',
-              });
-            });
-          }
-
-          queryToolPanel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function() {
-            queryToolPanel.trigger(wcDocker.EVENT.RESIZED);
-          });
-
-          commonUtils.registerDetachEvent(queryToolPanel);
-
-          // Listen on the panelRename event.
-          queryToolPanel.on(wcDocker.EVENT.RENAME, function(panel_data) {
-            var temp_title = panel_data.$titleText[0].textContent;
-            var is_dirty_editor = queryToolPanel.is_dirty_editor ? queryToolPanel.is_dirty_editor : false;
-            var title = queryToolPanel.is_dirty_editor ? panel_data.$titleText[0].textContent.replace(/.$/, '') : temp_title;
-            alertify.prompt('', title,
-              // We will execute this function when user clicks on the OK button
-              function(evt, value) {
-                // Remove the leading and trailing white spaces.
-                value = value.trim();
-                if(value) {
-                  var is_file = false;
-                  if(panel_data.$titleText[0].innerHTML.includes('File - ')) {
-                    is_file = true;
-                  }
-                  var selected_item = pgBrowser.tree.selected();
-                  var panel_titles = '';
-
-                  if(is_query_tool) {
-                    panel_titles = panelTitleFunc.getPanelTitle(pgBrowser, selected_item, value);
-                  } else {
-                    panel_titles = showData.generateDatagridTitle(pgBrowser, selected_item, value);
-                  }
-                  // Set title to the selected tab.
-                  if (is_dirty_editor) {
-                    panel_titles = panel_titles + ' *';
-                  }
-                  panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, is_query_tool, _.unescape(panel_titles), is_file);
-                }
-              },
-              // We will execute this function when user clicks on the Cancel
-              // button.  Do nothing just close it.
-              function(evt) { evt.cancel = false; }
-            ).set({'title': gettext('Rename Panel')});
-          });
-
-          var openQueryToolURL = function(j) {
-            // add spinner element
-            let $spinner_el =
-              $(`<div class="pg-sp-container">
-                    <div class="pg-sp-content">
-                        <div class="row">
-                            <div class="col-12 pg-sp-icon"></div>
-                        </div>
-                    </div>
-                </div>`).appendTo($(j).data('embeddedFrame').$container);
-
-            let init_poller_id = setInterval(function() {
-              var frameInitialized = $(j).data('frameInitialized');
-              if (frameInitialized) {
-                clearInterval(init_poller_id);
-                var frame = $(j).data('embeddedFrame');
-                if (frame) {
-                  frame.onLoaded(()=>{
-                    $spinner_el.remove();
-                  });
-                  frame.openHTML(queryToolForm);
-                }
-              }
-            }, 100);
-          };
-
-          openQueryToolURL(queryToolPanel);
-        }
-        return true;
-      },
-    },
-    Backbone.Events);
-
-  return pgAdmin.DataGrid;
-});
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
deleted file mode 100644
index 1cb3b3cd2..000000000
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ /dev/null
@@ -1,507 +0,0 @@
-{% extends "base.html" %}
-{% block title %}{{title}}{% endblock %}
-{% block body %}
-<style>
-    body {padding: 0px;}
-    {% if is_desktop_mode and is_linux %}
-    .alertify .ajs-dimmer,.alertify .ajs-modal{-webkit-transform: none;}
-    .alertify-notifier{-webkit-transform: none;}
-    .alertify-notifier .ajs-message{-webkit-transform: none;}
-    .alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
-    .sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
-    {% endif %}
-</style>
-{% block css_link %}
-<link type="text/css" rel="stylesheet" href="{{ url_for('browser.browser_css')}}"/>
-{% endblock %}
-<div id="main-editor_panel">
-    <div class="sql-editor">
-        <div id="btn-toolbar" class="editor-toolbar" role="toolbar" aria-label="">
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-show-query-tool" type="button" class="btn btn-sm btn-primary-icon btn-show-query-tool"
-                        title=""
-                        aria-label="show query tool"
-                        tabindex="0">
-                    <i class="pg-font-icon icon-query_tool" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-load-file" type="button" class="btn btn-sm btn-primary-icon btn-load-file"
-                        title=""
-                        accesskey=""
-                        tabindex="0">
-                    <i class="fa fa-folder-open sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-save-file" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        disabled>
-                    <i class="fa fa-save sql-icon-lg" aria-hidden="true" tabindex="0" role="img"></i>
-                </button>
-                <button id="btn-file-menu-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" disabled
-                        tabindex="0" aria-label="file menu">
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-file-menu-save" href="#" tabindex="0">
-                            <span>{{ _('Save') }}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-file-menu-save-as" href="#" tabindex="0">
-                            <span>{{ _('Save As') }}</span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-save-data" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="pg-font-icon icon-save_data_changes sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-find" type="button" class="btn btn-sm btn-primary-icon" aria-label="{{ _('Find') }}" title="{{ _('Find (Ctrl/Cmd+F)') }}">
-                    <i class="fa fa-search sql-icon-lg" aria-hidden="true" tabindex="0" role="img"></i>
-                </button>
-                <button id="btn-find-menu-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        title=""
-                        accesskey=""
-                        tabindex="0">
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-find" href="#" tabindex="0">
-                            <span> {{ _('Find') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+F)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+F)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-find-next" href="#" tabindex="0">
-                            <span> {{ _('Find Next') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+G)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+G)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-find-previous" href="#" tabindex="0">
-                            <span> {{ _('Find Previous') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+Shift+G)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+Shift+G)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-find-persistent" href="#" tabindex="0">
-                            <span>{{ _('Persistent Find') }}</span>
-                        </a>
-                    </li>
-                    <li class="dropdown-divider"></li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-replace" href="#" tabindex="0">
-                            <span> {{ _('Replace') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+Option+F)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+Shift+F)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-replace-all" href="#" tabindex="0">
-                            <span>{{ _('Replace All') }}</span>
-                        </a>
-                    </li>
-                    <li class="dropdown-divider"></li>
-                    <li>
-                        <a class="dropdown-item" id="btn-find-menu-jump" href="#" tabindex="0">
-                            <span>{{ _('Jump (Alt+G)') }}</span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-copy-row" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-copy sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-copy-row-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        tabindex="0" aria-label="copy row">
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-copy-with-header" href="#" tabindex="0">
-                            <i class="copy-with-header fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Copy with headers') }} </span>
-                        </a>
-                    </li>
-                </ul>
-                <button id="btn-paste-row" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-paste sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-delete-row" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-trash-alt sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-edit-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        aria-label="{{ _('Edit') }}" title="{{ _('Edit') }}" tabindex="0">
-                    <i class="fa fa-edit sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-indent-code" href="#" tabindex="0">
-                            <span> {{ _('Indent Selection (Tab)') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-unindent-code" href="#" tabindex="0">
-                            <span> {{ _('Unindent Selection (Shift+Tab)') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-comment-line" href="#" tabindex="0">
-                            <span> {{ _('Inline Comment Selection') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+/)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+/)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-uncomment-line" href="#" tabindex="0">
-                            <span> {{ _('Inline Uncomment Selection') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Cmd+.)') }}
-                            {% else %}
-                                 {{ _(' (Ctrl+.)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-toggle-comment-block" href="#" tabindex="0">
-                            <span> {{ _('Block Comment/Uncomment Selection') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Shift+Cmd+/)') }}
-                            {% else %}
-                                 {{ _(' (Shift+Ctrl+/)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                    <li class="dropdown-divider"></li>
-                    <li>
-                        <a class="dropdown-item" id="btn-format-sql" href="#" tabindex="0">
-                            <span>{{ _('Format SQL') }}{% if client_platform == 'macos' -%}
-                                 {{ _(' (Shift+Cmd+K)') }}
-                            {% else %}
-                                 {{ _(' (Shift+Ctrl+K)') }}{%- endif %}</span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-filter" type="button" class="btn btn-secondary"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-filter sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-filter-dropdown" type="button" class="btn btn-sm btn-secondary dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        title=""
-                        accesskey=""
-                        disabled tabindex="0">
-                </button>
-                <ul class="dropdown-menu dropdown-menu-right">
-                    <li>
-                        <a id="btn-filter-menu" class="dropdown-item" href="#" tabindex="0">{{ _('Sort/Filter') }}</a>
-                    </li>
-                    <li>
-                        <a id="btn-include-filter" class="dropdown-item" href="#" tabindex="0">{{ _('Filter by Selection') }}</a>
-                    </li>
-                    <li>
-                        <a id="btn-exclude-filter" class="dropdown-item" href="#" tabindex="0">{{ _('Exclude by Selection') }}</a>
-                    </li>
-                    <li>
-                        <a id="btn-remove-filter" class="dropdown-item" href="#" tabindex="0">{{ _('Remove Sort/Filter') }}</a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <select id="btn-rows-limit" class="limit form-control form-control-sm" disabled
-                        title=""
-                        accesskey=""
-                        tabindex="0">
-                    <option value="-1">{{ _('No limit') }}</option>
-                    <option value="1000">{{ _('1000 rows') }}</option>
-                    <option value="500">{{ _('500 rows') }}</option>
-                    <option value="100">{{ _('100 rows') }}</option>
-                </select>
-            </div>
-
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-cancel-query" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled >
-                    <i class="fa fa-stop sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-flash" data-test-selector="execute-refresh-button" type="button" class="btn btn-sm btn-primary-icon" style="width: 32px;"
-                        title=""
-                        tabindex="0" data-click-counter="0" disabled>
-                    <i class="fa fa-play sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-query-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        accesskey=""
-                        title=""
-                        tabindex="0">
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-auto-commit" href="#" tabindex="0">
-                            <i class="auto-commit fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Auto commit?') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-auto-rollback" href="#" tabindex="0">
-                            <i class="auto-rollback fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Auto rollback on error?') }} </span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-explain" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-hand-pointer sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-explain-analyze" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey="" disabled>
-                    <i class="fa fa-list-alt sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-explain-options-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle dropdown-toggle-split"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        tabindex="0" aria-label="explain">
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-explain-verbose" href="#" tabindex="0">
-                            <i class="explain-verbose fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Verbose') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-explain-costs" href="#" tabindex="0">
-                            <i class="explain-costs fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Costs') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-explain-buffers" href="#" tabindex="0">
-                            <i class="explain-buffers fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Buffers') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-explain-timing" href="#" tabindex="0">
-                            <i class="explain-timing fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Timing') }} </span>
-                        </a>
-                    </li>
-                    <li data-min-ver="100000">
-                        <a class="dropdown-item" id="btn-explain-summary" href="#" tabindex="0">
-                            <i class="explain-summary fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Summary') }} </span>
-                        </a>
-                    </li>
-                    <li data-min-ver="120000">
-                        <a class="dropdown-item" id="btn-explain-settings" href="#" tabindex="0">
-                            <i class="explain-settings fa fa-check visibility-hidden" aria-hidden="true" role="img"></i>
-                            <span> {{ _('Settings') }} </span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-commit" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        accesskey=""
-                        tabindex="0" disabled>
-                    <i class="pg-font-icon icon-commit sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <button id="btn-rollback" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        tabindex="0" disabled>
-                    <i class="pg-font-icon icon-rollback sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1" role="group" aria-label="">
-                <button id="btn-clear-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        title=""
-                        accesskey=""
-                        tabindex="0">
-                    <i class="fa fa-eraser sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-clear" href="#" tabindex="0">
-                            <span> {{ _('Clear Query Window') }} </span>
-                        </a>
-                    </li>
-                    <li>
-                        <a class="dropdown-item" id="btn-clear-history" href="#" tabindex="0">
-                            <span> {{ _('Clear History') }} </span>
-                        </a>
-                    </li>
-                </ul>
-            </div>
-            <div class="btn-group" role="group" aria-label="">
-                <button id="btn-save-results-to-file" type="button" class="btn btn-sm btn-primary-icon"
-                        title=""
-                        tabindex="0" disabled>
-                    <i class="fa fa-download sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-            </div>
-            <div class="btn-group mr-1 user_macros" role="group" aria-label="">
-                <button id="btn-macro-dropdown" type="button" class="btn btn-sm btn-primary-icon dropdown-toggle"
-                        data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
-                        aria-label="{{ _('Macros') }}" title="{{ _('Macros') }}" tabindex="0">
-                    <i class="fa fa-scroll sql-icon-lg" aria-hidden="true" role="img"></i>
-                </button>
-                <ul class="dropdown-menu">
-                    <li>
-                        <a class="dropdown-item" id="btn-manage-macros" href="#" tabindex="0">
-                            <span> {{ _('Manage Macros...') }} </span>
-                        </a>
-                    </li>
-                    {% if macros|length > 0 %}
-                    <li class="dropdown-divider"></li>
-                    {% endif %}
-                    {% for i in macros %}
-                    <li>
-                        <a class="dropdown-item btn-macro" data-macro-id="{{ i.id }}" href="#" tabindex="0">
-                            <span> {{ _(i.name)|e }} </span>
-                            <span> ({{ i.key_label }}) </span>
-                        </a>
-                    </li>
-                    {% endfor %}
-                </ul>
-            </div>
-        </div>
-
-        <div class="connection_status_wrapper d-flex">
-            <div id="btn-conn-status"
-                 role="status"
-                 class="connection_status d-flex justify-content-center align-items-center" data-container="body"
-                 data-toggle="popover" data-placement="bottom"
-                 data-content=""
-                 data-panel-visible="visible"
-                 accesskey=""
-                 tabindex="0">
-                <i class="pg-font-icon icon-disconnected obtaining-conn d-flex m-auto" aria-hidden="true"
-                    title="" role="img">
-                </i>
-            </div>
-            <div class="connection-info btn-group mr-1" role="group" aria-label="" style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">
-                <div class="connection-data" data-toggle="dropdown">
-                    <div class="editor-title" aria-haspopup="true" aria-expanded="false"
-                         style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% endif %}; color: {% if fgcolor %}{{ fgcolor }}{% endif %};">&nbsp;
-                    </div>
-                    <span class="conn-info-dd dropdown-toggle dropdown-toggle-split"
-                    aria-haspopup="true" aria-expanded="false"></span>
-                </div>
-                <ul class="dropdown-menu" id="connections-list">
-                </ul>
-            </div>
-
-
-        </div>
-        <div id="editor-panel" tabindex="0">
-            <div id="main_loader" class="pg-sp-container sql-editor-busy-fetching">
-                <div class="pg-sp-content">
-                    <div class="row">
-                        <div class="col-12 pg-sp-icon sql-editor-busy-icon"></div>
-                    </div>
-                    <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">{{ _('Loading...') }}</div></div>
-                </div>
-            </div>
-        </div>
-        <div class="sql-editor-busy-text-status d-none"></div>
-    </div>
-</div>
-{% endblock %}
-
-{% block init_script %}
-require(['sources/generated/browser_nodes', 'sources/generated/codemirror', 'sources/generated/slickgrid'], function() {
-    require(['sources/generated/sqleditor'], function(ctx) {
-        var $ = pgAdmin.SqlEditor.jquery,
-            S = pgAdmin.SqlEditor.S,
-            editorPanel = $('.sql-editor');
-
-        // Register unload event on window close.
-        /* If opened in new tab, close the connection only on tab/window close and
-         * not on refresh attempt because the user may cancel the reload
-         */
-        if(window.opener) {
-          $(window).on('unload', function(ev) {
-            $.ajax({
-              method: 'DELETE',
-              url: "{{ url_for('datagrid.index') }}" + "close/" + {{ uniqueId }}
-            });
-          });
-        } else {
-          $(window).on('beforeunload', function(ev) {
-            $.ajax({
-              method: 'DELETE',
-              url: "{{ url_for('datagrid.index') }}" + "close/" + {{ uniqueId }}
-            });
-          });
-        }
-
-        // Get the controller object from pgAdmin.SqlEditor
-        var sqlEditorController = pgAdmin.SqlEditor.create(editorPanel);
-
-        // Listen on events to show/hide loading-icon and change messages.
-        {% if script_type_url %}
-            var script_type_url = '{{ script_type_url }}';
-        {% else %}
-            var script_type_url = '';
-        {% endif %}
-        // Start the query tool.
-
-        sqlEditorController.start(
-            {{ uniqueId }},
-            {{ url_params|safe}},
-            '{{ layout|safe }}',
-            {{ macros|safe }}
-        );
-
-        // If opening from schema diff, set the generated script to the SQL Editor
-
-        var schema_ddl_diff = (window.opener !== null) ? window.opener.pgAdmin.ddl_diff : (window.parent !== null) ? window.parent.pgAdmin.ddl_diff : window.top.pgAdmin.ddl_diff;
-        sqlEditorController.set_value_to_editor(schema_ddl_diff);
-        if (window.opener !== null) window.opener.pgAdmin.ddl_diff = '';
-        else if (window.parent !== null) window.parent.pgAdmin.ddl_diff = '';
-        else if (window.top !== null) window.top.pgAdmin.ddl_diff = '';
-
-    });
-});
-{% endblock %}
diff --git a/web/pgadmin/tools/datagrid/tests/__init__.py b/web/pgadmin/tools/datagrid/tests/__init__.py
deleted file mode 100644
index e69de29bb..000000000
diff --git a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json b/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
deleted file mode 100644
index 0075f35e3..000000000
--- a/web/pgadmin/tools/datagrid/tests/datagrid_test_data.json
+++ /dev/null
@@ -1,134 +0,0 @@
-{
-  "data_grid_init_query_tool": [
-    {
-      "name": "Datagrid init query tool",
-      "url": "/datagrid/initialize/query_tool/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": {},
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    }
-  ],
-  "data_grid_query_tool_close": [
-    {
-      "name": "Datagrid query tool close",
-      "url": "/datagrid/close/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": {},
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    }
-  ],
-  "data_grid_validate_filter": [
-    {
-      "name": "Datagrid validate filter",
-      "url": "/datagrid/filter/validate/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": "id = 1",
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    },
-    {
-      "name": "Datagrid validate filter",
-      "url": "/datagrid/filter/validate/",
-      "is_positive_test": false,
-      "mocking_required": true,
-      "test_data": "id = 1",
-      "mock_data": {
-        "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_scalar",
-        "return_value": "(False, 'Mocked Internal Server Error while validate filter')"
-      },
-      "expected_data": {
-        "status_code": 200
-      }
-    }
-  ],
-  "data_grid_update_connection": [
-    {
-      "name": "Datagrid update connection positive",
-      "url": "/datagrid/initialize/query_tool/update_connection/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "is_create_role": false,
-      "test_data": {},
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    },
-    {
-      "name": "Datagrid update connection with new user",
-      "url": "/datagrid/initialize/query_tool/update_connection/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "is_create_role": true,
-      "test_data": {},
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    }
-  ],
-  "data_grid_panel": [
-    {
-      "name": "Datagrid Panel",
-      "url": "/datagrid/panel/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": {},
-      "mock_data": {
-      },
-      "expected_data": {
-        "status_code": 200
-      }
-    }
-  ],
-  "data_grid_initialize": [
-    {
-      "name": "Datagrid Initialize",
-      "url": "/datagrid/initialize/datagrid/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": "id=1",
-      "mock_data": {
-
-      },
-      "expected_data": {
-        "status_code": 200
-      }
-    },{
-      "name": "Datagrid Initialize",
-      "url": "/datagrid/initialize/datagrid/",
-      "is_positive_test": true,
-      "mocking_required": false,
-      "test_data": null,
-      "mock_data": {},
-      "expected_data": {
-        "status_code": 200
-      }
-    },
-    {
-      "name": "Datagrid Initialize",
-      "url": "/datagrid/initialize/datagrid/",
-      "is_positive_test": false,
-      "mocking_required": true,
-      "test_data": "id=1",
-      "mock_data": {
-        "function_name": "pgadmin.utils.driver.psycopg2.connection.Connection.execute_dict",
-        "return_value": "(False, 'Mocked Internal Server Error while initialize datagrid.')"
-      },
-      "expected_data": {
-        "status_code": 500
-      }
-    }
-  ]
-}
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
deleted file mode 100644
index bc7946be5..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_data_grid_init_query_tool.py
+++ /dev/null
@@ -1,73 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from pgadmin.utils.exception import ExecuteError
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from . import utils as data_grid_utils
-
-
-class DatagridInitQueryToolTestCase(BaseTestGenerator):
-    """
-    This will init query-tool connection.
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_init_query_tool',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-
-        self.trans_id = str(random.randint(1, 9999999))
-
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-
-    def init_query_tool(self):
-        response = self.tester.post(
-            self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' + str(
-                self.sid) + '/' + str(self.did),
-            content_type='html/json'
-        )
-        return response
-
-    def runTest(self):
-        """ This function will init query tool connection."""
-
-        if self.is_positive_test:
-            response = self.init_query_tool()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-
-            self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
deleted file mode 100644
index 5511a021c..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_data_grid_panel.py
+++ /dev/null
@@ -1,90 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from pgadmin.utils.exception import ExecuteError
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from . import utils as data_grid_utils
-
-
-class DatagridPanelTestCase(BaseTestGenerator):
-    """
-    This will data grid panel.
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_panel',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-
-        self.trans_id = str(random.randint(1, 9999999))
-        qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
-                                                   self.sgid, self.sid,
-                                                   self.did)
-
-        if not qt_init['success']:
-            raise ExecuteError("Could not initialize querty tool.")
-
-    def panel(self):
-        query_param = \
-            '?is_query_tool={0}&sgid={1}&sid={2}&server_type={3}' \
-            '&did={4}&title={5}'.format(True, self.sgid, self.sid,
-                                        self.server_information['type'],
-                                        self.did, 'Query panel')
-
-        response = self.tester.post(
-            self.url + str(self.trans_id) + query_param,
-            data=json.dumps(self.test_data),
-            content_type='html/json'
-        )
-        return response
-
-    def runTest(self):
-        """ This function will update query tool connection."""
-
-        if self.is_positive_test:
-            response = self.panel()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-        else:
-            with patch(self.mock_data["function_name"],
-                       return_value=eval(self.mock_data["return_value"])):
-                response = self.panel()
-                actual_response_code = response.status_code
-                expected_response_code = self.expected_data['status_code']
-
-        self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
deleted file mode 100644
index 440ea7c51..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_data_grid_query_tool_close.py
+++ /dev/null
@@ -1,78 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from . import utils as data_grid_utils
-from pgadmin.utils.exception import ExecuteError
-
-
-class DatagridQueryToolCloseTestCase(BaseTestGenerator):
-    """
-    This will close query-tool connection.
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_query_tool_close',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-
-        self.trans_id = str(random.randint(1, 9999999))
-        qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
-                                                   self.sgid, self.sid,
-                                                   self.did)
-
-        if not qt_init['success']:
-            raise ExecuteError("Could not initialize querty tool.")
-
-    def close_connection(self):
-        response = self.tester.delete(
-            self.url + str(self.trans_id),
-            content_type='html/json'
-        )
-        return response
-
-    def runTest(self):
-        """ This function will update query tool connection."""
-
-        if self.is_positive_test:
-            response = self.close_connection()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-
-            self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
deleted file mode 100644
index 5dd7fff38..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_data_grid_update_connection.py
+++ /dev/null
@@ -1,121 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from pgadmin.browser.server_groups.servers.roles.tests import \
-    utils as roles_utils
-from . import utils as data_grid_utils
-from pgadmin.utils.exception import ExecuteError
-
-
-class DatagridUpdateConnectionTestCase(BaseTestGenerator):
-    """
-    This will update query-tool connection.
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_update_connection',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-
-        self.trans_id = str(random.randint(1, 9999999))
-        self.roles = None
-
-        if self.is_create_role:
-            data = roles_utils.get_role_data(self.server['db_password'])
-            self.role_name = data['rolname']
-            self.role_password = data['rolpassword']
-            roles_utils.create_role_with_password(
-                self.server, self.role_name, self.role_password)
-
-        if not self.is_positive_test or self.is_create_role:
-            qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
-                                                       self.sgid, self.sid,
-                                                       self.did)
-
-            if not qt_init['success']:
-                raise ExecuteError("Could not initialize querty tool.")
-
-        self.test_data = {
-            "database": self.did,
-            "server": self.sid,
-        }
-
-        if self.server_information['type'] == 'ppas':
-            self.test_data['password'] = 'enterprisedb'
-            self.test_data['user'] = 'enterprisedb'
-        else:
-            self.test_data['password'] = 'postgres'
-            self.test_data['user'] = 'postgres'
-
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-
-    def update_connection(self, user_data=None):
-        if user_data:
-            response = self.tester.post(
-                self.url + str(self.trans_id) + '/' + str(self.sgid) +
-                '/' + str(self.sid) + '/' + str(self.did),
-                data=json.dumps(user_data),
-                content_type='html/json'
-            )
-        else:
-            response = self.tester.post(
-                self.url + str(self.trans_id) + '/' + str(self.sgid) + '/' +
-                str(self.sid) + '/' + str(self.did),
-                data=json.dumps(self.test_data),
-                content_type='html/json'
-            )
-        return response
-
-    def runTest(self):
-        """ This function will update query tool connection."""
-
-        if self.is_positive_test:
-            user_data = dict()
-            if self.is_create_role:
-                user_data['user'] = self.role_name
-                user_data['password'] = self.role_password
-                user_data['role'] = None
-            response = self.update_connection(user_data=user_data)
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-        else:
-            response = self.update_connection()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-
-        self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py b/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
deleted file mode 100644
index ef31f0716..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_data_grid_validate_filter.py
+++ /dev/null
@@ -1,92 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
-from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
-    import utils as tables_utils
-from . import utils as data_grid_utils
-from pgadmin.utils.exception import ExecuteError
-
-
-class DatagridValidateFilterTestCase(BaseTestGenerator):
-    """
-    This will validate filter connection.
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_validate_filter',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-        self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
-        self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      self.db_name,
-                                                      self.schema_name)
-        if not schema_response:
-            raise ExecuteError("Could not find the schema to add a table.")
-        self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
-        self.table_id = tables_utils.create_table(self.server, self.db_name,
-                                                  self.schema_name,
-                                                  self.table_name)
-
-    def validate_filter(self):
-        response = self.tester.post(
-            self.url + str(self.sid) + '/' + str(self.did) + '/' +
-            str(self.table_id),
-            data=json.dumps(self.test_data),
-            content_type='html/json'
-        )
-        return response
-
-    def runTest(self):
-        """ This function will update query tool connection."""
-
-        if self.is_positive_test:
-            response = self.validate_filter()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-        else:
-            with patch(self.mock_data["function_name"],
-                       return_value=eval(self.mock_data["return_value"])):
-                response = self.validate_filter()
-                actual_response_code = response.status_code
-                expected_response_code = self.expected_data['status_code']
-
-        self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py b/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
deleted file mode 100644
index 08c253be1..000000000
--- a/web/pgadmin/tools/datagrid/tests/test_initialize_data_grid.py
+++ /dev/null
@@ -1,109 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-
-
-import json
-import uuid
-import random
-
-from unittest.mock import patch
-from pgadmin.browser.server_groups.servers.databases.tests import utils as \
-    database_utils
-
-from pgadmin.utils.route import BaseTestGenerator
-from regression import parent_node_dict
-from regression.python_test_utils import test_utils as utils
-from regression.test_setup import config_data
-from pgadmin.browser.server_groups.servers.databases.schemas.tests import \
-    utils as schema_utils
-from pgadmin.browser.server_groups.servers.databases.schemas.tables.tests \
-    import utils as tables_utils
-from . import utils as data_grid_utils
-from pgadmin.utils.exception import ExecuteError
-
-
-class DatagridInitializeTestCase(BaseTestGenerator):
-    """
-    This will Initialize datagrid
-    """
-
-    scenarios = utils.generate_scenarios(
-        'data_grid_initialize',
-        data_grid_utils.test_cases
-    )
-
-    def setUp(self):
-        self.database_info = parent_node_dict["database"][-1]
-        self.db_name = self.database_info["db_name"]
-        self.did = self.database_info["db_id"]
-        self.sid = parent_node_dict["server"][-1]["server_id"]
-        self.sgid = config_data['server_group']
-
-        db_con = database_utils.connect_database(self, utils.SERVER_GROUP,
-                                                 self.sid, self.did)
-        if not db_con['data']["connected"]:
-            raise ExecuteError("Could not connect to database to add a table.")
-
-        self.schema_id = parent_node_dict['schema'][-1]["schema_id"]
-        self.schema_name = parent_node_dict['schema'][-1]["schema_name"]
-        schema_response = schema_utils.verify_schemas(self.server,
-                                                      self.db_name,
-                                                      self.schema_name)
-        if not schema_response:
-            raise ExecuteError("Could not find the schema to add a table.")
-        self.table_name = "table_for_wizard%s" % (str(uuid.uuid4())[1:8])
-        self.table_id = tables_utils.create_table(self.server, self.db_name,
-                                                  self.schema_name,
-                                                  self.table_name)
-        self.trans_id = str(random.randint(1, 9999999))
-        qt_init = data_grid_utils._init_query_tool(self, self.trans_id,
-                                                   self.sgid, self.sid,
-                                                   self.did)
-
-        if not qt_init['success']:
-            raise ExecuteError("Could not initialize query tool.")
-
-    def initialize_datagrid(self):
-        if self.test_data:
-            response = self.tester.post(
-                self.url + str(self.trans_id) + '/4/table/' +
-                str(self.sgid) + '/' + str(self.sid) + '/' +
-                str(self.did) + '/' + str(self.table_id),
-                data=json.dumps(self.test_data),
-                content_type='html/json'
-            )
-        else:
-            response = self.tester.post(
-                self.url + str(self.trans_id) + '/4/table/' +
-                str(self.sgid) + '/' + str(self.sid) + '/' +
-                str(self.did) + '/' + str(self.table_id),
-                content_type='html/json'
-            )
-        return response
-
-    def runTest(self):
-        """ This function will update query tool connection."""
-
-        if self.is_positive_test:
-            response = self.initialize_datagrid()
-            actual_response_code = response.status_code
-            expected_response_code = self.expected_data['status_code']
-        else:
-            with patch(self.mock_data["function_name"],
-                       return_value=eval(self.mock_data["return_value"])):
-                response = self.initialize_datagrid()
-                actual_response_code = response.status_code
-                expected_response_code = self.expected_data['status_code']
-
-        self.assertEqual(actual_response_code, expected_response_code)
-
-    def tearDown(self):
-        """This function disconnect database."""
-        database_utils.disconnect_database(self, self.sid,
-                                           self.did)
diff --git a/web/pgadmin/tools/datagrid/tests/utils.py b/web/pgadmin/tools/datagrid/tests/utils.py
deleted file mode 100644
index 78831abce..000000000
--- a/web/pgadmin/tools/datagrid/tests/utils.py
+++ /dev/null
@@ -1,33 +0,0 @@
-##########################################################################
-#
-# pgAdmin 4 - PostgreSQL Tools
-#
-# Copyright (C) 2013 - 2022, The pgAdmin Development Team
-# This software is released under the PostgreSQL Licence
-#
-##########################################################################
-import os
-import json
-
-file_name = os.path.basename(__file__)
-CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
-with open(CURRENT_PATH + "/datagrid_test_data.json") as data_file:
-    test_cases = json.load(data_file)
-
-
-def _init_query_tool(self, trans_id, server_group, server_id, db_id):
-    QUERY_TOOL_INIT_URL = '/datagrid/initialize/query_tool'
-
-    qt_init = self.tester.post(
-        '{0}/{1}/{2}/{3}/{4}'.format(
-            QUERY_TOOL_INIT_URL,
-            trans_id,
-            server_group,
-            server_id,
-            db_id
-        ),
-        follow_redirects=True
-    )
-    assert qt_init.status_code == 200
-    qt_init = json.loads(qt_init.data.decode('utf-8'))
-    return qt_init
diff --git a/web/pgadmin/tools/debugger/static/js/debugger.js b/web/pgadmin/tools/debugger/static/js/debugger.js
index 44cd53c47..0ac8293c3 100644
--- a/web/pgadmin/tools/debugger/static/js/debugger.js
+++ b/web/pgadmin/tools/debugger/static/js/debugger.js
@@ -14,8 +14,8 @@ define([
   'alertify', 'sources/pgadmin', 'pgadmin.browser',
   'backbone', 'pgadmin.backgrid', 'codemirror', 'pgadmin.backform',
   'pgadmin.tools.debugger.ui', 'pgadmin.tools.debugger.utils',
-  'tools/datagrid/static/js/show_query_tool', 'sources/utils',
-  'pgadmin.authenticate.kerberos', 'tools/datagrid/static/js/datagrid_panel_title',
+  'tools/sqleditor/static/js/show_query_tool', 'sources/utils',
+  'pgadmin.authenticate.kerberos', 'tools/sqleditor/static/js/sqleditor_title',
   'wcdocker', 'pgadmin.browser.frame',
 ], function(
   gettext, url_for, $, _, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
diff --git a/web/pgadmin/tools/debugger/static/js/debugger_utils.js b/web/pgadmin/tools/debugger/static/js/debugger_utils.js
index 8352ae895..fbc2f4b7c 100644
--- a/web/pgadmin/tools/debugger/static/js/debugger_utils.js
+++ b/web/pgadmin/tools/debugger/static/js/debugger_utils.js
@@ -6,8 +6,8 @@
 // This software is released under the PostgreSQL Licence
 //
 //////////////////////////////////////////////////////////////////////////
-import {generateTitle} from '../../../datagrid/static/js/datagrid_panel_title';
-import {_set_dynamic_tab} from '../../../datagrid/static/js/show_query_tool';
+import {generateTitle} from '../../../sqleditor/static/js/sqleditor_title';
+import {_set_dynamic_tab} from '../../../sqleditor/static/js/show_query_tool';
 
 function setFocusToDebuggerEditor(editor, command) {
   const TAB = 9;
diff --git a/web/pgadmin/tools/erd/static/js/erd_module.js b/web/pgadmin/tools/erd/static/js/erd_module.js
index b418885e3..9a0e7309b 100644
--- a/web/pgadmin/tools/erd/static/js/erd_module.js
+++ b/web/pgadmin/tools/erd/static/js/erd_module.js
@@ -7,7 +7,7 @@
 //
 //////////////////////////////////////////////////////////////
 
-import {getPanelTitle} from 'tools/datagrid/static/js/datagrid_panel_title';
+import {getPanelTitle} from 'tools/sqleditor/static/js/sqleditor_title';
 import {getRandomInt, registerDetachEvent} from 'sources/utils';
 import Notify from '../../../../static/js/helpers/Notifier';
 
diff --git a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
index 2b598d808..0eb34fc77 100644
--- a/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
+++ b/web/pgadmin/tools/erd/static/js/erd_tool/ui_components/BodyWidget.jsx
@@ -22,7 +22,7 @@ import FloatingNote from './FloatingNote';
 import {setPanelTitle} from '../../erd_module';
 import gettext from 'sources/gettext';
 import url_for from 'sources/url_for';
-import {showERDSqlTool} from 'tools/datagrid/static/js/show_query_tool';
+import {showERDSqlTool} from 'tools/sqleditor/static/js/show_query_tool';
 import 'wcdocker';
 import Theme from '../../../../../../static/js/Theme';
 import TableSchema from '../../../../../../browser/server_groups/servers/databases/schemas/tables/static/js/table.ui';
@@ -579,7 +579,7 @@ export default class BodyWidget extends React.Component {
 
         let sqlId = `erd${this.props.params.trans_id}`;
         localStorage.setItem(sqlId, sqlScript);
-        showERDSqlTool(parentData, sqlId, this.props.params.title, this.props.pgWindow.pgAdmin.DataGrid, this.props.alertify);
+        showERDSqlTool(parentData, sqlId, this.props.params.title, this.props.pgWindow.pgAdmin.Tools.SQLEditor, this.props.alertify);
       })
       .catch((error)=>{
         this.handleAxiosCatch(error);
diff --git a/web/pgadmin/tools/psql/static/js/psql_module.js b/web/pgadmin/tools/psql/static/js/psql_module.js
index e3380152c..f70f1901a 100644
--- a/web/pgadmin/tools/psql/static/js/psql_module.js
+++ b/web/pgadmin/tools/psql/static/js/psql_module.js
@@ -18,7 +18,7 @@ import {retrieveAncestorOfTypeServer} from 'sources/tree/tree_utils';
 import pgWindow from 'sources/window';
 import Notify from '../../../../static/js/helpers/Notifier';
 
-import {generateTitle, refresh_db_node} from 'tools/datagrid/static/js/datagrid_panel_title';
+import {generateTitle, refresh_db_node} from 'tools/sqleditor/static/js/sqleditor_title';
 
 
 export function setPanelTitle(psqlToolPanel, panelTitle) {
diff --git a/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js b/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js
index d47c266a4..a8b23d9eb 100644
--- a/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js
+++ b/web/pgadmin/tools/schema_diff/static/js/schema_diff_ui.js
@@ -15,8 +15,7 @@ import Backbone from 'backbone';
 import Slick from 'sources/../bundle/slickgrid';
 import pgAdmin from 'sources/pgadmin';
 import {setPGCSRFToken} from 'sources/csrf';
-import {generateScript} from 'tools/datagrid/static/js/show_query_tool';
-import 'pgadmin.sqleditor';
+import 'pgadmin.tools.sqleditor';
 import pgWindow from 'sources/window';
 import _ from 'underscore';
 import Notify from '../../../../static/js/helpers/Notifier';
@@ -27,6 +26,7 @@ import { SchemaDiffSelect2Control, SchemaDiffHeaderView,
 
 import { handleDependencies, selectDependenciesForGroup,
   selectDependenciesForAll, selectDependencies } from './schema_diff_dependency';
+import { generateScript } from '../../../sqleditor/static/js/show_query_tool';
 
 var wcDocker = window.wcDocker;
 
@@ -252,6 +252,7 @@ export default class SchemaDiffUI {
           let data = res.data;
           let server_data = {};
           if (data) {
+            let sqlId = `schema${self.trans_id}`;
             server_data['sgid'] = data.gid;
             server_data['sid'] = data.sid;
             server_data['stype'] = data.type;
@@ -259,13 +260,13 @@ export default class SchemaDiffUI {
             server_data['user'] = data.user;
             server_data['did'] = self.model.get('target_did');
             server_data['database'] = data.database;
+            server_data['sql_id'] = sqlId;
 
             if (_.isUndefined(generated_script)) {
               generated_script = script_header + 'BEGIN;' + '\n' + self.model.get('diff_ddl') + '\n' + 'END;';
             }
-
-            pgWindow.pgAdmin.ddl_diff = generated_script;
-            generateScript(server_data, pgWindow.pgAdmin.DataGrid, Alertify);
+            localStorage.setItem(sqlId, generated_script);
+            generateScript(server_data, pgWindow.pgAdmin.Tools.SQLEditor, Alertify);
           }
 
           $('#diff_fetching_data').find('.schema-diff-busy-text').text('');
diff --git a/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js
index af8d0cfb3..44d0e0c3c 100644
--- a/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js
+++ b/web/pgadmin/tools/search_objects/static/js/search_objects_dialog.js
@@ -9,7 +9,7 @@
 
 import gettext from 'sources/gettext';
 import {Dialog} from 'sources/alertify/dialog';
-import {getPanelTitle} from 'tools/datagrid/static/js/datagrid_panel_title';
+import {getPanelTitle} from 'tools/sqleditor/static/js/sqleditor_title';
 import {retrieveAncestorOfTypeDatabase} from 'sources/tree/tree_utils';
 
 export default class SearchObjectsDialog extends Dialog {
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index ac85b5eaf..23945c604 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -11,16 +11,21 @@
 import os
 import pickle
 import re
+import random
 from urllib.parse import unquote
+from threading import Lock
 
 import simplejson as json
-from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT
+from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT,\
+    ALLOW_SAVE_PASSWORD
+from werkzeug.user_agent import UserAgent
 from flask import Response, url_for, render_template, session, current_app
-from flask import request, jsonify
+from flask import request
 from flask_babel import gettext
 from flask_security import login_required, current_user
 from pgadmin.misc.file_manager import Filemanager
-from pgadmin.tools.sqleditor.command import QueryToolCommand
+from pgadmin.tools.sqleditor.command import QueryToolCommand, ObjectRegistry, \
+    SQLFilter
 from pgadmin.tools.sqleditor.utils.constant_definition import ASYNC_OK, \
     ASYNC_EXECUTION_ABORTED, \
     CONNECTION_STATUS_MESSAGE_MAPPING, TX_STATUS_INERROR
@@ -33,7 +38,8 @@ from pgadmin.utils.ajax import make_json_response, bad_request, \
     success_return, internal_server_error
 from pgadmin.utils.driver import get_driver
 from pgadmin.utils.exception import ConnectionLost, SSHTunnelConnectionLost, \
-    CryptKeyMissing
+    CryptKeyMissing, ObjectGone
+from pgadmin.browser.utils import underscore_unescape
 from pgadmin.utils.menu import MenuItem
 from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete
 from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
@@ -48,10 +54,13 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS, \
     SERVER_CONNECTION_CLOSED, ERROR_MSG_TRANS_ID_NOT_FOUND, ERROR_FETCHING_DATA
 from pgadmin.model import Server, ServerGroup
 from pgadmin.tools.schema_diff.node_registry import SchemaDiffRegistry
+from pgadmin.settings import get_setting
+from pgadmin.utils.preferences import Preferences
 
 MODULE_NAME = 'sqleditor'
 TRANSACTION_STATUS_CHECK_FAILED = gettext("Transaction status check failed.")
 _NODES_SQL = 'nodes.sql'
+sqleditor_close_session_lock = Lock()
 
 
 class SqlEditorModule(PgAdminModule):
@@ -73,13 +82,6 @@ class SqlEditorModule(PgAdminModule):
                      url=url_for('help.static', filename='index.html'))
         ]}
 
-    def get_own_javascripts(self):
-        return [{
-            'name': 'pgadmin.sqleditor',
-            'path': url_for('sqleditor.index') + "sqleditor",
-            'when': None
-        }]
-
     def get_panels(self):
         return []
 
@@ -89,6 +91,15 @@ class SqlEditorModule(PgAdminModule):
             list: URL endpoints for sqleditor module
         """
         return [
+            'sqleditor.initialize_viewdata',
+            'sqleditor.initialize_sqleditor',
+            'sqleditor.initialize_sqleditor_with_did',
+            'sqleditor.filter_validate',
+            'sqleditor.filter',
+            'sqleditor.panel',
+            'sqleditor.close',
+            'sqleditor.update_sqleditor_connection',
+
             'sqleditor.view_data_start',
             'sqleditor.query_tool_start',
             'sqleditor.poll',
@@ -117,6 +128,7 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.get_macros',
             'sqleditor.set_macros',
             'sqleditor.get_new_connection_data',
+            'sqleditor.get_new_connection_servers',
             'sqleditor.get_new_connection_database',
             'sqleditor.get_new_connection_user',
             'sqleditor._check_server_connection_status',
@@ -125,6 +137,20 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.connect_server_with_user',
         ]
 
+    def on_logout(self, user):
+        """
+        This is a callback function when user logout from pgAdmin
+        :param user:
+        :return:
+        """
+        with sqleditor_close_session_lock:
+            if 'gridData' in session:
+                for trans_id in session['gridData']:
+                    close_sqleditor_session(trans_id)
+
+                # Delete all grid data from session variable
+                del session['gridData']
+
     def register_preferences(self):
         register_query_tool_preferences(self)
 
@@ -140,6 +166,480 @@ def index():
     )
 
 
+@blueprint.route("/filter", endpoint='filter')
+@login_required
+def show_filter():
+    return render_template(MODULE_NAME + '/filter.html')
+
+
+@blueprint.route(
+    '/initialize/viewdata/<int:trans_id>/<int:cmd_type>/<obj_type>/'
+    '<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
+    methods=["PUT", "POST"],
+    endpoint="initialize_viewdata"
+)
+@login_required
+def initialize_viewdata(trans_id, cmd_type, obj_type, sgid, sid, did, obj_id):
+    """
+    This method is responsible for creating an asynchronous connection.
+    After creating the connection it will instantiate and initialize
+    the object as per the object type. It will also create a unique
+    transaction id and store the information into session variable.
+
+    Args:
+        cmd_type: Contains value for which menu item is clicked.
+        obj_type: Contains type of selected object for which data grid to
+        be render
+        sgid: Server group Id
+        sid: Server Id
+        did: Database Id
+        obj_id: Id of currently selected object
+    """
+
+    if request.data:
+        filter_sql = json.loads(request.data, encoding='utf-8')
+    else:
+        filter_sql = request.args or request.form
+
+    # Create asynchronous connection using random connection id.
+    conn_id = str(random.randint(1, 9999999))
+    try:
+        manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+        # default_conn is same connection which is created when user connect to
+        # database from tree
+        default_conn = manager.connection(did=did)
+        conn = manager.connection(did=did, conn_id=conn_id,
+                                  auto_reconnect=False,
+                                  use_binary_placeholder=True,
+                                  array_to_string=True)
+    except (ConnectionLost, SSHTunnelConnectionLost):
+        raise
+    except Exception as e:
+        current_app.logger.error(e)
+        return internal_server_error(errormsg=str(e))
+
+    status, msg = default_conn.connect()
+    if not status:
+        current_app.logger.error(msg)
+        return internal_server_error(errormsg=str(msg))
+
+    status, msg = conn.connect()
+    if not status:
+        current_app.logger.error(msg)
+        return internal_server_error(errormsg=str(msg))
+    try:
+        # if object type is partition then it is nothing but a table.
+        if obj_type == 'partition':
+            obj_type = 'table'
+
+        # Get the object as per the object type
+        command_obj = ObjectRegistry.get_object(
+            obj_type, conn_id=conn_id, sgid=sgid, sid=sid,
+            did=did, obj_id=obj_id, cmd_type=cmd_type,
+            sql_filter=filter_sql
+        )
+    except ObjectGone:
+        raise
+    except Exception as e:
+        current_app.logger.error(e)
+        return internal_server_error(errormsg=str(e))
+
+    if 'gridData' not in session:
+        sql_grid_data = dict()
+    else:
+        sql_grid_data = session['gridData']
+
+    # Use pickle to store the command object which will be used later by the
+    # sql grid module.
+    sql_grid_data[str(trans_id)] = {
+        # -1 specify the highest protocol version available
+        'command_obj': pickle.dumps(command_obj, -1)
+    }
+
+    # Store the grid dictionary into the session variable
+    session['gridData'] = sql_grid_data
+
+    return make_json_response(
+        data={
+            'conn_id': conn_id
+        }
+    )
+
+
+@blueprint.route(
+    '/panel/<int:trans_id>',
+    methods=["POST"],
+    endpoint='panel'
+)
+def panel(trans_id):
+    """
+    This method calls index.html to render the data grid.
+
+    Args:
+        trans_id: unique transaction id
+    """
+
+    params = None
+    if request.args:
+        params = {k: v for k, v in request.args.items()}
+
+    close_url = ''
+    if request.form:
+        params['title'] = underscore_unescape(request.form['title'])
+        close_url = request.form['close_url']
+        if 'sql_filter' in request.form:
+            params['sql_filter'] = request.form['sql_filter']
+        if 'query_url' in request.form:
+            params['query_url'] = request.form['query_url']
+
+    params['trans_id'] = trans_id
+
+    # We need client OS information to render correct Keyboard shortcuts
+    params['client_platform'] = UserAgent(request.headers.get('User-Agent'))\
+        .platform
+
+    params['is_linux'] = False
+    from sys import platform as _platform
+    if "linux" in _platform:
+        params['is_linux'] = True
+
+    # Fetch the server details
+    params['bgcolor'] = None
+    params['fgcolor'] = None
+
+    s = Server.query.filter_by(id=params['sid']).first()
+    if s and s.bgcolor:
+        # If background is set to white means we do not have to change
+        # the title background else change it as per user specified
+        # background
+        if s.bgcolor != '#ffffff':
+            params['bgcolor'] = s.bgcolor
+        params['fgcolor'] = s.fgcolor or 'black'
+
+    params['server_name'] = s.name
+    params['username'] = s.username
+    params['layout'] = get_setting('SQLEditor/Layout')
+    params['macros'] = get_user_macros()
+    params['is_desktop_mode'] = current_app.PGADMIN_RUNTIME
+
+    return render_template(
+        "sqleditor/index.html",
+        close_url=close_url,
+        params=json.dumps(params),
+        requirejs=True,
+        basejs=True,
+    )
+
+
+@blueprint.route(
+    '/initialize/sqleditor/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
+    methods=["POST"], endpoint='initialize_sqleditor_with_did'
+)
+@blueprint.route(
+    '/initialize/sqleditor/<int:trans_id>/<int:sgid>/<int:sid>',
+    methods=["POST"], endpoint='initialize_sqleditor'
+)
+@login_required
+def initialize_sqleditor(trans_id, sgid, sid, did=None):
+    """
+    This method is responsible for instantiating and initializing
+    the query tool object. It will also create a unique
+    transaction id and store the information into session variable.
+
+    Args:
+        sgid: Server group Id
+        sid: Server Id
+        did: Database Id
+    """
+    connect = True
+    # Read the data if present. Skipping read may cause connection
+    # reset error if data is sent from the client
+    if request.data:
+        _ = request.data
+
+    req_args = request.args
+    if ('recreate' in req_args and
+            req_args['recreate'] == '1'):
+        connect = False
+
+    is_error, errmsg, conn_id, version = _init_sqleditor(
+        trans_id, connect, sgid, sid, did)
+    if is_error:
+        return errmsg
+
+    return make_json_response(
+        data={
+            'connId': str(conn_id),
+            'serverVersion': version,
+        }
+    )
+
+
+def _connect(conn, **kwargs):
+    """
+    Connect the database.
+    :param conn: Connection instance.
+    :param kwargs: user, role and password data from user.
+    :return:
+    """
+    user = None
+    role = None
+    password = None
+    is_ask_password = False
+    if 'user' in kwargs and 'role' in kwargs:
+        user = kwargs['user']
+        role = kwargs['role'] if kwargs['role'] else None
+        password = kwargs['password'] if kwargs['password'] else None
+        is_ask_password = True
+    if user:
+        status, msg = conn.connect(user=user, role=role,
+                                   password=password)
+    else:
+        status, msg = conn.connect()
+
+    return status, msg, is_ask_password, user, role, password
+
+
+def _init_sqleditor(trans_id, connect, sgid, sid, did, **kwargs):
+    # Create asynchronous connection using random connection id.
+    conn_id = str(random.randint(1, 9999999))
+
+    manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid)
+
+    if did is None:
+        did = manager.did
+    try:
+        command_obj = ObjectRegistry.get_object(
+            'query_tool', conn_id=conn_id, sgid=sgid, sid=sid, did=did
+        )
+    except Exception as e:
+        current_app.logger.error(e)
+        return True, internal_server_error(errormsg=str(e)), '', ''
+
+    try:
+        conn = manager.connection(did=did, conn_id=conn_id,
+                                  auto_reconnect=False,
+                                  use_binary_placeholder=True,
+                                  array_to_string=True)
+
+        if connect:
+            status, msg, is_ask_password, user, role, password = _connect(
+                conn, **kwargs)
+            if not status:
+                current_app.logger.error(msg)
+                if is_ask_password:
+                    server = Server.query.filter_by(id=sid).first()
+                    return True, make_json_response(
+                        success=0,
+                        status=428,
+                        result=render_template(
+                            'servers/password.html',
+                            server_label=server.name,
+                            username=user,
+                            errmsg=msg,
+                            _=gettext,
+                            allow_save_password=True if
+                            ALLOW_SAVE_PASSWORD and
+                            session['allow_save_password'] else False,
+                        )
+                    ), '', ''
+                else:
+                    return True, internal_server_error(
+                        errormsg=str(msg)), '', ''
+    except (ConnectionLost, SSHTunnelConnectionLost) as e:
+        current_app.logger.error(e)
+        raise
+    except Exception as e:
+        current_app.logger.error(e)
+        return True, internal_server_error(errormsg=str(e)), '', ''
+
+    if 'gridData' not in session:
+        sql_grid_data = dict()
+    else:
+        sql_grid_data = session['gridData']
+
+    # Set the value of auto commit and auto rollback specified in Preferences
+    pref = Preferences.module('sqleditor')
+    command_obj.set_auto_commit(pref.preference('auto_commit').get())
+    command_obj.set_auto_rollback(pref.preference('auto_rollback').get())
+
+    # Use pickle to store the command object which will be used
+    # later by the sql grid module.
+    sql_grid_data[str(trans_id)] = {
+        # -1 specify the highest protocol version available
+        'command_obj': pickle.dumps(command_obj, -1)
+    }
+
+    # Store the grid dictionary into the session variable
+    session['gridData'] = sql_grid_data
+
+    return False, '', conn_id, manager.version
+
+
+@blueprint.route(
+    '/initialize/sqleditor/update_connection/<int:trans_id>/'
+    '<int:sgid>/<int:sid>/<int:did>',
+    methods=["POST"], endpoint='update_sqleditor_connection'
+)
+def update_sqleditor_connection(trans_id, sgid, sid, did):
+    # Remove transaction Id.
+    with sqleditor_close_session_lock:
+        data = json.loads(request.data, encoding='utf-8')
+
+        if 'gridData' not in session:
+            return make_json_response(data={'status': True})
+
+        grid_data = session['gridData']
+
+        # Return from the function if transaction id not found
+        if str(trans_id) not in grid_data:
+            return make_json_response(data={'status': True})
+
+        connect = True
+
+        req_args = request.args
+        if ('recreate' in req_args and
+                req_args['recreate'] == '1'):
+            connect = False
+
+        new_trans_id = str(random.randint(1, 9999999))
+        kwargs = {
+            'user': data['user'],
+            'role': data['role'] if 'role' in data else None,
+            'password': data['password'] if 'password' in data else None
+        }
+
+        is_error, errmsg, conn_id, version = _init_sqleditor(
+            new_trans_id, connect, sgid, sid, did, **kwargs)
+
+        if is_error:
+            return errmsg
+        else:
+            try:
+                # Check the transaction and connection status
+                status, error_msg, conn, trans_obj, session_obj = \
+                    check_transaction_status(trans_id)
+
+                status, error_msg, new_conn, new_trans_obj, new_session_obj = \
+                    check_transaction_status(new_trans_id)
+
+                new_session_obj['primary_keys'] = session_obj[
+                    'primary_keys'] if 'primary_keys' in session_obj else None
+                new_session_obj['columns_info'] = session_obj[
+                    'columns_info'] if 'columns_info' in session_obj else None
+                new_session_obj['client_primary_key'] = session_obj[
+                    'client_primary_key'] if 'client_primary_key'\
+                                             in session_obj else None
+
+                close_sqleditor_session(trans_id)
+                # Remove the information of unique transaction id from the
+                # session variable.
+                grid_data.pop(str(trans_id), None)
+                session['gridData'] = grid_data
+            except Exception as e:
+                current_app.logger.error(e)
+
+    return make_json_response(
+        data={
+            'connId': str(conn_id),
+            'serverVersion': version,
+            'trans_id': new_trans_id
+        }
+    )
+
+
+@blueprint.route('/close/<int:trans_id>', methods=["DELETE"], endpoint='close')
+def close(trans_id):
+    """
+    This method is used to close the asynchronous connection
+    and remove the information of unique transaction id from
+    the session variable.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    with sqleditor_close_session_lock:
+        if 'gridData' not in session:
+            return make_json_response(data={'status': True})
+
+        grid_data = session['gridData']
+        # Return from the function if transaction id not found
+        if str(trans_id) not in grid_data:
+            return make_json_response(data={'status': True})
+
+        try:
+            close_sqleditor_session(trans_id)
+            # Remove the information of unique transaction id from the
+            # session variable.
+            grid_data.pop(str(trans_id), None)
+            session['gridData'] = grid_data
+        except Exception as e:
+            current_app.logger.error(e)
+            return internal_server_error(errormsg=str(e))
+
+    return make_json_response(data={'status': True})
+
+
+@blueprint.route(
+    '/filter/validate/<int:sid>/<int:did>/<int:obj_id>',
+    methods=["PUT", "POST"], endpoint='filter_validate'
+)
+@login_required
+def validate_filter(sid, did, obj_id):
+    """
+    This method is used to validate the sql filter.
+
+    Args:
+        sid: Server Id
+        did: Database Id
+        obj_id: Id of currently selected object
+    """
+    if request.data:
+        filter_sql = json.loads(request.data, encoding='utf-8')
+    else:
+        filter_sql = request.args or request.form
+
+    try:
+        # Create object of SQLFilter class
+        sql_filter_obj = SQLFilter(sid=sid, did=did, obj_id=obj_id)
+
+        # Call validate_filter method to validate the SQL.
+        status, res = sql_filter_obj.validate_filter(filter_sql)
+    except ObjectGone:
+        raise
+    except Exception as e:
+        current_app.logger.error(e)
+        return internal_server_error(errormsg=str(e))
+
+    return make_json_response(data={'status': status, 'result': res})
+
+
+def close_sqleditor_session(trans_id):
+    """
+    This function is used to cancel the transaction and release the connection.
+
+    :param trans_id: Transaction id
+    :return:
+    """
+    if 'gridData' in session and str(trans_id) in session['gridData']:
+        cmd_obj_str = session['gridData'][str(trans_id)]['command_obj']
+        # Use pickle.loads function to get the command object
+        cmd_obj = pickle.loads(cmd_obj_str)
+
+        # if connection id is None then no need to release the connection
+        if cmd_obj.conn_id is not None:
+            manager = get_driver(
+                PG_DEFAULT_DRIVER).connection_manager(cmd_obj.sid)
+            if manager is not None:
+                conn = manager.connection(
+                    did=cmd_obj.did, conn_id=cmd_obj.conn_id)
+
+                # Release the connection
+                if conn.connected():
+                    conn.cancel_transaction(cmd_obj.conn_id, cmd_obj.did)
+                    manager.release(did=cmd_obj.did, conn_id=cmd_obj.conn_id)
+
+
 def check_transaction_status(trans_id):
     """
     This function is used to check the transaction id
@@ -286,8 +786,6 @@ def start_view_data(trans_id):
             'filter_applied': filter_applied,
             'limit': limit, 'can_edit': can_edit,
             'can_filter': can_filter, 'sql': sql,
-            'info_notifier_timeout':
-                blueprint.info_notifier_timeout.get() * 1000
         }
     )
 
@@ -1345,7 +1843,7 @@ def start_query_download_tool(trans_id):
             errormsg=TRANSACTION_STATUS_CHECK_FAILED
         )
 
-    data = request.values if request.values else None
+    data = request.values if request.values else request.json
     if data is None:
         return make_json_response(
             status=410,
@@ -1534,8 +2032,12 @@ def _check_server_connection_status(sgid, sid=None):
     '/new_connection_dialog/<int:sgid>/<int:sid>',
     methods=["GET"], endpoint='get_new_connection_data'
 )
+@blueprint.route(
+    '/new_connection_dialog',
+    methods=["GET"], endpoint='get_new_connection_servers'
+)
 @login_required
-def get_new_connection_data(sgid, sid=None):
+def get_new_connection_data(sgid=None, sid=None):
     """
     This method is used to get required data for get new connection.
     :extract_sql_from_network_parameters,
@@ -1822,7 +2324,7 @@ def connect_server(sid, usr=None):
             )
 
     view = SchemaDiffRegistry.get_node_view('server')
-    return view.connect(server.servergroup_id, sid, user_name=user)
+    return view.connect(server.servergroup_id, sid, user_name=user, resp_json=True)
 
 
 @blueprint.route(
@@ -1886,7 +2388,8 @@ def clear_query_history(trans_id):
     status, error_msg, conn, trans_obj, session_ob = \
         check_transaction_status(trans_id)
 
-    return QueryHistory.clear(current_user.id, trans_obj.sid, conn.db)
+    filter = request.json
+    return QueryHistory.clear(current_user.id, trans_obj.sid, conn.db, filter)
 
 
 @blueprint.route(
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
deleted file mode 100644
index 0c5688976..000000000
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ /dev/null
@@ -1,401 +0,0 @@
-#main-editor_panel {
-  position: absolute;
-  left: 0;
-  right: 0;
-  top : 0;
-  bottom: 0;
-}
-
-.sql-editor {
-  position: absolute;
-  left: 0;
-  right: 0;
-  top : 0;
-  bottom: 0;
-}
-
-.filter-container .CodeMirror-scroll {
-  min-height: 120px;
-}
-
-.filter-container .sql-textarea{
-  box-shadow: 0.1px 0.1px 3px #000;
-  margin-bottom: 5px;
-}
-
-#filter .btn-group {
-  margin-right: 2px;
-  float: right;
-}
-
-#filter .btn-group > button {
-  padding: 3px;
-}
-
-#filter .btn-group .btn-primary {
-  margin: auto !important;
-}
-
-.has-select-all table thead tr th:nth-child(1),
-.has-select-all table tbody tr td:nth-child(1) {
-  width: 35px !important;
-  max-width: 35px !important;
-  min-width: 35px !important;
-}
-.sql-status-cell {
-  max-width: 30px;
-}
-
-.btn-circle {
-  width: 16px;
-  height: 16px;
-  text-align: center;
-  padding: 0;
-  font-size: 10px;
-  line-height: 1.428571429;
-  border-radius: 10px;
-  cursor: auto;
-}
-
-.visibility-hidden {
-  visibility: hidden;
-}
-
-.sql-editor-mark {
-  border-bottom: 2px dotted red;
-}
-
-.CodeMirror {
-  min-height: 100%;
-  height: 100%;
-}
-
-#output-panel {
-  height: 100% !important;
-}
-
-.sql-editor-explain {
-  height: 100%;
-  width: 100%;
-  overflow: auto;
-}
-
-
-.sqleditor-hint {
-  padding-left: 20px;
-}
-
-.CodeMirror-hint .fa::before {
-  padding-right: 7px;
-}
-
-h2 {
-  font-size: 10pt;
-  border-bottom: 1px dotted gray;
-}
-
-ul {
-  margin-left: 0;
-  padding: 0;
-  cursor: default;
-}
-
-li {
-  padding: 0 0 0 0px;
-  list-style: none;
-  margin: 0;
-}
-
-#datagrid {
-  background: white;
-  outline: 0;
-  font-size: 9pt;
-}
-
-#datagrid .slick-header-column.ui-state-default {
-    height: 32px !important;
-}
-
-#datagrid .grid-header label {
-  display: inline-block;
-  font-weight: bold;
-  margin: auto auto auto 6px;
-}
-
-.grid-header .ui-icon {
-  margin: 4px 4px auto 6px;
-  background-color: transparent;
-  border-color: transparent;
-}
-
-.slick-row .cell-actions {
-  text-align: left;
-}
-
-/* Slick.Editors.Text, Slick.Editors.Date */
-#datagrid .slick-header > input.editor-text {
-  width: 100%;
-  height: 100%;
-  border: 0;
-  margin: 0;
-  background: transparent;
-  outline: 0;
-  padding: 0;
-}
-
-/* Slick.Editors.Checkbox */
-#datagrid .slick-header > input.editor-checkbox {
-  margin: 0;
-  height: 100%;
-  padding: 0;
-  border: 0;
-}
-
-.slick-row.selected .cell-selection {
-  background-color: transparent; /* show default selected row background */
-}
-
-#datagrid .slick-header .ui-state-default,
-#datagrid .slick-header .ui-widget-content.ui-state-default,
-#datagrid .slick-header .ui-widget-header .ui-state-default {
-  background: none;
-}
-
-#datagrid .slick-header .slick-header-column .column-name {
-  font-weight: bold;
-  display: block;
-}
-
-.column-description {
-  display: table-cell;
-}
-
-.long_text_editor {
-  margin-left: 5px;
-  font-size: 12px !important;
-  padding: 1px 7px;
-}
-
-/* Slick.Editors.Text, Slick.Editors.Date */
-input.editor-text {
-  width: 100%;
-  height: 100%;
-  border: 0;
-  margin: 0;
-  background: transparent;
-  outline: 0;
-  padding: 0;
-}
-
-/* Slick.Editors.Text, Slick.Editors.Date */
-textarea.editor-text {
-  width: 100%;
-  height: 100%;
-  border: 0;
-  margin: 0;
-  background: transparent;
-  outline: 0;
-  padding: 0;
-}
-
-/* Slick.Editors.Checkbox */
-input.editor-checkbox {
-  margin: 0;
-  height: 100%;
-  padding: 0;
-  border: 0;
-}
-
-/* remove outlined border on focus */
-input.editor-checkbox:focus {
-  outline: none;
-}
-
-.slick-cell span[data-cell-type="row-header-selector"] {
-  display: block;
-  text-align: center;
-}
-
-/*
-  SlickGrid, To fix the issue of width misalignment between Column Header &
-  actual Column in Mozilla Firefox browser
-  Ref: https://github.com/mleibman/SlickGrid/issues/742
-*/
-.slickgrid, .slickgrid *, .slick-header-column {
-  box-sizing: content-box;
-  -moz-box-sizing: content-box;
-  -webkit-box-sizing: content-box;
-  -ms-box-sizing: content-box;
-}
-
-.select-all-icon {
-  margin-left: 9px;
-  margin-right: 4px;
-  vertical-align: bottom;
-  position: absolute;
-  bottom: 4px;
-  right: 0;
-}
-
-/* Style for text editor */
-.pg_buttons {
-  text-align:right;
-}
-
-#datagrid .slick-row .slick-cell {
-  white-space: pre;
-}
-
-.connection_status {
-  font-size: 1rem;
-  width: 40px;
-}
-
-.ajs-body .warn-header {
-  font-size: 13px;
-  font-weight: bold;
-  line-height: 3em;
-}
-
-.ajs-body .warn-body {
-  font-size: 13px;
-}
-
-.ajs-body .warn-footer {
-  font-size: 13px;
-  line-height: 3em;
-}
-
-/* For Filter status bar */
-.data_sorting_dialog .pg-prop-status-bar {
-  position: absolute;
-  left: 0;
-  right: 0;
-  bottom: 0;
-  z-index: 5;
-}
-
-.data_sorting_dialog .CodeMirror-gutter-wrapper {
-  left: -30px !important;
-}
-
-.data_sorting_dialog .CodeMirror-gutters {
-  left: 0px !important;
-}
-
-.data_sorting_dialog .custom_height_css_class {
-  height: 100px;
-}
-
-.data_sorting_dialog .data_sorting {
-  padding: 10px 0px;
-}
-
-.connection-status-hide {
-  display: none !important;
-}
-
-/* For geometry data viewer panel */
-.sql-editor-geometry-viewer{
-  width: 100%;
-  height: 100%;
-}
-
-.geometry-viewer-container {
-  width: 100%;
-  height: 100%;
-  background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAANElEQVQoU2O8e/fuf2VlZUYGLAAkB5bApggmBteJrAiZjWI0SAJkIrKVxCvAawVeRxLyJgB+Ajc1cwux9wAAAABJRU5ErkJggg==);
-  /* Let's keep the background as fff irrespective of theme
-   * make geometry viewer look clean
-   */
-  background-color: #fff;
- }
-
-
-/* For geometry column button */
-.div-view-geometry-column, .editable-column-header-icon {
-  float: right;
-  height: 100%;
-  display: flex;
-  display: -webkit-flex;
-  align-items: center;
-  padding-right: 6px;
-}
-
-/* For leaflet popup */
-.leaflet-popup-content-wrapper {
-  border-radius: 2px;
-}
-
-.leaflet-popup-content {
-  margin: 5px;
-  padding: 10px 10px 0;
-  overflow-y: scroll;
-  overflow-x: hidden;
-}
-
-/* For geometry viewer property table */
-.view-geometry-property-table {
-  table-layout: fixed;
-  white-space: nowrap;
-  padding: 0;
-}
-
-.view-geometry-property-table th {
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-.view-geometry-property-table td {
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-
-/* For geometry viewer info control */
-.geometry-viewer-info-control {
-  padding: 5px;
-  background: white;
-  border: 2px solid rgba(0, 0, 0, 0.2);
-  background-clip: padding-box;
-  border-radius: 2px;
-}
-
-.geometry-viewer-info-control i{
-  margin: 0 0 0 4px;
-}
-
-.hide-vertical-scrollbar {
-  overflow-y: hidden;
-}
-
-/*  Macros */
-
-.macro-tab {
-  top: 0px !important;
-}
-
-.macro-tab .tab-pane {
-  padding: 0px !important;
-}
-
-.macro_dialog .CodeMirror {
-  overflow-y: auto;
-  resize: vertical;
-}
-.macro_dialog .sql-cell > div {
-  overflow-y: auto;
-  resize: vertical;
-}
-
-.macro_dialog .CodeMirror-cursor {
-    width: 1px !important;
-    height: 18px !important;
-}
-
-.macro_dialog  .pg-prop-status-bar {
-  z-index: 1;
-}
-
-.new-connection-dialog-style {
-  width: 100% !important;
-}
diff --git a/web/pgadmin/tools/sqleditor/static/img/loading.gif b/web/pgadmin/tools/sqleditor/static/img/loading.gif
deleted file mode 100644
index 500fa0860e2eb9883f86c30da579008475f596c4..0000000000000000000000000000000000000000
GIT binary patch
diff --git a/web/pgadmin/tools/sqleditor/static/js/SQLEditorModule.js b/web/pgadmin/tools/sqleditor/static/js/SQLEditorModule.js
new file mode 100644
index 000000000..7a2501f63
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/SQLEditorModule.js
@@ -0,0 +1,424 @@
+
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import * as showViewData from './show_view_data';
+import * as showQueryTool from './show_query_tool';
+import * as toolBar from 'pgadmin.browser.toolbar';
+import * as panelTitleFunc from './sqleditor_title';
+import * as commonUtils from 'sources/utils';
+import $ from 'jquery';
+import url_for from 'sources/url_for';
+import _ from 'lodash';
+import alertify from 'pgadmin.alertifyjs';
+var wcDocker = window.wcDocker;
+import pgWindow from 'sources/window';
+import pgAdmin from 'sources/pgadmin';
+import pgBrowser from 'pgadmin.browser';
+import 'pgadmin.file_manager';
+import 'pgadmin.tools.user_management';
+import gettext from 'sources/gettext';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import QueryToolComponent from './components/QueryToolComponent';
+import ModalProvider from '../../../../static/js/helpers/ModalProvider';
+
+
+export function setPanelTitle(queryToolPanel, panelTitle) {
+  queryToolPanel.title('<span title="'+panelTitle+'">'+panelTitle+'</span>');
+}
+
+export default class SQLEditor {
+  static instance;
+
+  static getInstance(...args) {
+    if(!SQLEditor.instance) {
+      SQLEditor.instance = new SQLEditor(...args);
+    }
+    return SQLEditor.instance;
+  }
+
+  SUPPORTED_NODES = [
+    'table', 'view', 'mview',
+    'foreign_table', 'catalog_object', 'partition',
+  ];
+
+  /* Enable/disable View data menu in tools based
+  * on node selected. if selected node is present
+  * in supportedNodes, menu will be enabled
+  * otherwise disabled.
+  */
+  viewMenuEnabled(obj) {
+    var isEnabled = (() => {
+      if (!_.isUndefined(obj) && !_.isNull(obj))
+        return (_.indexOf(this.SUPPORTED_NODES, obj._type) !== -1 ? true : false);
+      else
+        return false;
+    })();
+
+    toolBar.enable(gettext('View Data'), isEnabled);
+    toolBar.enable(gettext('Filtered Rows'), isEnabled);
+    return isEnabled;
+  }
+
+  /* Enable/disable Query tool menu in tools based
+  * on node selected. if selected node is present
+  * in unsupported_nodes, menu will be disabled
+  * otherwise enabled.
+  */
+  queryToolMenuEnabled(obj) {
+    var isEnabled = (() => {
+      if (!_.isUndefined(obj) && !_.isNull(obj)) {
+        if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) {
+          if (obj._type == 'database' && obj.allowConn) {
+            return true;
+          } else if (obj._type != 'database') {
+            return true;
+          } else {
+            return false;
+          }
+        } else {
+          return false;
+        }
+      } else {
+        return false;
+      }
+    })();
+
+    toolBar.enable(gettext('Query Tool'), isEnabled);
+    return isEnabled;
+  }
+
+  init() {
+    if(this.initialized)
+      return;
+    this.initialized = true;
+
+    let self = this;
+    /* Cache may take time to load for the first time
+     * Keep trying till available
+     */
+    let cacheIntervalId = setInterval(function() {
+      if(pgBrowser.preference_version() > 0) {
+        self.preferences = pgBrowser.get_preferences_for_module('sqleditor');
+        clearInterval(cacheIntervalId);
+      }
+    },0);
+
+    pgBrowser.onPreferencesChange('sqleditor', function() {
+      self.preferences = pgBrowser.get_preferences_for_module('sqleditor');
+    });
+
+    // Define the nodes on which the menus to be appear
+    var menus = [{
+      name: 'query_tool',
+      module: this,
+      applies: ['tools'],
+      callback: 'showQueryTool',
+      enable: self.queryToolMenuEnabled,
+      priority: 1,
+      label: gettext('Query Tool'),
+      icon: 'pg-font-icon icon-query_tool',
+      data:{
+        applies: 'tools',
+        data_disabled: gettext('Please select a database from the browser tree to access Query Tool.'),
+      },
+    }];
+
+    // Create context menu
+    for (const supportedNode of self.SUPPORTED_NODES) {
+      menus.push({
+        name: 'view_all_rows_context_' + supportedNode,
+        node: supportedNode,
+        module: this,
+        data: {
+          mnuid: 3,
+        },
+        applies: ['context', 'object'],
+        callback: 'showViewData',
+        enable: self.viewMenuEnabled,
+        category: 'view_data',
+        priority: 101,
+        label: gettext('All Rows'),
+      }, {
+        name: 'view_first_100_rows_context_' + supportedNode,
+        node: supportedNode,
+        module: this,
+        data: {
+          mnuid: 1,
+        },
+        applies: ['context', 'object'],
+        callback: 'showViewData',
+        enable: self.viewMenuEnabled,
+        category: 'view_data',
+        priority: 102,
+        label: gettext('First 100 Rows'),
+      }, {
+        name: 'view_last_100_rows_context_' + supportedNode,
+        node: supportedNode,
+        module: this,
+        data: {
+          mnuid: 2,
+        },
+        applies: ['context', 'object'],
+        callback: 'showViewData',
+        enable: self.viewMenuEnabled,
+        category: 'view_data',
+        priority: 103,
+        label: gettext('Last 100 Rows'),
+      }, {
+        name: 'view_filtered_rows_context_' + supportedNode,
+        node: supportedNode,
+        module: this,
+        data: {
+          mnuid: 4,
+        },
+        applies: ['context', 'object'],
+        callback: 'showFilteredRow',
+        enable: self.viewMenuEnabled,
+        category: 'view_data',
+        priority: 104,
+        label: gettext('Filtered Rows...'),
+      });
+    }
+
+    pgBrowser.add_menu_category('view_data', gettext('View/Edit Data'), 100, '');
+    pgBrowser.add_menus(menus);
+
+    // Creating a new pgAdmin.Browser frame to show the data.
+    var frame = new pgAdmin.Browser.Frame({
+      name: 'frm_sqleditor',
+      showTitle: true,
+      isCloseable: true,
+      isRenamable: true,
+      isPrivate: true,
+      url: 'about:blank',
+    });
+
+    // Load the newly created frame
+    frame.load(pgBrowser.docker);
+  }
+
+  // This is a callback function to show data when user click on menu item.
+  showViewData(data, i) {
+    const transId = commonUtils.getRandomInt(1, 9999999);
+    showViewData.showViewData(this, pgBrowser, alertify, data, i, transId);
+  }
+
+  // This is a callback function to show filtered data when user click on menu item.
+  showFilteredRow(data, i) {
+    const transId = commonUtils.getRandomInt(1, 9999999);
+    showViewData.showViewData(this, pgBrowser, alertify, data, i, transId, true, this.preferences);
+  }
+
+  // This is a callback function to show query tool when user click on menu item.
+  showQueryTool(url, treeIdentifier) {
+    const transId = commonUtils.getRandomInt(1, 9999999);
+    var t = pgBrowser.tree,
+      i = treeIdentifier || t.selected(),
+      d = i ? t.itemData(i) : undefined;
+
+    //Open query tool with create script if copy_sql_to_query_tool is true else open blank query tool
+    var preference = pgBrowser.get_preference('sqleditor', 'copy_sql_to_query_tool');
+    if(preference.value && !d._type.includes('coll-') && (url === '' || url['applies'] === 'tools')){
+      var stype = d._type.toLowerCase();
+      var data = {
+        'script': stype,
+        data_disabled: gettext('The selected tree node does not support this option.'),
+      };
+      pgBrowser.Node.callbacks.show_script(data);
+    } else {
+      if(d._type.includes('coll-')){
+        url = '';
+      }
+      showQueryTool.showQueryTool(this, pgBrowser, url, treeIdentifier, transId);
+    }
+  }
+
+  onPanelRename(queryToolPanel, panelData, is_query_tool) {
+    var temp_title = panelData.$titleText[0].textContent;
+    var is_dirty_editor = queryToolPanel.is_dirty_editor ? queryToolPanel.is_dirty_editor : false;
+    var title = queryToolPanel.is_dirty_editor ? panelData.$titleText[0].textContent.replace(/.$/, '') : temp_title;
+    alertify.prompt('', title,
+      // We will execute this function when user clicks on the OK button
+      function(evt, value) {
+        // Remove the leading and trailing white spaces.
+        value = value.trim();
+        if(value) {
+          var is_file = false;
+          if(panelData.$titleText[0].innerHTML.includes('File - ')) {
+            is_file = true;
+          }
+          var selected_item = pgBrowser.tree.selected();
+          var panel_titles = '';
+
+          if(is_query_tool) {
+            panel_titles = panelTitleFunc.getPanelTitle(pgBrowser, selected_item, value);
+          } else {
+            panel_titles = showViewData.generateDatagridTitle(pgBrowser, selected_item, value);
+          }
+          // Set title to the selected tab.
+          if (is_dirty_editor) {
+            panel_titles = panel_titles + ' *';
+          }
+          panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, is_query_tool, _.unescape(panel_titles), is_file);
+        }
+      },
+      // We will execute this function when user clicks on the Cancel
+      // button.  Do nothing just close it.
+      function(evt) { evt.cancel = false; }
+    ).set({'title': gettext('Rename Panel')});
+  }
+
+
+  openQueryToolPanel(trans_id, is_query_tool, panel_title, closeUrl, queryToolForm) {
+    let self = this;
+    var browser_preferences = pgBrowser.get_preferences_for_module('browser');
+    var propertiesPanel = pgBrowser.docker.findPanels('properties');
+    var queryToolPanel = pgBrowser.docker.addPanel('frm_sqleditor', wcDocker.DOCK.STACKED, propertiesPanel[0]);
+    queryToolPanel.trans_id = trans_id;
+    showQueryTool._set_dynamic_tab(pgBrowser, browser_preferences['dynamic_tabs']);
+
+    // Set panel title and icon
+    panelTitleFunc.setQueryToolDockerTitle(queryToolPanel, is_query_tool, _.unescape(panel_title));
+    queryToolPanel.focus();
+
+    // Listen on the panel closed event.
+    if (queryToolPanel.isVisible()) {
+      queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
+        $.ajax({
+          url: closeUrl,
+          method: 'DELETE',
+        });
+      });
+    }
+
+    queryToolPanel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function() {
+      queryToolPanel.trigger(wcDocker.EVENT.RESIZED);
+    });
+
+    commonUtils.registerDetachEvent(queryToolPanel);
+
+    // Listen on the panelRename event.
+    queryToolPanel.on(wcDocker.EVENT.RENAME, function(panelData) {
+      self.onPanelRename(queryToolPanel, panelData, is_query_tool);
+    });
+
+    var openQueryToolURL = function(j) {
+      // add spinner element
+      let $spinner_el =
+        $(`<div class="pg-sp-container">
+              <div class="pg-sp-content">
+                  <div class="row">
+                      <div class="col-12 pg-sp-icon"></div>
+                  </div>
+              </div>
+          </div>`).appendTo($(j).data('embeddedFrame').$container);
+
+      let init_poller_id = setInterval(function() {
+        var frameInitialized = $(j).data('frameInitialized');
+        if (frameInitialized) {
+          clearInterval(init_poller_id);
+          var frame = $(j).data('embeddedFrame');
+          if (frame) {
+            frame.onLoaded(()=>{
+              $spinner_el.remove();
+            });
+            frame.openHTML(queryToolForm);
+          }
+        }
+      }, 100);
+    };
+
+    openQueryToolURL(queryToolPanel);
+  }
+
+  launch(trans_id, panel_url, is_query_tool, panel_title, sURL=null, sql_filter=null) {
+    const self = this;
+    let closeUrl = url_for('sqleditor.close', {'trans_id': trans_id});
+    let queryToolForm = `
+      <form id="queryToolForm" action="${panel_url}" method="post">
+        <input id="title" name="title" hidden />
+        <input id="conn_title" name="conn_title" hidden />
+        <input name="close_url" value="${closeUrl}" hidden />`;
+
+
+    if(sURL){
+      queryToolForm +=`<input name="query_url" value="${sURL}" hidden />`;
+    }
+    if(sql_filter) {
+      queryToolForm +=`<textarea name="sql_filter" hidden>${sql_filter}</textarea>`;
+    }
+
+    /* Escape backslashes as it is stripped by back end */
+    queryToolForm +=`
+      </form>
+        <script>
+          document.getElementById("title").value = "${_.escape(panel_title.replace('\\', '\\\\'))}";
+          document.getElementById("queryToolForm").submit();
+        </script>
+      `;
+
+    var browser_preferences = pgBrowser.get_preferences_for_module('browser');
+    var open_new_tab = browser_preferences.new_browser_tab_open;
+    if (open_new_tab && open_new_tab.includes('qt')) {
+      var newWin = window.open('', '_blank');
+      if(newWin) {
+        newWin.document.write(queryToolForm);
+        newWin.document.title = panel_title;
+        // Send the signal to runtime, so that proper zoom level will be set.
+        setTimeout(function() {
+          pgBrowser.send_signal_to_runtime('Runtime new window opened');
+        }, 500);
+      } else {
+        return false;
+      }
+    } else {
+      /* On successfully initialization find the dashboard panel,
+       * create new panel and add it to the dashboard panel.
+       */
+      self.openQueryToolPanel(trans_id, is_query_tool, panel_title, closeUrl, queryToolForm);
+    }
+    return true;
+  }
+
+  setupPreferencesWorker() {
+    if (window.location == window.parent?.location) {
+      /* Sync the local preferences with the main window if in new tab */
+      setInterval(()=>{
+        if(pgWindow?.pgAdmin) {
+          if(pgAdmin.Browser.preference_version() < pgWindow.pgAdmin.Browser.preference_version()){
+            pgAdmin.Browser.preferences_cache = pgWindow.pgAdmin.Browser.preferences_cache;
+            pgAdmin.Browser.preference_version(pgWindow.pgAdmin.Browser.preference_version());
+            pgAdmin.Browser.triggerPreferencesChange('browser');
+            pgAdmin.Browser.triggerPreferencesChange('sqleditor');
+          }
+        }
+      }, 1000);
+    }
+  }
+
+  loadComponent(container, params) {
+    let panel = null;
+    let selectedNodeInfo = pgWindow.pgAdmin.Browser.tree.getTreeNodeHierarchy(
+      pgWindow.pgAdmin.Browser.tree.selected()
+    );
+    _.each(pgWindow.pgAdmin.Browser.docker.findPanels('frm_sqleditor'), function(p) {
+      if (p.trans_id == params.trans_id) {
+        panel = p;
+      }
+    });
+    this.setupPreferencesWorker();
+    ReactDOM.render(
+      <ModalProvider>
+        <QueryToolComponent params={params} pgWindow={pgWindow} pgAdmin={pgAdmin} panel={panel} selectedNodeInfo={selectedNodeInfo}/>
+      </ModalProvider>,
+      container
+    );
+  }
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
new file mode 100644
index 000000000..6d2af6045
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolComponent.jsx
@@ -0,0 +1,646 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React, {useCallback, useRef, useMemo, useState, useEffect} from 'react';
+import _ from 'lodash';
+import Layout, { LayoutHelper } from '../../../../../static/js/helpers/Layout';
+import EventBus from '../../../../../static/js/helpers/EventBus';
+import Query from './sections/Query';
+import { ConnectionBar } from './sections/ConnectionBar';
+import { ResultSet } from './sections/ResultSet';
+import { StatusBar } from './sections/StatusBar';
+import { MainToolBar } from './sections/MainToolBar';
+import { Messages } from './sections/Messages';
+import Theme from 'sources/Theme';
+import getApiInstance, {parseApiError} from '../../../../../static/js/api_instance';
+import url_for from 'sources/url_for';
+import { PANELS, QUERY_TOOL_EVENTS, CONNECTION_STATUS } from './QueryToolConstants';
+import { useInterval } from '../../../../../static/js/custom_hooks';
+import { Box } from '@material-ui/core';
+import { getDatabaseLabel, getTitle, setQueryToolDockerTitle } from '../sqleditor_title';
+import gettext from 'sources/gettext';
+import NewConnectionDialog from './dialogs/NewConnectionDialog';
+import { evalFunc } from '../../../../../static/js/utils';
+import { Notifications } from './sections/Notifications';
+import MacrosDialog from './dialogs/MacrosDialog';
+import Notifier from '../../../../../static/js/helpers/Notifier';
+import FilterDialog from './dialogs/FilterDialog';
+import { QueryHistory } from './sections/QueryHistory';
+import * as showQueryTool from '../show_query_tool';
+import * as commonUtils from 'sources/utils';
+import * as Kerberos from 'pgadmin.authenticate.kerberos';
+import PropTypes from 'prop-types';
+import { retrieveNodeName } from '../show_view_data';
+import 'wcdocker';
+import { useModal } from '../../../../../static/js/helpers/ModalProvider';
+
+export const QueryToolContext = React.createContext();
+export const QueryToolConnectionContext = React.createContext();
+export const QueryToolEventsContext = React.createContext();
+
+function fetchConnectionStatus(api, transId) {
+  return api.get(url_for('sqleditor.connection_status', {trans_id: transId}));
+}
+
+function initConnection(api, params, passdata) {
+  return api.post(url_for('NODE-server.connect_id', params), passdata);
+}
+
+function setPanelTitle(panel, title, qtState, dirty=false) {
+  if(title) {
+    title =title.split('\\').pop().split('/').pop();
+  } else if(qtState.current_file) {
+    title = qtState.current_file.split('\\').pop().split('/').pop();
+  } else {
+    title = qtState.params.title || 'Untitled';
+  }
+
+  title = title + (dirty ? '*': '');
+  if (qtState.is_new_tab) {
+    window.document.title = title;
+  } else {
+    setQueryToolDockerTitle(panel, true, title, qtState.current_file ? true : false);
+  }
+}
+export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedNodeInfo, panel, eventBusObj}) {
+  const containerRef = React.useRef(null);
+  const forceClose = React.useRef(false);
+  const [qtState, _setQtState] = useState({
+    preferences: {
+      browser: {}, sqleditor: {},
+    },
+    is_new_tab: window.location == window.parent?.location,
+    current_file: null,
+    obtaining_conn: true,
+    connected: false,
+    connection_status: null,
+    connection_status_msg: '',
+    params: {
+      ...params,
+      is_query_tool: params.is_query_tool == 'true' ? true : false,
+      node_name: retrieveNodeName(selectedNodeInfo),
+    },
+    connection_list: [{
+      sgid: params.sgid,
+      sid: params.sid,
+      did: params.did,
+      user: params.username,
+      role: null,
+      title: _.unescape(params.title),
+      conn_title: getTitle(
+        pgAdmin, null, selectedNodeInfo, true, params.server_name, params.database_name || getDatabaseLabel(selectedNodeInfo),
+        params.username, params.is_query_tool == 'true' ? true : false),
+      server_name: params.server_name,
+      database_name: params.database_name || getDatabaseLabel(selectedNodeInfo),
+      is_selected: true,
+    }],
+  });
+
+  const setQtState = (state)=>{
+    _setQtState((prev)=>({...prev,...evalFunc(null, state, prev)}));
+  };
+  const eventBus = useRef(eventBusObj || (new EventBus()));
+  const docker = useRef(null);
+  const api = useMemo(()=>getApiInstance(), []);
+  const modal = useModal();
+
+  /* Connection status poller */
+  let pollTime = qtState.preferences.sqleditor.connection_status_fetch_time > 0 ?
+    qtState.preferences.sqleditor.connection_status_fetch_time*1000 : -1;
+  /* No need to poll when the query is executing. Query poller will the txn status */
+  if(qtState.connection_status === CONNECTION_STATUS.TRANSACTION_STATUS_ACTIVE && qtState.connected) {
+    pollTime = -1;
+  }
+  useInterval(async ()=>{
+    try {
+      let {data: respData} = await fetchConnectionStatus(api, qtState.params.trans_id);
+      if(respData.data) {
+        setQtState({
+          connected: true,
+          connection_status: respData.data.status,
+        });
+      } else {
+        setQtState({
+          connected: false,
+          connection_status: null,
+          connection_status_msg: gettext('An unexpected error occurred - ensure you are logged into the application.')
+        });
+      }
+      if(respData.data.notifies) {
+        eventBus.current.fireEvent(QUERY_TOOL_EVENTS.PUSH_NOTICE, respData.data.notifies);
+      }
+    } catch (error) {
+      console.error(error);
+      setQtState({
+        connected: false,
+        connection_status: null,
+        connection_status_msg: parseApiError(error),
+      });
+    }
+  }, pollTime);
+
+  let defaultLayout = {
+    dockbox: {
+      mode: 'vertical',
+      children: [
+        {
+          mode: 'horizontal',
+          children: [
+            {
+              tabs: [
+                LayoutHelper.getPanel({id: PANELS.QUERY, title: gettext('Query'), content: <Query />}),
+                LayoutHelper.getPanel({id: PANELS.HISTORY, title: 'Query History', content: <QueryHistory />,
+                  cached: undefined}),
+              ],
+            },
+            {
+              size: 75,
+              tabs: [
+                LayoutHelper.getPanel({
+                  id: PANELS.SCRATCH, title: gettext('Scratch Pad'),
+                  closable: true,
+                  content: <textarea style={{
+                    border: 0,
+                    height: '100%',
+                    width: '100%',
+                    resize: 'none'
+                  }}/>
+                }),
+              ]
+            }
+          ]
+        },
+        {
+          mode: 'horizontal',
+          children: [
+            {
+              tabs: [
+                LayoutHelper.getPanel({
+                  id: PANELS.DATA_OUTPUT, title: 'Data output', content: <ResultSet />,
+                }),
+                LayoutHelper.getPanel({
+                  id: PANELS.MESSAGES, title:'Messages', content: <Messages />,
+                }),
+                LayoutHelper.getPanel({
+                  id: PANELS.NOTIFICATIONS, title:'Notifications', content: <Notifications />,
+                }),
+              ],
+            }
+          ]
+        },
+      ]
+    },
+  };
+
+  const reflectPreferences = useCallback(()=>{
+    setQtState({preferences: {
+      browser: pgWindow.pgAdmin.Browser.get_preferences_for_module('browser'),
+      sqleditor: pgWindow.pgAdmin.Browser.get_preferences_for_module('sqleditor'),
+    }});
+  }, []);
+
+  const getSQLScript = ()=>{
+    // Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT)
+    // Call AJAX only if script type url is present
+    if(qtState.params.is_query_tool && qtState.params.query_url) {
+      api.get(qtState.params.query_url)
+        .then((res)=>{
+          eventBus.current.fireEvent(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, res.data);
+        })
+        .catch((err)=>{
+          eventBus.current.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+        });
+    } else if(qtState.params.sql_id) {
+      let sqlValue = localStorage.getItem(qtState.params.sql_id);
+      localStorage.removeItem(qtState.params.sql_id);
+      if(sqlValue) {
+        eventBus.current.fireEvent(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, sqlValue);
+      }
+    }
+  };
+
+  const initializeQueryTool = ()=>{
+    let selectedConn = _.find(qtState.connection_list, (c)=>c.is_selected);
+    let baseUrl = '';
+    if(qtState.params.is_query_tool) {
+      let endpoint = 'sqleditor.initialize_sqleditor';
+
+      if(qtState.params.did) {
+        endpoint = 'sqleditor.initialize_sqleditor_with_did';
+      }
+      baseUrl = url_for(endpoint, {
+        ...selectedConn,
+        trans_id: qtState.params.trans_id,
+      });
+    } else {
+      baseUrl = url_for('sqleditor.initialize_viewdata', {
+        ...qtState.params,
+      });
+    }
+    api.post(baseUrl, qtState.params.is_query_tool ? null : JSON.stringify(qtState.params.sql_filter))
+      .then(()=>{
+        setQtState({
+          connected: true,
+          obtaining_conn: false,
+        });
+
+        if(!qtState.params.is_query_tool) {
+          eventBus.current.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+        }
+      }).catch((err)=>{
+        if(err.response?.request?.responseText?.search('Ticket expired') !== -1) {
+          Kerberos.fetch_ticket()
+            .then(()=>{
+              initializeQueryTool();
+            })
+            .catch((kberr)=>{
+              setQtState({
+                connected: false,
+                obtaining_conn: false,
+              });
+              eventBus.current.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, kberr);
+            });
+        }
+        setQtState({
+          connected: false,
+          obtaining_conn: false,
+        });
+        eventBus.current.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+      });
+  };
+
+  useEffect(()=>{
+    getSQLScript();
+    initializeQueryTool();
+
+    eventBus.current.registerListener(QUERY_TOOL_EVENTS.FOCUS_PANEL, (panelId)=>{
+      LayoutHelper.focus(docker.current, panelId);
+    });
+
+    eventBus.current.registerListener(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, (status)=>{
+      setQtState({connection_status: status});
+    });
+
+    eventBus.current.registerListener(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL, ()=>{
+      panel.off(window.wcDocker.EVENT.CLOSING);
+      panel.close();
+    });
+
+    reflectPreferences();
+    pgWindow.pgAdmin.Browser.onPreferencesChange('sqleditor', function() {
+      reflectPreferences();
+    });
+
+    /* WC docker events */
+    panel?.on(window.wcDocker.EVENT.CLOSING, function() {
+      if(!forceClose.current) {
+        eventBus.current.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE);
+      } else {
+        panel.close();
+      }
+    });
+
+    pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:select_file', (fileName)=>{
+      eventBus.current.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE, fileName);
+    }, pgAdmin);
+
+    pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', (fileName)=>{
+      eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE, fileName);
+    }, pgAdmin);
+  }, []);
+
+  useEffect(()=>{
+    const pushHistory = (h)=>{
+      api.post(
+        url_for('sqleditor.add_query_history', {
+          'trans_id': qtState.params.trans_id,
+        }),
+        JSON.stringify(h),
+      ).catch((error)=>{console.error(error);});
+    };
+    eventBus.current.registerListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
+    return ()=>{eventBus.current.deregisterListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);};
+  }, [qtState.params.trans_id]);
+
+
+  const handleApiError = (error, handleParams)=>{
+    if(error.response && pgAdmin.Browser?.UserManagement?.isPgaLoginRequired(error.response)) {
+      return pgAdmin.Browser.UserManagement.pgaLogin();
+    }
+
+    if(error.response?.status == 503 && error.response.data?.info == 'CONNECTION_LOST') {
+      // We will display re-connect dialog, no need to display error message again
+      modal.confirm(
+        gettext('Connection Warning'),
+        <p>
+          <span>{gettext('The application has lost the database connection:')}</span>
+          <br/><span>{gettext('⁃ If the connection was idle it may have been forcibly disconnected.')}</span>
+          <br/><span>{gettext('⁃ The application server or database server may have been restarted.')}</span>
+          <br/><span>{gettext('⁃ The user session may have timed out.')}</span>
+          <br />
+          <span>{gettext('Do you want to continue and establish a new session')}</span>
+        </p>,
+        function() {
+          handleParams?.connectionLostCallback?.();
+        }, null,
+        gettext('Continue'),
+        gettext('Cancel')
+      );
+    } else if(handleParams?.checkTransaction && error.response?.data.info == 'DATAGRID_TRANSACTION_REQUIRED') {
+      let selectedConn = _.find(qtState.connection_list, (c)=>c.is_selected);
+      initConnection(api, {
+        'gid': selectedConn.sgid,
+        'sid': selectedConn.sid,
+        'did': selectedConn.did,
+        'role': selectedConn.role,
+      }).then(()=>{
+        initializeQueryTool();
+      }).catch((err)=>{
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+      });
+    } else {
+      let msg = parseApiError(error);
+      eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, msg, true);
+      eventBus.current.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
+      Notifier.error(msg);
+    }
+  };
+
+  useEffect(()=>{
+    const fileDone = (fileName, success=true)=>{
+      if(success) {
+        setQtState({
+          current_file: fileName,
+        });
+        setPanelTitle(panel, fileName, {...qtState, current_file: fileName});
+      }
+    };
+    const events = [
+      [QUERY_TOOL_EVENTS.TRIGGER_LOAD_FILE, ()=>{
+        let fileParams = {
+          'supported_types': ['*', 'sql'], // file types allowed
+          'dialog_type': 'select_file', // open select file dialog
+        };
+        pgAdmin.FileManager.init();
+        pgAdmin.FileManager.show_dialog(fileParams);
+      }],
+      [QUERY_TOOL_EVENTS.TRIGGER_SAVE_FILE, (isSaveAs=false)=>{
+        if(!isSaveAs && qtState.current_file) {
+          eventBus.current.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE, qtState.current_file);
+        } else {
+          let fileParams = {
+            'supported_types': ['*', 'sql'],
+            'dialog_type': 'create_file',
+            'dialog_title': 'Save File',
+            'btn_primary': 'Save',
+          };
+          pgAdmin.FileManager.init();
+          pgAdmin.FileManager.show_dialog(fileParams);
+        }
+      }],
+      [QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileDone],
+      [QUERY_TOOL_EVENTS.SAVE_FILE_DONE, fileDone],
+      [QUERY_TOOL_EVENTS.QUERY_CHANGED, (isDirty)=>{
+        if(qtState.params.is_query_tool) {
+          setPanelTitle(panel, null, qtState, isDirty);
+        }
+      }],
+      [QUERY_TOOL_EVENTS.HANDLE_API_ERROR, handleApiError],
+    ];
+
+    events.forEach((e)=>{
+      eventBus.current.registerListener(e[0], e[1]);
+    });
+
+    return ()=>{
+      events.forEach((e)=>{
+        eventBus.current.deregisterListener(e[0], e[1]);
+      });
+    };
+  }, [qtState]);
+
+  useEffect(()=>{
+    /* Fire query change so that title changes to latest */
+    eventBus.current.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_QUERY_CHANGE);
+  }, [qtState.params.title]);
+
+  const updateQueryToolConnection = useCallback((connectionData, isNew=false)=>{
+    setQtState((prev)=>{
+      let newConnList = [...prev.connection_list];
+      if(isNew) {
+        newConnList.push(connectionData);
+      }
+      for (const connItem of newConnList) {
+        if(connectionData.sid == connItem.sid
+          && connectionData.did == connItem.did
+          && connectionData.user == connItem.user
+          && connectionData.role == connItem.role) {
+          connItem.is_selected = true;
+        } else {
+          connItem.is_selected = false;
+        }
+      }
+      return {
+        connection_list: newConnList,
+      };
+    });
+    setQtState((prev)=>{
+      return {
+        params: {
+          ...prev.params,
+          sid: connectionData.sid,
+          did: connectionData.did,
+          title: connectionData.title,
+        },
+        obtaining_conn: true,
+        connected: false,
+      };
+    });
+    return api.post(url_for('sqleditor.update_sqleditor_connection', {
+      trans_id: qtState.params.trans_id,
+      sgid: connectionData.sgid,
+      sid: connectionData.sid,
+      did: connectionData.did
+    }), connectionData)
+      .then(({data: respData})=>{
+        setQtState((prev)=>{
+          return {
+            params: {
+              ...prev.params,
+              trans_id: respData.data.trans_id,
+            },
+            connected: respData.data.trans_id ? true : false,
+            obtaining_conn: false,
+          };
+        });
+        let msg = `${connectionData['server_name']}/${connectionData['database_name']} - Database connected`;
+        Notifier.success(msg);
+      });
+  }, [qtState.params.trans_id]);
+
+  const onNewConnClick = useCallback(()=>{
+    const onClose = ()=>LayoutHelper.close(docker.current, 'new-conn');
+    LayoutHelper.openDialog(docker.current, {
+      id: 'new-conn',
+      title: gettext('Add new connection'),
+      content: <NewConnectionDialog onSave={(_isNew, data)=>{
+        let connectionData = {
+          sgid: 0,
+          sid: data.sid,
+          did: data.did,
+          user: data.user,
+          role: data.role && null,
+          title: getTitle(pgAdmin, qtState.preferences.browser, null, false, data.server_name, data.database_name, data.user, true),
+          conn_title: getTitle(pgAdmin, null, null, true, data.server_name, data.database_name, data.user, true),
+          server_name: data.server_name,
+          database_name: data.database_name,
+          is_selected: true,
+        };
+        updateQueryToolConnection(connectionData, true);
+        onClose();
+        return Promise.resolve();
+      }}
+      onClose={onClose}/>
+    });
+  }, [qtState.preferences.browser]);
+
+
+  const onNewQueryToolClick = ()=>{
+    const transId = commonUtils.getRandomInt(1, 9999999);
+    let selectedConn = _.find(qtState.connection_list, (c)=>c.is_selected);
+    let parentData = {
+      server_group: {
+        _id: selectedConn.sgid || 0,
+      },
+      server: {
+        _id: selectedConn.sid,
+        server_type: qtState.params.server_type,
+      },
+      database: {
+        _id: selectedConn.did,
+        label: selectedConn.database_name,
+      },
+    };
+
+    const gridUrl = showQueryTool.generateUrl(transId, parentData, null);
+    const title = getTitle(pgAdmin, qtState.preferences.browser, null, false, selectedConn.server_name, selectedConn.database_name, selectedConn.user);
+    showQueryTool.launchQueryTool(pgWindow.pgAdmin.Tools.SQLEditor, transId, gridUrl, title, '');
+  };
+
+  const onManageMacros = useCallback(()=>{
+    const onClose = ()=>LayoutHelper.close(docker.current, 'manage-macros');
+    LayoutHelper.openDialog(docker.current, {
+      id: 'manage-macros',
+      title: gettext('Manage Macros'),
+      content: <MacrosDialog onSave={(newMacros)=>{
+        setQtState((prev)=>{
+          return {
+            params: {
+              ...prev.params,
+              macros: newMacros,
+            },
+          };
+        });
+      }}
+      onClose={onClose}/>
+    }, 850, 500);
+  }, [qtState.preferences.browser]);
+
+  const onFilterClick = useCallback(()=>{
+    const onClose = ()=>LayoutHelper.close(docker.current, 'filter-dialog');
+    LayoutHelper.openDialog(docker.current, {
+      id: 'filter-dialog',
+      title: gettext('Sort/Filter options'),
+      content: <FilterDialog onSave={()=>{
+        onClose();
+        eventBus.current.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+      }}
+      onClose={onClose}/>
+    }, 700, 400);
+  }, [qtState.preferences.browser]);
+
+  const onResetLayout = useCallback(()=>{
+    docker.current?.resetLayout();
+    eventBus.current.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.QUERY);
+  }, []);
+
+  const queryToolContextValue = React.useMemo(()=>({
+    docker: docker.current,
+    api: api,
+    modal: modal,
+    params: qtState.params,
+    preferences: qtState.preferences,
+  }), [qtState.params, qtState.preferences]);
+
+  const queryToolConnContextValue = React.useMemo(()=>({
+    connected: qtState.connected,
+    obtainingConn: qtState.obtaining_conn,
+    connectionStatus: qtState.connection_status,
+  }), [qtState]);
+
+  /* Push only those things in context which do not change frequently */
+  return (
+    <QueryToolContext.Provider value={queryToolContextValue}>
+      <QueryToolConnectionContext.Provider value={queryToolConnContextValue}>
+        <QueryToolEventsContext.Provider value={eventBus.current}>
+          <Theme>
+            <Box width="100%" height="100%" display="flex" flexDirection="column" flexGrow="1" tabIndex="0" ref={containerRef}>
+              <ConnectionBar
+                connected={qtState.connected}
+                connecting={qtState.obtaining_conn}
+                connectionStatus={qtState.connection_status}
+                connectionStatusMsg={qtState.connection_status_msg}
+                connectionList={qtState.connection_list}
+                onConnectionChange={(connectionData)=>updateQueryToolConnection(connectionData)}
+                onNewConnClick={onNewConnClick}
+                onNewQueryToolClick={onNewQueryToolClick}
+                onResetLayout={onResetLayout}
+                docker={docker.current}
+              />
+              <MainToolBar
+                containerRef={containerRef}
+                onManageMacros={onManageMacros}
+                onFilterClick={onFilterClick}
+              />
+              <Layout
+                getLayoutInstance={(obj)=>docker.current=obj}
+                defaultLayout={defaultLayout}
+                layoutId="SQLEditor/Layout"
+                savedLayout={params.layout}
+              />
+              <StatusBar />
+            </Box>
+          </Theme>
+        </QueryToolEventsContext.Provider>
+      </QueryToolConnectionContext.Provider>
+    </QueryToolContext.Provider>
+  );
+}
+
+QueryToolComponent.propTypes = {
+  params:PropTypes.shape({
+    trans_id: PropTypes.number.isRequired,
+    sgid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+    sid: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+    did: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+    server_type: PropTypes.string,
+    title: PropTypes.string.isRequired,
+    bgcolor: PropTypes.string,
+    fgcolor: PropTypes.string,
+    is_query_tool: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]).isRequired,
+    username: PropTypes.string,
+    server_name: PropTypes.string,
+    database_name: PropTypes.string,
+    layout: PropTypes.string,
+  }),
+  pgWindow: PropTypes.object.isRequired,
+  pgAdmin: PropTypes.object.isRequired,
+  selectedNodeInfo: PropTypes.object,
+  panel: PropTypes.object,
+  eventBusObj: PropTypes.objectOf(EventBus),
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js
new file mode 100644
index 000000000..a6635dc2d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolConstants.js
@@ -0,0 +1,97 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import gettext from 'sources/gettext';
+
+export const QUERY_TOOL_EVENTS = {
+  TRIGGER_STOP_EXECUTION: 'TRIGGER_STOP_EXECUTION',
+  TRIGGER_EXECUTION: 'TRIGGER_EXECUTION',
+  TRIGGER_LOAD_FILE: 'TRIGGER_LOAD_FILE',
+  TRIGGER_SAVE_FILE: 'TRIGGER_SAVE_FILE',
+  TRIGGER_SAVE_DATA: 'TRIGGER_SAVE_DATA',
+  TRIGGER_DELETE_ROWS: 'TRIGGER_DELETE_ROWS',
+  TRIGGER_COPY_DATA: 'TRIGGER_COPY_DATA',
+  TRIGGER_ADD_ROWS: 'TRIGGER_ADD_ROWS',
+  TRIGGER_RENDER_GEOMETRIES: 'TRIGGER_RENDER_GEOMETRIES',
+  TRIGGER_SAVE_RESULTS: 'TRIGGER_SAVE_RESULTS',
+  TRIGGER_SAVE_RESULTS_END: 'TRIGGER_SAVE_RESULTS_END',
+  TRIGGER_PASTE_ROWS: 'TRIGGER_PASTE_ROWS',
+  TRIGGER_QUERY_CHANGE: 'TRIGGER_QUERY_CHANGE',
+  TRIGGER_INCLUDE_EXCLUDE_FILTER: 'TRIGGER_INCLUDE_EXCLUDE_FILTER',
+  TRIGGER_REMOVE_FILTER: 'TRIGGER_REMOVE_FILTER',
+  TRIGGER_SET_LIMIT: 'TRIGGER_SET_LIMIT',
+  TRIGGER_FORMAT_SQL: 'TRIGGER_FORMAT_SQL',
+
+  COPY_DATA: 'COPY_DATA',
+  SET_LIMIT_VALUE: 'SET_LIMIT_VALUE',
+  SET_CONNECTION_STATUS: 'SET_CONNECTION_STATUS',
+  EXECUTION_START: 'EXECUTION_START',
+  EXECUTION_END: 'EXECUTION_END',
+  STOP_QUERY: 'STOP_QUERY',
+  CURSOR_ACTIVITY: 'CURSOR_ACTIVITY',
+  SET_MESSAGE: 'SET_MESSAGE',
+  ROWS_FETCHED: 'ROWS_FETCHED',
+  SELECTED_ROWS_COLS_CHANGED: 'SELECTED_ROWS_COLS_CHANGED',
+  DATAGRID_CHANGED: 'DATAGRID_CHANGED',
+  HIGHLIGHT_ERROR: 'HIGHLIGHT_ERROR',
+  FOCUS_PANEL: 'FOCUS_PANEL',
+  LOAD_FILE: 'LOAD_FILE',
+  LOAD_FILE_DONE: 'LOAD_FILE_DONE',
+  SAVE_FILE: 'SAVE_FILE',
+  SAVE_FILE_DONE: 'SAVE_FILE_DONE',
+  QUERY_CHANGED: 'QUERY_CHANGED',
+  API_ERROR: 'API_ERROR',
+  SAVE_DATA_DONE: 'SAVE_DATA_DONE',
+  TASK_START: 'TASK_START',
+  TASK_END: 'TASK_END',
+  RENDER_GEOMETRIES: 'RENDER_GEOMETRIES',
+  PUSH_NOTICE: 'PUSH_NOTICE',
+  PUSH_HISTORY: 'PUSH_HISTORY',
+  HANDLE_API_ERROR: 'HANDLE_API_ERROR',
+  SET_FILTER_INFO: 'SET_FILTER_INFO',
+  FETCH_MORE_ROWS: 'FETCH_MORE_ROWS',
+
+  EDITOR_FIND_REPLACE: 'EDITOR_FIND_REPLACE',
+  EDITOR_EXEC_CMD: 'EDITOR_EXEC_CMD',
+  EDITOR_SET_SQL: 'EDITOR_SET_SQL',
+  COPY_TO_EDITOR: 'COPY_TO_EDITOR',
+
+  WARN_SAVE_DATA_CLOSE: 'WARN_SAVE_DATA_CLOSE',
+  WARN_SAVE_TEXT_CLOSE: 'WARN_SAVE_TEXT_CLOSE',
+  WARN_TXN_CLOSE: 'WARN_TXN_CLOSE',
+
+  RESET_LAYOUT: 'RESET_LAYOUT',
+  FORCE_CLOSE_PANEL: 'FORCE_CLOSE_PANEL',
+};
+
+export const CONNECTION_STATUS = {
+  TRANSACTION_STATUS_IDLE: 0,
+  TRANSACTION_STATUS_ACTIVE: 1,
+  TRANSACTION_STATUS_INTRANS: 2,
+  TRANSACTION_STATUS_INERROR: 3,
+  TRANSACTION_STATUS_UNKNOWN: 4,
+};
+
+export const CONNECTION_STATUS_MESSAGE = {
+  [CONNECTION_STATUS.TRANSACTION_STATUS_IDLE]: gettext('The session is idle and there is no current transaction.'),
+  [CONNECTION_STATUS.TRANSACTION_STATUS_ACTIVE]: gettext('A command is currently in progress.'),
+  [CONNECTION_STATUS.TRANSACTION_STATUS_INTRANS]: gettext('The session is idle in a valid transaction block.'),
+  [CONNECTION_STATUS.TRANSACTION_STATUS_INERROR]: gettext('The session is idle in a failed transaction block.'),
+  [CONNECTION_STATUS.TRANSACTION_STATUS_UNKNOWN]: gettext('The connection with the server is bad.')
+};
+
+export const PANELS = {
+  QUERY: 'id-query',
+  MESSAGES: 'id-messages',
+  SCRATCH: 'id-scratch',
+  DATA_OUTPUT: 'id-dataoutput',
+  EXPLAIN: 'id-explain',
+  GEOMETRY: 'id-geometry',
+  NOTIFICATIONS: 'id-notifications',
+  HISTORY: 'id-history',
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/CopyData.js b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/CopyData.js
new file mode 100644
index 000000000..b434c81e2
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/CopyData.js
@@ -0,0 +1,99 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import JSONBigNumber from 'json-bignumber';
+import _ from 'lodash';
+import * as clipboard from '../../../../../../static/js/clipboard';
+import { CSVToArray } from '../../../../../../static/js/utils';
+
+export default class CopyData {
+  constructor(options) {
+    this.CSVOptions = {
+      field_separator: '\t',
+      quote_char: '"',
+      quoting: 'strings',
+      ...options,
+    };
+  }
+
+  setCSVOptions(options) {
+    this.CSVOptions = {
+      ...this.CSVOptions,
+      ...options,
+    };
+  }
+
+  copyRowsToCsv(rows=[], columns=[], withHeaders=false) {
+    let csvRows = rows.reduce((prevCsvRows, currRow)=>{
+      let csvRow = columns.reduce((prevCsvCols, column)=>{
+        prevCsvCols.push(this.csvCell(currRow[column.key], column));
+        return prevCsvCols;
+      }, []).join(this.CSVOptions.field_separator);
+      prevCsvRows.push(csvRow);
+      return prevCsvRows;
+    }, []);
+
+    if(withHeaders) {
+      let csvRow = columns.reduce((prevCsvCols, column)=>{
+        prevCsvCols.push(this.csvCell(column.name, column, true));
+        return prevCsvCols;
+      }, []).join(this.CSVOptions.field_separator);
+      csvRows.unshift(csvRow);
+    }
+    clipboard.copyToClipboard(csvRows.join('\n'));
+    localStorage.setItem('copied-with-headers', withHeaders);
+  }
+
+  escape(iStr) {
+    return (this.CSVOptions.quote_char == '"') ?
+      iStr.replace(/\"/g, '""') : iStr.replace(/\'/g, '\'\'');
+  }
+
+  allQuoteCell(value) {
+    if (value && _.isObject(value)) {
+      value = this.CSVOptions.quote_char + JSONBigNumber.stringify(value) + this.CSVOptions.quote_char;
+    } else if (value) {
+      value = this.CSVOptions.quote_char + this.escape(value.toString()) + this.CSVOptions.quote_char;
+    } else if (_.isNull(value) || _.isUndefined(value)) {
+      value = '';
+    }
+    return value;
+  }
+
+  stringQuoteCell(value, column) {
+    if (value && _.isObject(value)) {
+      value = this.CSVOptions.quote_char + JSONBigNumber.stringify(value) + this.CSVOptions.quote_char;
+    } else if (value && column.cell != 'number' && column.cell != 'boolean') {
+      value = this.CSVOptions.quote_char + this.escape(value.toString()) + this.CSVOptions.quote_char;
+    } else if (column.cell == 'string' && _.isNull(value)){
+      value = null;
+    } else if (_.isNull(value) || _.isUndefined(value)) {
+      value = '';
+    }
+    return value;
+  }
+
+  csvCell(value, column, header=false) {
+    if (this.CSVOptions.quoting == 'all' || header) {
+      value = this.allQuoteCell(value);
+    } else if(this.CSVOptions.quoting == 'strings') {
+      value = this.stringQuoteCell(value, column);
+    }
+    return value;
+  }
+
+  getCopiedRows() {
+    let copiedText = clipboard.getFromClipboard();
+    let copiedRows = CSVToArray(copiedText, this.CSVOptions.field_separator, this.CSVOptions.quote_char);
+
+    if(localStorage.getItem('copied-with-headers') == 'true') {
+      copiedRows = copiedRows.slice(1);
+    }
+    return copiedRows;
+  }
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
new file mode 100644
index 000000000..b48152b11
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Editors.jsx
@@ -0,0 +1,345 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles, Box, Portal } from '@material-ui/core';
+import React, {useContext} from 'react';
+import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
+import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
+import CloseIcon from '@material-ui/icons/Close';
+import gettext from 'sources/gettext';
+import clsx from 'clsx';
+import JSONBigNumber from 'json-bignumber';
+import JsonEditor from '../../../../../../static/js/components/JsonEditor';
+import PropTypes from 'prop-types';
+import { RowInfoContext } from '.';
+import Notifier from '../../../../../../static/js/helpers/Notifier';
+
+const useStyles = makeStyles((theme)=>({
+  textEditor: {
+    position: 'absolute',
+
+    zIndex: 1050,
+    backgroundColor: theme.palette.background.default,
+    padding: '0.25rem',
+    fontSize: '12px',
+    ...theme.mixins.panelBorder.all,
+    left: 0,
+    // bottom: 0,
+    top: 0,
+    '& textarea': {
+      width: '250px',
+      height: '80px',
+      border: 0,
+      outline: 0,
+      resize: 'both',
+    }
+  },
+  jsonEditor: {
+    position: 'absolute',
+    zIndex: 1050,
+    backgroundColor: theme.palette.background.default,
+    ...theme.mixins.panelBorder,
+    padding: '0.25rem',
+    '& .jsoneditor-div': {
+      fontSize: '12px',
+      minWidth: '525px',
+      minHeight: '300px',
+      ...theme.mixins.panelBorder.all,
+      outline: 0,
+      resize: 'both',
+      overflow: 'auto',
+    },
+    '& .jsoneditor': {
+      height: 'abc',
+      border: 'none',
+      '& .ace-jsoneditor .ace_marker-layer .ace_active-line': {
+        background: theme.palette.primary.light
+      }
+    }
+  },
+  buttonMargin: {
+    marginLeft: '0.5rem',
+  },
+  textarea: {
+    resize: 'both'
+  },
+  input: {
+    appearance: 'none',
+    width: '100%',
+    height: '100%',
+    verticalAlign: 'top',
+    outline: 'none',
+    backgroundColor: theme.palette.background.default,
+    color: theme.palette.text.primary,
+    border: 0,
+    boxShadow: 'inset 0 0 0 1.5px '+theme.palette.primary.main,
+    padding: '0 2px',
+    '::selection': {
+      background: theme.palette.primary.light,
+    }
+  },
+  check: {
+    display: 'inline-block',
+    verticalAlign: 'top',
+    width: '16px',
+    height: '16px',
+    border: '1px solid '+theme.palette.grey[800],
+    margin: '3px',
+    textAlign: 'center',
+    lineHeight: '16px',
+
+    '&.checked, &.unchecked': {
+      background: theme.palette.background.default,
+    },
+    '&.checked:after': {
+      content: '\'\\2713\'',
+      fontWeight: 'bold',
+    },
+    '&.intermediate': {
+      background: theme.palette.grey[200],
+      '&:after': {
+        content: '\'\\003F\'',
+        fontWeight: 'bold',
+      },
+    },
+  }
+}));
+
+function autoFocusAndSelect(input) {
+  input?.focus();
+  input?.select();
+}
+
+function isValidArray(val) {
+  val = val?.trim();
+  return !(val != '' && (val.charAt(0) != '{' || val.charAt(val.length - 1) != '}'));
+}
+
+function setEditorPosition(cellEle, editorEle) {
+  if(!editorEle || !cellEle) {
+    return;
+  }
+  let gridEle = cellEle.closest('.rdg');
+  let cellRect = cellEle.getBoundingClientRect();
+  let position = {
+    left: cellRect.left,
+    top: cellRect.top - editorEle.offsetHeight + 12,
+  };
+
+  if ((position.left + editorEle.offsetWidth + 10) > gridEle.offsetWidth) {
+    position.left -=  position.left + editorEle.offsetWidth - gridEle.offsetWidth + 10;
+  }
+  editorEle.style.left = position.left + 'px';
+  editorEle.style.top = position.top + 'px';
+}
+
+const EditorPropTypes = {
+  row: PropTypes.object,
+  column: PropTypes.object,
+  onRowChange: PropTypes.func,
+  onClose: PropTypes.func
+};
+
+function textColumnFinalVal(columnVal, column) {
+  if(columnVal === '') {
+    columnVal = null;
+  } else if (!column.is_array) {
+    if (columnVal === '\'\'' || columnVal === '""') {
+      columnVal = '';
+    } else if (columnVal === '\\\'\\\'') {
+      columnVal = '\'\'';
+    } else if (columnVal === '\\"\\"') {
+      columnVal = '""';
+    }
+  }
+  return columnVal;
+}
+export function TextEditor({row, column, onRowChange, onClose}) {
+  const classes = useStyles();
+  const value = row[column.key] ?? '';
+  const [localVal, setLocalVal] = React.useState(value);
+  const {getCellElement} = useContext(RowInfoContext);
+
+  const onChange = React.useCallback((e)=>{
+    setLocalVal(e.target.value);
+  }, []);
+
+  const onOK = ()=>{
+    if(column.is_array && !isValidArray(value)) {
+      console.error(gettext('Arrays must start with "{" and end with "}"'));
+    } else {
+      let columnVal = textColumnFinalVal(localVal, column);
+      onRowChange({ ...row, [column.key]: columnVal}, true);
+      onClose();
+    }
+  };
+
+  return(
+    <Portal container={document.body}>
+      <Box ref={(ele)=>{
+        setEditorPosition(getCellElement(column.idx), ele);
+      }} className={classes.textEditor}>
+        <textarea ref={autoFocusAndSelect} className={classes.textarea} value={localVal} onChange={onChange} />
+        <Box display="flex" justifyContent="flex-end">
+          <DefaultButton startIcon={<CloseIcon />} onClick={()=>onClose(false)} size="small">
+            {gettext('Cancel')}
+          </DefaultButton>
+          {column.can_edit &&
+          <>
+            <PrimaryButton startIcon={<CheckRoundedIcon />} onClick={onOK} size="small" className={classes.buttonMargin}>
+              {gettext('OK')}
+            </PrimaryButton>
+          </>}
+        </Box>
+      </Box>
+    </Portal>
+  );
+}
+TextEditor.propTypes = EditorPropTypes;
+
+export function NumberEditor({row, column, onRowChange, onClose}) {
+  const classes = useStyles();
+  const value = row[column.key] ?? '';
+  const onBlur = ()=>{
+    if(!column.is_array && isNaN(value)){
+      Notifier.error(gettext('Please enter a valid number'));
+      return;
+    } else if(column.is_array) {
+      if(!isValidArray(value)) {
+        Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
+        return;
+      }
+      let checkVal = value.trim().slice(1, -1);
+      if(checkVal == '') {
+        checkVal = [];
+      } else {
+        checkVal = checkVal.split(',');
+      }
+      for (const val of checkVal) {
+        if(isNaN(val)) {
+          Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
+          return;
+        }
+      }
+    }
+    onClose(column.can_edit ? true : false);
+  };
+  const onKeyDown = (e)=>{
+    if(e.code == 'Tab') {
+      e.preventDefault();
+      onBlur();
+    }
+  };
+  return (
+    <input
+      className={classes.input}
+      ref={autoFocusAndSelect}
+      value={value}
+      onChange={(e)=>{
+        if(column.can_edit) {
+          onRowChange({ ...row, [column.key]: e.target.value });
+        }
+      }}
+      // onBlur={onBlur}
+      onKeyDown={onKeyDown}
+    />
+  );
+}
+NumberEditor.propTypes = EditorPropTypes;
+
+export function CheckboxEditor({row, column, onRowChange, onClose}) {
+  const classes = useStyles();
+  const value = row[column.key] ?? null;
+  const changeValue = ()=>{
+    if(!column.can_edit) {
+      return;
+    }
+    let newVal = true;
+    if(value) {
+      newVal = false;
+    } else if(value != null && !value) {
+      newVal = null;
+    }
+    onRowChange({ ...row, [column.key]: newVal});
+  };
+  const onBlur = ()=>{onClose(true);};
+  let className = 'checked';
+  if(!value) {
+    className = 'unchecked';
+  } else if(value == null){
+    className = 'intermediate';
+  }
+  return (
+    <div onClick={changeValue} tabIndex="0" onBlur={onBlur}>
+      <span className={clsx(classes.check, className)}></span>
+    </div>
+  );
+}
+CheckboxEditor.propTypes = EditorPropTypes;
+
+export function JsonTextEditor({row, column, onRowChange, onClose}) {
+  const classes = useStyles();
+  const {getCellElement} = useContext(RowInfoContext);
+  const value = React.useMemo(()=>{
+    let newVal = row[column.key] ?? null;
+    /* If jsonb or array */
+    if(column.column_type_internal === 'jsonb' && !Array.isArray(newVal) && newVal != null) {
+      newVal = JSONBigNumber.stringify(JSONBigNumber.parse(newVal), null, 2);
+    } else if (Array.isArray(newVal)) {
+      var temp = newVal.map((ele)=>{
+        if (typeof ele === 'object') {
+          return JSONBigNumber.stringify(ele, null, 2);
+        }
+        return ele;
+      });
+      newVal = '[' + temp.join() + ']';
+    }
+    /* set editor content to empty if value is null*/
+    if (_.isNull(newVal)){
+      newVal = '';
+    }
+    return newVal;
+  });
+  const [localVal, setLocalVal] = React.useState(value);
+
+  const onChange = React.useCallback((newVal)=>{
+    setLocalVal(newVal);
+  }, []);
+  const onOK = ()=>{
+    onRowChange({ ...row, [column.key]: localVal}, true);
+    onClose();
+  };
+  return (
+    <Portal container={document.body}>
+      <Box ref={(ele)=>{
+        setEditorPosition(getCellElement(column.idx), ele);
+      }} className={classes.jsonEditor}>
+        <JsonEditor
+          value={localVal}
+          options={{
+            onChange: onChange,
+            onError: (error)=>console.error('Invalid Json: ' + error.message.split(':')[0]),
+          }}
+          className={'jsoneditor-div'}
+        />
+        <Box display="flex" justifyContent="flex-end" marginTop="0.25rem">
+          <DefaultButton startIcon={<CloseIcon />} onClick={()=>onClose(false)} size="small">
+            {gettext('Cancel')}
+          </DefaultButton>
+          {column.can_edit &&
+          <>
+            <PrimaryButton startIcon={<CheckRoundedIcon />} onClick={onOK} size="small" className={classes.buttonMargin}>
+              {gettext('OK')}
+            </PrimaryButton>
+          </>}
+        </Box>
+      </Box>
+    </Portal>
+  );
+}
+JsonTextEditor.propTypes = EditorPropTypes;
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Formatters.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Formatters.jsx
new file mode 100644
index 000000000..682a2e0f8
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/Formatters.jsx
@@ -0,0 +1,73 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React from 'react';
+import _ from 'lodash';
+import { makeStyles } from '@material-ui/core';
+import PropTypes from 'prop-types';
+import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
+
+const useStyles = makeStyles((theme)=>({
+  disabledCell: {
+    opacity: theme.palette.action.disabledOpacity,
+  }
+}));
+
+function NullAndDefaultFormatter({value, column, children}) {
+  const classes = useStyles();
+  if (_.isUndefined(value) && column.has_default_val) {
+    return <span className={classes.disabledCell}>[default]</span>;
+  } else if ((_.isUndefined(value) && column.not_null) ||
+      (_.isUndefined(value) || _.isNull(value))) {
+    return <span className={classes.disabledCell}>[null]</span>;
+  }
+  return children;
+}
+NullAndDefaultFormatter.propTypes = {
+  value: PropTypes.any,
+  column: PropTypes.object,
+  children: CustomPropTypes.children,
+};
+
+const FormatterPropTypes = {
+  row: PropTypes.object,
+  column: PropTypes.object,
+};
+export function TextFormatter({row, column}) {
+  let value = row[column.key];
+  if(!_.isNull(value) && !_.isUndefined(value)) {
+    value = value.toString();
+  }
+  return (
+    <NullAndDefaultFormatter value={value} column={column}>
+      <>{value}</>
+    </NullAndDefaultFormatter>
+  );
+}
+TextFormatter.propTypes = FormatterPropTypes;
+
+export function NumberFormatter({row, column}) {
+  let value = row[column.key];
+  return (
+    <NullAndDefaultFormatter value={value} column={column}>
+      <div style={{textAlign: 'right'}}>{value}</div>
+    </NullAndDefaultFormatter>
+  );
+}
+NumberFormatter.propTypes = FormatterPropTypes;
+
+export function BinaryFormatter({row, column}) {
+  let value = row[column.key];
+  const classes = useStyles();
+  return (
+    <NullAndDefaultFormatter value={value} column={column}>
+      <span className={classes.disabledCell}>[{value}]</span>
+    </NullAndDefaultFormatter>
+  );
+}
+BinaryFormatter.propTypes = FormatterPropTypes;
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/index.jsx b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/index.jsx
new file mode 100644
index 000000000..17b4b7bf9
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/QueryToolDataGrid/index.jsx
@@ -0,0 +1,398 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { Box, makeStyles } from '@material-ui/core';
+import _ from 'lodash';
+import React, {useState, useEffect, useCallback, useContext, useRef} from 'react';
+import ReactDataGrid, {Row, useRowSelection} from 'react-data-grid';
+import LockIcon from '@material-ui/icons/Lock';
+import EditIcon from '@material-ui/icons/Edit';
+import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
+import * as Editors from './Editors';
+import * as Formatters from './Formatters';
+import clsx from 'clsx';
+import { PgIconButton } from '../../../../../../static/js/components/Buttons';
+import MapIcon from '@material-ui/icons/Map';
+import { QueryToolEventsContext } from '../QueryToolComponent';
+import PropTypes, { number } from 'prop-types';
+import gettext from 'sources/gettext';
+
+export const ROWNUM_KEY = '$_pgadmin_rownum_key_$';
+export const GRID_ROW_SELECT_KEY = '$_pgadmin_gridrowselect_key_$';
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    height: '100%',
+    color: theme.palette.text.primary,
+    backgroundColor: theme.otherVars.qtDatagridBg,
+    fontSize: '12px',
+    border: 'none',
+    '--rdg-selection-color': theme.palette.primary.main,
+    '& .rdg-cell': {
+      ...theme.mixins.panelBorder.right,
+      ...theme.mixins.panelBorder.bottom,
+      fontWeight: 'abc',
+      '&[aria-colindex="1"]': {
+        padding: 0,
+      }
+    },
+    '& .rdg-header-row .rdg-cell': {
+      padding: 0,
+    },
+    '& .rdg-header-row': {
+      backgroundColor: theme.palette.background.default,
+      fontWeight: 'normal',
+    },
+    '& .rdg-row': {
+      backgroundColor: theme.palette.background.default,
+      '&[aria-selected=true]': {
+        backgroundColor: theme.palette.primary.light,
+        color: theme.otherVars.qtDatagridSelectFg,
+        '& .rdg-cell:nth-child(1)': {
+          backgroundColor: theme.palette.primary.main,
+          color: theme.palette.primary.contrastText,
+        }
+      },
+    }
+  },
+  columnHeader: {
+    padding: '3px 6px',
+    height: '100%',
+    display: 'flex',
+    lineHeight: '16px',
+    alignItems: 'center',
+  },
+  columnName: {
+    fontWeight: 'bold',
+  },
+  editedCell: {
+    fontWeight: 'bold',
+  },
+  deletedRow: {
+    '&:before': {
+      content: '" "',
+      position: 'absolute',
+      top: '50%',
+      left: 0,
+      borderTop: '1px solid ' + theme.palette.error.main,
+      width: '100%',
+    }
+  },
+  rowNumCell: {
+    padding: '0px 8px',
+  },
+  colHeaderSelected: {
+    outlineColor: theme.palette.primary.main,
+    backgroundColor: theme.palette.primary.main,
+    color: theme.palette.primary.contrastText,
+  },
+  colSelected: {
+    outlineColor: theme.palette.primary.main,
+    backgroundColor: theme.palette.primary.light,
+    color: theme.otherVars.qtDatagridSelectFg,
+  }
+}));
+
+export const RowInfoContext = React.createContext();
+
+function CustomRow(props) {
+  const rowRef = useRef();
+  const rowInfoValue = {
+    rowIdx: props.rowIdx,
+    getCellElement: (colIdx)=>{
+      return rowRef.current.querySelector(`.rdg-cell[aria-colindex="${colIdx+1}"]`);
+    }
+  };
+  return (
+    <RowInfoContext.Provider value={rowInfoValue}>
+      <Row ref={rowRef} {...props} />
+    </RowInfoContext.Provider>
+  );
+}
+
+CustomRow.propTypes = {
+  rowIdx: number,
+};
+
+function SelectAllHeaderRenderer(props) {
+  const [checked, setChecked] = useState(false);
+  const eventBus = useContext(QueryToolEventsContext);
+  const onClick = ()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, true, ()=>{
+      setChecked(!checked);
+      props.onAllRowsSelectionChange(!checked);
+    });
+  };
+  return <div style={{widht: '100%', height: '100%'}} onClick={onClick}></div>;
+}
+SelectAllHeaderRenderer.propTypes = {
+  onAllRowsSelectionChange: PropTypes.func,
+};
+
+function SelectableHeaderRenderer({column, selectedColumns, onSelectedColumnsChange}) {
+  const classes = useStyles();
+  const eventBus = useContext(QueryToolEventsContext);
+
+  const onClick = ()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, true, ()=>{
+      const newSelectedCols = new Set(selectedColumns);
+      if (newSelectedCols.has(column.idx)) {
+        newSelectedCols.delete(column.idx);
+      } else {
+        newSelectedCols.add(column.idx);
+      }
+      onSelectedColumnsChange(newSelectedCols);
+    });
+  };
+
+  const isSelected = selectedColumns.has(column.idx);
+
+  return (
+    <Box className={clsx(classes.columnHeader, isSelected ? classes.colHeaderSelected : null)} onClick={onClick}>
+      {(column.column_type_internal == 'geometry' || column.column_type_internal == 'geography') &&
+      <Box>
+        <PgIconButton title={gettext('View all geometries in this column')} icon={<MapIcon />} size="small" style={{marginRight: '0.25rem'}} onClick={(e)=>{
+          e.stopPropagation();
+          eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_RENDER_GEOMETRIES, column);
+        }}/>
+      </Box>}
+      <Box marginRight="auto">
+        <span className={classes.columnName}>{column.display_name}</span><br/>
+        <span>{column.display_type}</span>
+      </Box>
+      <Box marginLeft="4px">{column.can_edit ?
+        <EditIcon fontSize="small" style={{fontSize: '0.875rem'}} />:
+        <LockIcon fontSize="small" style={{fontSize: '0.875rem'}} />
+      }</Box>
+    </Box>
+  );
+}
+SelectableHeaderRenderer.propTypes = {
+  column: PropTypes.object,
+  selectedColumns: PropTypes.objectOf(Set),
+  onSelectedColumnsChange: PropTypes.func,
+};
+
+function setEditorFormatter(col) {
+  // If grid is editable then add editor else make it readonly
+  if (col.cell == 'oid' && col.name == 'oid') {
+    col.editor = null;
+    col.formatter = Formatters.TextFormatter;
+  } else if (col.cell == 'Json') {
+    col.editor = Editors.JsonTextEditor;
+    col.formatter = Formatters.TextFormatter;
+  } else if (['number', 'oid'].indexOf(col.cell) != -1 || ['xid', 'real'].indexOf(col.type) != -1) {
+    col.formatter = Formatters.NumberFormatter;
+    col.editor = Editors.NumberEditor;
+  } else if (col.cell == 'boolean') {
+    col.editor = Editors.CheckboxEditor;
+    col.formatter = Formatters.TextFormatter;
+  } else if (col.cell == 'binary') {
+    // We do not support editing binary data in SQL editor and data grid.
+    col.editor = null;
+    col.formatter = Formatters.BinaryFormatter;
+  } else {
+    col.editor = Editors.TextEditor;
+    col.formatter = Formatters.TextFormatter;
+  }
+}
+
+function cellClassGetter(col, classes, isSelected, dataChangeStore, rowKeyGetter){
+  return (row)=>{
+    let cellClasses = [];
+    if(dataChangeStore && rowKeyGetter) {
+      if(rowKeyGetter(row) in (dataChangeStore?.updated || {})
+        && !_.isUndefined(dataChangeStore?.updated[rowKeyGetter(row)]?.data[col.key])
+        || rowKeyGetter(row) in (dataChangeStore?.added || {})
+      ) {
+        cellClasses.push(classes.editedCell);
+      }
+      if(rowKeyGetter(row) in (dataChangeStore?.deleted || {})) {
+        cellClasses.push(classes.deletedRow);
+      }
+    }
+    if(isSelected) {
+      cellClasses.push(classes.colSelected);
+    }
+    return clsx(cellClasses);
+  };
+}
+
+function initialiseColumns(columns, rows, totalRowCount, columnWidthBy) {
+  let retColumns = [
+    ...columns,
+  ];
+  const canvas = document.createElement('canvas');
+  const canvasContext = canvas.getContext('2d');
+  canvasContext.font = '12px Roboto';
+
+  for(const col of retColumns) {
+    col.width = getTextWidth(col, rows, canvasContext, columnWidthBy);
+    col.resizable = true;
+    col.editorOptions = {
+      commitOnOutsideClick: false,
+      onCellKeyDown: (e)=>{
+        /* Do not open the editor */
+        e.preventDefault();
+      }
+    };
+    setEditorFormatter(col);
+  }
+
+  let rowNumCol = {
+    key: ROWNUM_KEY, name: '', frozen: true, resizable: false,
+    minWidth: 45, width: canvasContext.measureText((totalRowCount||'').toString()).width,
+  };
+  rowNumCol.cellClass = cellClassGetter(rowNumCol);
+  retColumns.unshift(rowNumCol);
+  canvas.remove();
+  return retColumns;
+}
+
+function formatColumns(columns, dataChangeStore, selectedColumns, onSelectedColumnsChange, rowKeyGetter, classes) {
+  let retColumns = [
+    ...columns,
+  ];
+
+  const HeaderRenderer = (props)=>{
+    return <SelectableHeaderRenderer {...props} selectedColumns={selectedColumns} onSelectedColumnsChange={onSelectedColumnsChange}/>;
+  };
+
+  for(const [idx, col] of retColumns.entries()) {
+    col.headerRenderer = HeaderRenderer;
+    col.cellClass = cellClassGetter(col, classes, selectedColumns.has(idx), dataChangeStore, rowKeyGetter);
+  }
+
+  let rowNumCol = retColumns[0];
+  rowNumCol.headerRenderer = SelectAllHeaderRenderer;
+  rowNumCol.formatter = ({row})=>{
+    const {rowIdx} = useContext(RowInfoContext);
+    const [isRowSelected, onRowSelectionChange] = useRowSelection();
+    let rowKey = rowKeyGetter(row);
+    let rownum = rowIdx+1;
+    if(rowKey in (dataChangeStore?.added || {})) {
+      rownum = rownum+'+';
+    } else if(rowKey in (dataChangeStore?.deleted || {})) {
+      rownum = rownum+'-';
+    }
+    return (<div className={classes.rowNumCell} onClick={()=>{
+      onSelectedColumnsChange(new Set());
+      onRowSelectionChange({ row: row, checked: !isRowSelected, isShiftClick: false});
+    }}>
+      {rownum}
+    </div>);
+  };
+
+  return retColumns;
+}
+
+function getTextWidth(column, rows, canvas, columnWidthBy) {
+  const dataWidthReducer = (longest, nextRow) => {
+    let value = nextRow[column.key];
+    if(_.isNull(value) || _.isUndefined(value)) {
+      value = '';
+    }
+    value = value.toString();
+    return longest.length > value.length ? longest : value;
+  };
+
+  let columnHeaderLen = column.display_name.length > column.display_type.length ?
+    canvas.measureText(column.display_name).width : canvas.measureText(column.display_type).width;
+  /* padding 12, icon-width 15 */
+  columnHeaderLen += 15 + 12;
+  if(column.column_type_internal == 'geometry' || column.column_type_internal == 'geography') {
+    columnHeaderLen += 40;
+  }
+  let width = columnHeaderLen;
+  if(typeof(columnWidthBy) == 'number') {
+    /* padding 16 */
+    width = 16 + Math.ceil(canvas.measureText(rows.reduce(dataWidthReducer, '')).width);
+    if(width > columnWidthBy && columnWidthBy > 0) {
+      width = columnWidthBy;
+    }
+    if(width < columnHeaderLen) {
+      width = columnHeaderLen;
+    }
+  }
+  /* Gracefull */
+  width += 2;
+  return width;
+}
+
+export default function QueryToolDataGrid({columns, rows, totalRowCount, dataChangeStore,
+  onSelectedCellChange, rowsResetKey, selectedColumns, onSelectedColumnsChange, columnWidthBy, ...props}) {
+  const classes = useStyles();
+  const [readyColumns, setColumns] = useState([]);
+  const eventBus = useContext(QueryToolEventsContext);
+  const onSelectedColumnsChangeWrapped = (arg)=>{
+    props.onSelectedRowsChange(new Set());
+    onSelectedColumnsChange(arg);
+  };
+
+  useEffect(()=>{
+    if(columns.length > 0 || rows.length > 0) {
+      let initCols = initialiseColumns(columns, rows, totalRowCount, columnWidthBy);
+      setColumns(formatColumns(initCols, dataChangeStore, selectedColumns, onSelectedColumnsChangeWrapped, props.rowKeyGetter, classes));
+    } else {
+      setColumns([], [], 0);
+    }
+  }, [columns, rowsResetKey]);
+
+  useEffect(()=>{
+    setColumns((prevCols)=>{
+      return formatColumns(prevCols, dataChangeStore, selectedColumns, onSelectedColumnsChangeWrapped, props.rowKeyGetter, classes);
+    });
+  }, [dataChangeStore, selectedColumns]);
+
+  const onRowClick = useCallback((row, column)=>{
+    if(column.key === ROWNUM_KEY) {
+      onSelectedCellChange && onSelectedCellChange(null);
+    } else {
+      onSelectedCellChange && onSelectedCellChange([row, column]);
+    }
+  }, []);
+
+  function handleCopy() {
+    if (window.isSecureContext) {
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_COPY_DATA);
+    }
+  }
+
+  return (
+    <ReactDataGrid
+      id="datagrid"
+      columns={readyColumns}
+      rows={rows}
+      className={classes.root}
+      headerRowHeight={40}
+      rowHeight={25}
+      mincolumnWidthBy={50}
+      enableCellSelect={true}
+      onRowClick={onRowClick}
+      onCopy={handleCopy}
+      components={{
+        rowRenderer: CustomRow,
+      }}
+      {...props}
+    />
+  );
+}
+
+QueryToolDataGrid.propTypes = {
+  columns: PropTypes.array,
+  rows: PropTypes.array,
+  totalRowCount: PropTypes.number,
+  dataChangeStore: PropTypes.object,
+  onSelectedCellChange: PropTypes.func,
+  onSelectedRowsChange: PropTypes.func,
+  selectedColumns: PropTypes.objectOf(Set),
+  onSelectedColumnsChange: PropTypes.func,
+  rowKeyGetter: PropTypes.func,
+  rowsResetKey: PropTypes.any,
+  columnWidthBy: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmSaveContent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmSaveContent.jsx
new file mode 100644
index 000000000..ef48b8c30
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmSaveContent.jsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
+import gettext from 'sources/gettext';
+import { Box } from '@material-ui/core';
+import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
+import CloseIcon from '@material-ui/icons/CloseRounded';
+import CheckRoundedIcon from '@material-ui/icons/CheckRounded';
+import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
+import HTMLReactParser from 'html-react-parser';
+import PropTypes from 'prop-types';
+
+export default function ConfirmSaveContent({closeModal, text, onDontSave, onSave}) {
+  const classes = useModalStyles();
+  return (
+    <Box display="flex" flexDirection="column" height="100%">
+      <Box flexGrow="1" p={2}>{typeof(text) == 'string' ? HTMLReactParser(text) : text}</Box>
+      <Box className={classes.footer}>
+        <DefaultButton data-test="close" startIcon={<CloseIcon />} onClick={()=>{
+          closeModal();
+        }} >{gettext('Cancel')}</DefaultButton>
+        <DefaultButton data-test="dont-save" className={classes.margin} startIcon={<DeleteRoundedIcon />} onClick={()=>{
+          onDontSave?.();
+          closeModal();
+        }} >{gettext('Don\'t save')}</DefaultButton>
+        <PrimaryButton data-test="save" className={classes.margin} startIcon={<CheckRoundedIcon />} onClick={()=>{
+          onSave?.();
+          closeModal();
+        }} autoFocus={true} >{gettext('Save')}</PrimaryButton>
+      </Box>
+    </Box>
+  );
+}
+
+ConfirmSaveContent.propTypes = {
+  closeModal: PropTypes.func,
+  text: PropTypes.string,
+  onDontSave: PropTypes.func,
+  onSave: PropTypes.func
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmTransactionContent.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmTransactionContent.jsx
new file mode 100644
index 000000000..bfe88d97f
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/ConfirmTransactionContent.jsx
@@ -0,0 +1,38 @@
+import React from 'react';
+import { useModalStyles } from '../../../../../../static/js/helpers/ModalProvider';
+import gettext from 'sources/gettext';
+import { Box } from '@material-ui/core';
+import { DefaultButton, PrimaryButton } from '../../../../../../static/js/components/Buttons';
+import CloseIcon from '@material-ui/icons/CloseRounded';
+import HTMLReactParser from 'html-react-parser';
+import { CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
+import PropTypes from 'prop-types';
+
+export default function ConfirmTransactionContent({closeModal, text, onRollback, onCommit}) {
+  const classes = useModalStyles();
+  return (
+    <Box display="flex" flexDirection="column" height="100%">
+      <Box flexGrow="1" p={2}>{typeof(text) == 'string' ? HTMLReactParser(text) : text}</Box>
+      <Box className={classes.footer}>
+        <DefaultButton data-test="cancel" startIcon={<CloseIcon />} onClick={()=>{
+          closeModal();
+        }} >{gettext('Cancel')}</DefaultButton>
+        <PrimaryButton data-test="rollback" className={classes.margin} startIcon={<RollbackIcon />} onClick={()=>{
+          onRollback?.();
+          closeModal();
+        }} >{gettext('Rollback')}</PrimaryButton>
+        <PrimaryButton data-test="commit" className={classes.margin} startIcon={<CommitIcon />} onClick={()=>{
+          onCommit?.();
+          closeModal();
+        }} autoFocus={true} >{gettext('Commit')}</PrimaryButton>
+      </Box>
+    </Box>
+  );
+}
+
+ConfirmTransactionContent.propTypes = {
+  closeModal: PropTypes.func,
+  text: PropTypes.string,
+  onRollback: PropTypes.func,
+  onCommit: PropTypes.func
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/FilterDialog.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/FilterDialog.jsx
new file mode 100644
index 000000000..336b47d31
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/FilterDialog.jsx
@@ -0,0 +1,155 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/core';
+import React from 'react';
+import SchemaView from '../../../../../../static/js/SchemaView';
+import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';
+import gettext from 'sources/gettext';
+import { QueryToolContext } from '../QueryToolComponent';
+import url_for from 'sources/url_for';
+import PropTypes from 'prop-types';
+
+class SortingCollection extends BaseUISchema {
+  constructor(columnOptions) {
+    super({
+      name: undefined,
+      order: 'asc',
+    });
+    this.columnOptions = columnOptions;
+    this.reloadColOptions = 0;
+  }
+
+  setColumnOptions(columnOptions) {
+    this.columnOptions = columnOptions;
+    this.reloadColOptions = this.reloadColOptions + 1;
+  }
+
+  get baseFields() {
+    return [
+      {
+        id: 'name', label: gettext('Column'), cell: 'select', controlProps: {
+          allowClear: false,
+        }, noEmpty: true, options: this.columnOptions, optionsReloadBasis: this.reloadColOptions
+      },
+      {
+        id: 'order', label: gettext('Order'), cell: 'select', controlProps: {
+          allowClear: false,
+        }, options: [
+          {label: gettext('ASC'), value: 'asc'},
+          {label: gettext('DESC'), value: 'desc'},
+        ]
+      },
+    ];
+  }
+}
+
+class FilterSchema extends BaseUISchema {
+  constructor(columnOptions) {
+    super({
+      sql: null,
+      data_sorting: [],
+    });
+    this.sortingCollObj = new SortingCollection(columnOptions);
+  }
+
+  setColumnOptions(columnOptions) {
+    this.sortingCollObj.setColumnOptions(columnOptions);
+  }
+
+  get baseFields() {
+    let obj = this;
+    return [
+      {
+        id: 'sql', label: gettext('SQL Filter'), type: 'sql', controlProps: {
+          options: {
+            lineWrapping: true,
+          },
+        }
+      },
+      {
+        id: 'data_sorting', label: gettext('Data Sorting'), type: 'collection', schema: obj.sortingCollObj,
+        group: 'temp', uniqueCol: ['name'], canAdd: true, canEdit: false, canDelete: true,
+      },
+    ];
+  }
+}
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    ...theme.mixins.tabPanel,
+  },
+}));
+
+export default function FilterDialog({onClose, onSave}) {
+  const classes = useStyles();
+  const queryToolCtx = React.useContext(QueryToolContext);
+  const filterSchemaObj = React.useMemo(()=>new FilterSchema([]));
+
+  const getInitData = ()=>{
+    return new Promise((resolve, reject)=>{
+      const getFilterData = async ()=>{
+        try {
+          let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_filter_data', {
+            'trans_id': queryToolCtx.params.trans_id,
+          }));
+          let {column_list: columns, ...filterData} = respData.data.result;
+          filterSchemaObj.setColumnOptions((columns||[]).map((c)=>({label: c, value: c})));
+          resolve(filterData);
+        } catch (error) {
+          reject(error);
+        }
+      };
+      getFilterData();
+    });
+  };
+
+  const onSaveClick = (_isNew, changeData)=>{
+    return new Promise((resolve, reject)=>{
+      const setFilterData = async ()=>{
+        try {
+          let {data: respData} = await queryToolCtx.api.put(url_for('sqleditor.set_filter_data', {
+            'trans_id': queryToolCtx.params.trans_id,
+          }), changeData);
+          if(respData.data.status) {
+            resolve();
+            onSave();
+          } else {
+            reject(respData.data.result);
+          }
+        } catch (error) {
+          reject(error);
+        }
+      };
+      setFilterData();
+    });
+  };
+
+  return (<>
+    <SchemaView
+      formType={'dialog'}
+      getInitData={getInitData}
+      schema={filterSchemaObj}
+      viewHelperProps={{
+        mode: 'create',
+      }}
+      onSave={onSaveClick}
+      onClose={onClose}
+      hasSQL={false}
+      disableSqlHelp={true}
+      disableDialogHelp={true}
+      isTabView={false}
+      formClassName={classes.root}
+    />
+  </>);
+}
+
+FilterDialog.propTypes = {
+  onClose: PropTypes.func,
+  onSave: PropTypes.func,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx
new file mode 100644
index 000000000..f67e18899
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/MacrosDialog.jsx
@@ -0,0 +1,189 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/core';
+import React from 'react';
+import SchemaView from '../../../../../../static/js/SchemaView';
+import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';
+import gettext from 'sources/gettext';
+import { QueryToolContext } from '../QueryToolComponent';
+import url_for from 'sources/url_for';
+import _ from 'lodash';
+import PropTypes from 'prop-types';
+
+class MacrosCollection extends BaseUISchema {
+  constructor(keyOptions) {
+    super();
+    this.keyOptions = keyOptions;
+  }
+
+  get idAttribute() {
+    return 'mid';
+  }
+
+  get baseFields() {
+    let obj = this;
+    return [
+      {
+        id: 'id', label: gettext('Key'), cell: 'select', noEmpty: true,
+        width: 100, options: obj.keyOptions, optionsReloadBasis: obj.keyOptions.length,
+        controlProps: {
+          allowClear: false,
+        }
+      },
+      {
+        id: 'name', label: gettext('Name'), cell: 'text', noEmpty: true,
+        width: 100,
+      },
+      {
+        id: 'sql', label: gettext('SQL'), cell: 'sql', noEmpty: true,
+        width: 300, controlProps: {
+          options: {
+            foldGutter: false,
+            lineNumbers: false,
+            gutters: [],
+            readOnly: true,
+            lineWrapping: true,
+          },
+        }
+      },
+    ];
+  }
+}
+
+class MacrosSchema extends BaseUISchema {
+  constructor(keyOptions) {
+    super();
+    this.macrosCollObj = new MacrosCollection(keyOptions);
+  }
+
+  get baseFields() {
+    let obj = this;
+    return [
+      {
+        id: 'macro', label: '', type: 'collection', schema: obj.macrosCollObj,
+        canAdd: true, canDelete: true, isFullTab: true, group: 'temp',
+      },
+    ];
+  }
+
+  validate(state, setError) {
+    let allKeys = state.macro.map((m)=>m.id.toString());
+    if(allKeys.length !=  new Set(allKeys).size) {
+      setError('macro', gettext('Key must be unique.'));
+      return true;
+    }
+    return false;
+  }
+}
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    ...theme.mixins.tabPanel,
+    padding: 0,
+  },
+}));
+
+function getChangedMacros(macrosData, changeData) {
+  /* For backend, added, removed is changed. Convert all added, removed to changed. */
+  let changed = [];
+  for (const m of (changeData.macro.changed || [])) {
+    let newM = {...m};
+    if('id' in m) {
+      /* if key changed, clear prev and add new */
+      changed.push({id: m.mid, name: null, sql: null});
+      let em = _.find(macrosData, (d)=>d.mid==m.mid);
+      newM = {name: em.name, sql: em.sql, ...m};
+    } else {
+      newM.id = m.mid;
+    }
+    delete newM.mid;
+    changed.push(newM);
+  }
+  for (const m of (changeData.macro.deleted || [])) {
+    changed.push({id: m.id, name: null, sql: null});
+  }
+  for (const m of (changeData.macro.added || [])) {
+    changed.push(m);
+  }
+  return changed;
+}
+
+export default function MacrosDialog({onClose, onSave}) {
+  const classes = useStyles();
+  const queryToolCtx = React.useContext(QueryToolContext);
+  const [macrosData, setMacrosData] = React.useState([]);
+  const [macrosErr, setMacrosErr] = React.useState(null);
+
+  React.useEffect(async ()=>{
+    try {
+      let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_macros', {
+        'trans_id': queryToolCtx.params.trans_id,
+      }));
+      /* Copying id to mid to track key id changes */
+      setMacrosData(respData.macro.map((m)=>({...m, mid: m.id})));
+    } catch (error) {
+      setMacrosErr(error);
+    }
+  }, []);
+
+  const onSaveClick = (_isNew, changeData)=>{
+    return new Promise((resolve, reject)=>{
+      const setMacros = async ()=>{
+        try {
+          let changed = getChangedMacros(macrosData, changeData);
+          let {data: respData} = await queryToolCtx.api.put(url_for('sqleditor.set_macros', {
+            'trans_id': queryToolCtx.params.trans_id,
+          }), {changed: changed});
+          resolve();
+          onSave(respData.macro?.filter((m)=>Boolean(m.name)));
+          onClose();
+        } catch (error) {
+          reject(error);
+        }
+      };
+      setMacros();
+    });
+  };
+
+  const keyOptions = macrosData.map((m)=>({
+    label: m.key_label,
+    value: m.id,
+  }));
+
+  if(keyOptions.length <= 0) {
+    return <></>;
+  }
+  return (<>
+    <SchemaView
+      formType={'dialog'}
+      getInitData={()=>{
+        if(macrosErr) {
+          return Promise.reject(macrosErr);
+        }
+        return Promise.resolve({macro: macrosData.filter((m)=>Boolean(m.name))});
+      }}
+      schema={new MacrosSchema(keyOptions)}
+      viewHelperProps={{
+        mode: 'edit',
+      }}
+      onSave={onSaveClick}
+      onClose={onClose}
+      hasSQL={false}
+      disableSqlHelp={true}
+      disableDialogHelp={true}
+      isTabView={false}
+      formClassName={classes.root}
+    />
+  </>);
+}
+
+MacrosDialog.propTypes = {
+  onClose: PropTypes.func,
+  onSave: PropTypes.func,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx
new file mode 100644
index 000000000..fc35c0964
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/dialogs/NewConnectionDialog.jsx
@@ -0,0 +1,254 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/core';
+import React, { useState } from 'react';
+import SchemaView from '../../../../../../static/js/SchemaView';
+import BaseUISchema from '../../../../../../static/js/SchemaView/base_schema.ui';
+import gettext from 'sources/gettext';
+import { QueryToolContext } from '../QueryToolComponent';
+import url_for from 'sources/url_for';
+import _ from 'lodash';
+import { flattenSelectOptions } from '../../../../../../static/js/components/FormComponents';
+import PropTypes from 'prop-types';
+import ConnectServerContent from '../../../../../../browser/static/js/ConnectServerContent';
+
+class NewConnectionSchema extends BaseUISchema {
+  constructor(api, params, connectServer) {
+    super({
+      sid: null,
+      did: null,
+      user: null,
+      role: null,
+      server_name: null,
+      database_name: null,
+    });
+    this.flatServers = [];
+    this.groupedServers = [];
+    this.dbs = [];
+    this.params = params;
+    this.api = api;
+    this.warningText = gettext('By changing the connection you will lose all your unsaved data for the current connection. <br> Do you want to continue?');
+    this.connectServer = connectServer;
+  }
+
+  setServerConnected(sid, icon) {
+    for(const group of this.groupedServers) {
+      for(const opt of group.options) {
+        if(opt.value == sid) {
+          opt.connected = true;
+          opt.image = icon || 'icon-pg';
+          break;
+        }
+      }
+    }
+  }
+
+  isServerConnected(sid) {
+    return _.find(this.flatServers, (s)=>s.value==sid)?.connected;
+  }
+
+  getServerList() {
+    let obj = this;
+    if(this.groupedServers?.length != 0) {
+      return Promise.resolve(this.groupedServers);
+    }
+    return new Promise((resolve, reject)=>{
+      this.api.get(url_for('sqleditor.get_new_connection_servers'))
+        .then(({data: respData})=>{
+          let groupedOptions = [];
+          _.forIn(respData.data.result.server_list, (v, k)=>{
+            /* initial selection */
+            _.find(v, (o)=>o.value==obj.params.sid).selected = true;
+            groupedOptions.push({
+              label: k,
+              options: v,
+            });
+          });
+          /* Will be re-used for changing icon when connected */
+          this.groupedServers = groupedOptions.map((group)=>{
+            return {
+              label: group.label,
+              options: group.options.map((o)=>({...o, selected: false})),
+            };
+          });
+          resolve(groupedOptions);
+        })
+        .catch((error)=>{
+          reject(error);
+        });
+    });
+  }
+
+  getOtherOptions(sid, type) {
+    if(!sid) {
+      return [];
+    }
+
+    if(!this.isServerConnected(sid)) {
+      return [];
+    }
+    return new Promise((resolve, reject)=>{
+      this.api.get(url_for(`sqleditor.${type}`, {
+        'sid': sid,
+        'sgid': 0,
+      }))
+        .then(({data: respData})=>{
+          resolve(respData.data.result.data);
+        })
+        .catch((error)=>{
+          reject(error);
+        });
+    });
+  }
+
+  get baseFields() {
+    let self = this;
+    return [
+      {
+        id: 'sid', label: gettext('Server'), type: 'select', noEmpty: true,
+        controlProps: {
+          allowClear: false,
+        }, options: ()=>this.getServerList(),
+        optionsLoaded: (res)=>this.flatServers=flattenSelectOptions(res),
+        optionsReloadBasis: this.flatServers.map((s)=>s.connected).join(''),
+        depChange: (state)=>{
+          /* Once the option is selected get the name */
+          /* Force sid to null, and set only if connected */
+          return {
+            server_name: _.find(this.flatServers, (s)=>s.value==state.sid)?.label,
+            did: null,
+            user: null,
+            role: null,
+            sid: null,
+          };
+        },
+        deferredDepChange: (state, source, topState, actionObj)=>{
+          return new Promise((resolve)=>{
+            let sid = actionObj.value;
+            if(!_.find(this.flatServers, (s)=>s.value==sid)?.connected) {
+              this.connectServer(sid, state.user, null, (data)=>{
+                self.setServerConnected(sid, data.icon);
+                resolve(()=>({sid: sid}));
+              });
+            } else {
+              resolve(()=>({sid: sid}));
+            }
+          });
+        },
+      }, {
+        id: 'did', label: gettext('Database'), deps: ['sid'], noEmpty: true,
+        controlProps: {
+          allowClear: false,
+        },
+        type: (state)=>({
+          type: 'select',
+          options: ()=>this.getOtherOptions(state.sid, 'get_new_connection_database'),
+          optionsReloadBasis: `${state.sid} ${this.isServerConnected(state.sid)}`,
+        }),
+        optionsLoaded: (res)=>this.dbs=res,
+        depChange: (state)=>{
+          /* Once the option is selected get the name */
+          return {database_name: _.find(this.dbs, (s)=>s.value==state.did)?.label};
+        }
+      },{
+        id: 'user', label: gettext('User'), deps: ['sid'], noEmpty: true,
+        controlProps: {
+          allowClear: false,
+        },
+        type: (state)=>({
+          type: 'select',
+          options: ()=>this.getOtherOptions(state.sid, 'get_new_connection_user'),
+          optionsReloadBasis: `${state.sid} ${this.isServerConnected(state.sid)}`,
+        }),
+      },{
+        id: 'role', label: gettext('Role'), deps: ['sid'],
+        type: (state)=>({
+          type: 'select',
+          options: ()=>this.getOtherOptions(state.sid, 'get_new_connection_role'),
+          optionsReloadBasis: `${state.sid} ${this.isServerConnected(state.sid)}`,
+        }),
+      },{
+        id: 'server_name', label: '', type: 'text', visible: false,
+      },{
+        id: 'database_name', label: '', type: 'text', visible: false,
+      },
+    ];
+  }
+}
+
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    ...theme.mixins.tabPanel,
+  },
+}));
+
+export default function NewConnectionDialog({onClose, onSave}) {
+  const classes = useStyles();
+  const [connecting, setConnecting] = useState(false);
+  const queryToolCtx = React.useContext(QueryToolContext);
+
+  const connectServer = async (sid, user, formData, connectCallback) => {
+    setConnecting(true);
+    try {
+      let {data: respData} = await queryToolCtx.api({
+        method: 'POST',
+        url: url_for('sqleditor.connect_server', {
+          'sid': sid,
+          ...(user ? {
+            'usr': user,
+          }:{}),
+        }),
+        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+        data: formData
+      });
+      setConnecting(false);
+      connectCallback?.(respData.data);
+    } catch (error) {
+      queryToolCtx.modal.showModal(gettext('Connect to server'), (closeModal)=>{
+        return (
+          <ConnectServerContent
+            closeModal={()=>{
+              setConnecting(false);
+              closeModal();
+            }}
+            data={error.response?.data?.result}
+            onOK={(formData)=>{
+              connectServer(sid, null, formData, connectCallback);
+            }}
+          />
+        );
+      });
+    }
+  };
+
+  return <SchemaView
+    formType={'dialog'}
+    getInitData={()=>Promise.resolve({})}
+    schema={new NewConnectionSchema(queryToolCtx.api, {
+      sid: queryToolCtx.params.sid, sgid: 0,
+    }, connectServer)}
+    viewHelperProps={{
+      mode: 'create',
+    }}
+    loadingText={connecting ? 'Connecting...' : ''}
+    onSave={onSave}
+    onClose={onClose}
+    hasSQL={false}
+    disableSqlHelp={true}
+    disableDialogHelp={true}
+    isTabView={false}
+    formClassName={classes.root}
+  />;
+}
+
+NewConnectionDialog.propTypes = {
+  onClose: PropTypes.func,
+  onSave: PropTypes.func,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx
new file mode 100644
index 000000000..da1493e69
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ConnectionBar.jsx
@@ -0,0 +1,148 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React from 'react';
+import { makeStyles } from '@material-ui/styles';
+import { Box, CircularProgress, Tooltip } from '@material-ui/core';
+import { DefaultButton, PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
+import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
+import { ConnectedIcon, DisonnectedIcon, QueryToolIcon } from '../../../../../../static/js/components/ExternalIcon';
+import { QueryToolContext } from '../QueryToolComponent';
+import { CONNECTION_STATUS, CONNECTION_STATUS_MESSAGE } from '../QueryToolConstants';
+import HourglassEmptyRoundedIcon from '@material-ui/icons/HourglassEmptyRounded';
+import QueryBuilderRoundedIcon from '@material-ui/icons/QueryBuilderRounded';
+import ErrorOutlineRoundedIcon from '@material-ui/icons/ErrorOutlineRounded';
+import ReportProblemRoundedIcon from '@material-ui/icons/ReportProblemRounded';
+import { PgMenu, PgMenuItem } from '../../../../../../static/js/components/Menu';
+import gettext from 'sources/gettext';
+import RotateLeftRoundedIcon from '@material-ui/icons/RotateLeftRounded';
+import PropTypes from 'prop-types';
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    padding: '2px 4px',
+    display: 'flex',
+    alignItems: 'center',
+    gap: '4px',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+  },
+  connectionButton: {
+    display: 'flex',
+    width: '450px',
+    backgroundColor: theme.palette.default.main,
+    color: theme.palette.default.contrastText,
+    border: '1px solid ' + theme.palette.default.borderColor,
+    justifyContent: 'flex-start',
+  },
+  viewDataConnTitle: {
+    marginTop: 'auto',
+    marginBottom: 'auto',
+    padding: '0px 4px',
+  },
+  menu: {
+    '& .szh-menu': {
+      minWidth: '450px',
+    }
+  }
+}));
+
+function ConnectionStatusIcon({connected, connecting, status}) {
+  if(connecting) {
+    return <CircularProgress style={{height: '18px', width: '18px'}} />;
+  } else if(connected) {
+    switch (status) {
+    case CONNECTION_STATUS.TRANSACTION_STATUS_ACTIVE:
+      return <HourglassEmptyRoundedIcon />;
+    case CONNECTION_STATUS.TRANSACTION_STATUS_INTRANS:
+      return <QueryBuilderRoundedIcon />;
+    case CONNECTION_STATUS.TRANSACTION_STATUS_INERROR:
+      return <ErrorOutlineRoundedIcon />;
+    case CONNECTION_STATUS.TRANSACTION_STATUS_UNKNOWN:
+      return <ReportProblemRoundedIcon />;
+    default:
+      return <ConnectedIcon />;
+    }
+  } else {
+    return <DisonnectedIcon />;
+  }
+}
+
+ConnectionStatusIcon.propTypes = {
+  connected: PropTypes.bool,
+  connecting: PropTypes.bool,
+  status: PropTypes.oneOf(Object.values(CONNECTION_STATUS)),
+};
+
+export function ConnectionBar({connected, connecting, connectionStatus, connectionStatusMsg,
+  connectionList, onConnectionChange, onNewConnClick, onNewQueryToolClick, onResetLayout}) {
+  const classes = useStyles();
+  const connMenuRef = React.useRef();
+  const [connDropdownOpen, setConnDropdownOpen] = React.useState(false);
+  const queryToolCtx = React.useContext(QueryToolContext);
+  const onConnItemClick = (e)=>{
+    if(!e.value.is_selected) {
+      onConnectionChange(e.value);
+    }
+    e.keepOpen = false;
+  };
+
+  const connTitle = React.useMemo(()=>_.find(connectionList, (c)=>c.is_selected)?.conn_title, [connectionList]);
+  return (
+    <>
+      <Box className={classes.root}>
+        <PgButtonGroup size="small">
+          {queryToolCtx.preferences?.sqleditor?.connection_status &&
+          <PgIconButton title={CONNECTION_STATUS_MESSAGE[connected ? connectionStatus : -1] ?? connectionStatusMsg}
+            icon={<ConnectionStatusIcon connecting={connecting} status={connectionStatus} connected={connected}/>}
+          />}
+          <DefaultButton className={classes.connectionButton} ref={connMenuRef}
+            onClick={queryToolCtx.params.is_query_tool ? ()=>setConnDropdownOpen(true) : undefined}
+            style={{backgroundColor: queryToolCtx.params.bgcolor, color: queryToolCtx.params.fgcolor}}
+          >
+            <Tooltip title={queryToolCtx.params.is_query_tool ? '' : connTitle}>
+              <Box display="flex" width="100%">
+                <Box textOverflow="ellipsis" overflow="hidden" marginRight="auto">{connecting && '(Obtaining connection)'}{connTitle}</Box>
+                {queryToolCtx.params.is_query_tool && <Box><KeyboardArrowDownIcon /></Box>}
+              </Box>
+            </Tooltip>
+          </DefaultButton>
+          <PgIconButton title="New query tool" icon={<QueryToolIcon />} onClick={onNewQueryToolClick}/>
+        </PgButtonGroup>
+        <PgButtonGroup size="small" variant="text" style={{marginLeft: 'auto'}}>
+          <PgIconButton title="Reset layout" icon={<RotateLeftRoundedIcon />} onClick={onResetLayout}/>
+        </PgButtonGroup>
+      </Box>
+      <PgMenu
+        anchorRef={connMenuRef}
+        open={connDropdownOpen}
+        onClose={()=>{setConnDropdownOpen(false);}}
+        className={classes.menu}
+      >
+        {(connectionList||[]).map((conn)=>{
+          return (
+            <PgMenuItem key={conn.conn_title} hasCheck checked={conn.is_selected} value={conn}
+              onClick={onConnItemClick}>{conn.conn_title}</PgMenuItem>
+          );
+        })}
+        <PgMenuItem onClick={onNewConnClick}>{`< ${gettext('New connection...')} >`}</PgMenuItem>
+      </PgMenu>
+    </>
+  );
+}
+
+ConnectionBar.propTypes = {
+  connected: PropTypes.bool,
+  connecting: PropTypes.bool,
+  connectionStatus: PropTypes.oneOf(Object.values(CONNECTION_STATUS)),
+  connectionStatusMsg: PropTypes.string,
+  connectionList: PropTypes.array,
+  onConnectionChange: PropTypes.func,
+  onNewConnClick: PropTypes.func,
+  onNewQueryToolClick: PropTypes.func,
+  onResetLayout: PropTypes.func,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/GeometryViewer.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/GeometryViewer.jsx
new file mode 100644
index 000000000..845148d6a
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/GeometryViewer.jsx
@@ -0,0 +1,386 @@
+
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React, { useEffect, useRef }  from 'react';
+import ReactDOMServer from 'react-dom/server';
+import { makeStyles } from '@material-ui/styles';
+import _ from 'lodash';
+import { MapContainer, TileLayer, LayersControl, GeoJSON, useMap } from 'react-leaflet';
+import Leaflet, { CRS } from 'leaflet';
+import {Geometry as WkxGeometry} from 'wkx';
+import {Buffer} from 'buffer';
+import gettext from 'sources/gettext';
+import Theme from 'sources/Theme';
+import clsx from 'clsx';
+import PropTypes from 'prop-types';
+
+const useStyles = makeStyles((theme)=>({
+  mapContainer: {
+    backgroundColor: theme.palette.background.default,
+    height: '100%',
+    width: '100%'
+  },
+  table: {
+    borderSpacing: 0,
+    width: '100%',
+    ...theme.mixins.panelBorder,
+  },
+  tableCell: {
+    margin: 0,
+    padding: theme.spacing(0.5),
+    ...theme.mixins.panelBorder.bottom,
+    ...theme.mixins.panelBorder.right,
+    overflow: 'hidden',
+    textOverflow: 'ellipsis',
+    whiteSpace: 'nowrap',
+  },
+  tableCellHead: {
+    fontWeight: 'bold',
+  }
+}));
+
+function parseEwkbData(rows, column) {
+  let key = column.key;
+  const maxRenderByteLength = 20 * 1024 * 1024; //render geometry data up to 20MB
+  const maxRenderGeometries = 100000; // render geometries up to 100000
+
+  let geometries3D = [],
+    supportedGeometries = [],
+    unsupportedRows = [],
+    geometryItemMap = new Map(),
+    geometryTotalByteLength = 0,
+    tooLargeDataSize = false,
+    tooManyGeometries = false,
+    infoList = [];
+
+  _.every(rows, function (item) {
+    try {
+      let value = item[key];
+      let buffer = Buffer.from(value, 'hex');
+      let geometry = WkxGeometry.parse(buffer);
+      if (geometry.hasZ) {
+        geometries3D.push(geometry);
+        return true;
+      }
+      geometryTotalByteLength += buffer.byteLength;
+      if (geometryTotalByteLength > maxRenderByteLength) {
+        tooLargeDataSize = true;
+        return false;
+      }
+      if (supportedGeometries.length >= maxRenderGeometries) {
+        tooManyGeometries = true;
+        return false;
+      }
+
+      if (!geometry.srid) {
+        geometry.srid = 0;
+      }
+      supportedGeometries.push(geometry);
+      geometryItemMap.set(geometry, item);
+    } catch (e) {
+      unsupportedRows.push(item);
+    }
+    return true;
+  });
+
+  // generate map info content
+  if (tooLargeDataSize || tooManyGeometries) {
+    infoList.push(supportedGeometries.length + ' of ' + rows.length + ' geometries rendered.');
+  }
+  if (geometries3D.length > 0) {
+    infoList.push(gettext('3D geometries not rendered.'));
+  }
+  if (unsupportedRows.length > 0) {
+    infoList.push(gettext('Unsupported geometries not rendered.'));
+  }
+
+  return [
+    supportedGeometries,
+    geometryItemMap,
+    infoList
+  ];
+}
+
+function parseData(rows, columns, column) {
+  if (rows.length === 0) {
+    return {
+      'geoJSONs': [],
+      'selectedSRID': 0,
+      'getPopupContent': undefined,
+      'infoList': ['Empty row.'],
+    };
+  }
+
+  let mixedSRID = false;
+  // parse ewkb data
+  let [
+    supportedGeometries,
+    geometryItemMap,
+    infoList
+  ] = parseEwkbData(rows, column);
+
+  if (supportedGeometries.length === 0) {
+    return {
+      'geoJSONs': [],
+      'selectedSRID': 0,
+      'getPopupContent': undefined,
+      'infoList': infoList,
+    };
+  }
+
+  // group geometries by SRID
+  let geometriesGroupBySRID = _.groupBy(supportedGeometries, 'srid');
+  let SRIDGeometriesPairs = _.toPairs(geometriesGroupBySRID);
+  if (SRIDGeometriesPairs.length > 1) {
+    mixedSRID = true;
+  }
+  // select the largest group
+  let selectedPair = _.max(SRIDGeometriesPairs, function (pair) {
+    return pair[1].length;
+  });
+  let selectedSRID = parseInt(selectedPair[0]);
+  let selectedGeometries = selectedPair[1];
+
+  let geoJSONs = _.map(selectedGeometries, function (geometry) {
+    return geometry.toGeoJSON();
+  });
+
+  let getPopupContent;
+  if (columns.length >= 3) {
+    // add popup when geometry has properties
+    getPopupContent = function (geojson) {
+      let geometry = selectedGeometries[geoJSONs.indexOf(geojson)];
+      let row = geometryItemMap.get(geometry);
+      let retVal = [];
+      for (const col of columns) {
+        if(col.key === column.key) {
+          continue;
+        }
+        retVal.push({
+          'column': col.display_name,
+          'value': row[col.key],
+        });
+      }
+      return retVal;
+    };
+  }
+
+  if (mixedSRID) {
+    infoList.push(gettext('Geometries with non-SRID %s not rendered.', selectedSRID));
+  }
+
+  return {
+    'geoJSONs': geoJSONs,
+    'selectedSRID': selectedSRID,
+    'getPopupContent': getPopupContent,
+    'infoList': infoList,
+  };
+}
+
+function PopupTable({data}) {
+  const classes = useStyles();
+  return (
+    <table className={classes.table}>
+      <tbody>
+        {data.map((row)=>{
+          return (
+            <tr key={row.column}>
+              <td className={clsx(classes.tableCell, classes.tableCellHead)}>{row.column}</td>
+              <td className={classes.tableCell}>{row.value}</td>
+            </tr>
+          );
+        })}
+      </tbody>
+    </table>
+  );
+}
+
+PopupTable.propTypes = {
+  data: PropTypes.arrayOf({
+    column: PropTypes.string,
+    value: PropTypes.string,
+  }),
+};
+
+function GeoJsonLayer({data}) {
+  const vectorLayerRef = useRef(null);
+  const mapObj = useMap();
+  useEffect(() => {
+    if(!vectorLayerRef.current) return;
+    if(data.geoJSONs.length <= 0) return;
+    let bounds = vectorLayerRef.current.getBounds().pad(0.1);
+    let maxLength = Math.max(bounds.getNorth() - bounds.getSouth(),
+      bounds.getEast() - bounds.getWest());
+    let minZoom = 0;
+    if(data.selectedSRID !== 4326) {
+      if (maxLength >= 180) {
+        // calculate the min zoom level to enable the map to fit the whole geometry.
+        minZoom = Math.floor(Math.log2(360 / maxLength)) - 2;
+      }
+    }
+    mapObj.setMinZoom(minZoom);
+    if (maxLength > 0) {
+      mapObj.fitBounds(bounds);
+    } else {
+      mapObj.setView(bounds.getCenter(), mapObj.getZoom());
+    }
+  });
+
+  return (
+    <GeoJSON
+      ref={vectorLayerRef}
+      pointToLayer={(_feature, latlng)=>{
+        return Leaflet.circleMarker(latlng, {
+          radius: 4,
+          weight: 3,
+        });
+      }}
+      style={{weight: 2}}
+      onEachFeature={(feature, layer)=>{
+        if(_.isFunction(data.getPopupContent)) {
+          const popupContentNode = (
+            <Theme>
+              <PopupTable data={data.getPopupContent(layer.feature.geometry)}/>
+            </Theme>
+          );
+          const popupContentHtml = ReactDOMServer.renderToString(popupContentNode);
+          layer.bindPopup(popupContentHtml, {
+            closeButton: false,
+            minWidth: 260,
+            maxWidth: 300,
+            maxHeight: 300,
+          });
+        }
+      }}
+      data={data.geoJSONs}
+    />
+  );
+}
+
+GeoJsonLayer.propTypes = {
+  data: PropTypes.shape({
+    geoJSONs: PropTypes.array,
+    selectedSRID: PropTypes.number,
+    getPopupContent: PropTypes.func,
+    infoList: PropTypes.array,
+  }),
+};
+
+function TheMap({data}) {
+  const mapObj = useMap();
+  const infoControl = useRef(null);
+  useEffect(()=>{
+    infoControl.current = Leaflet.control({position: 'topright'});
+    infoControl.current.onAdd = function () {
+      let ele = Leaflet.DomUtil.create('div', 'geometry-viewer-info-control');
+      ele.innerHTML = data.infoList.join('<br />');
+      return ele;
+    };
+    if(data.infoList.length > 0) {
+      infoControl.current.addTo(mapObj);
+    }
+    return ()=>{infoControl.current && infoControl.current.remove();};
+  }, [data]);
+  return (
+    <>
+      {data.selectedSRID === 4326 &&
+    <LayersControl position="topright">
+      <LayersControl.BaseLayer checked name="Empty">
+        <TileLayer
+          url=""
+        />
+      </LayersControl.BaseLayer>
+      <LayersControl.BaseLayer checked name="Street">
+        <TileLayer
+          url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
+          maxZoom={19}
+          attribution='&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
+        />
+      </LayersControl.BaseLayer>
+      <LayersControl.BaseLayer name="Topography">
+        <TileLayer
+          url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
+          maxZoom={17}
+          attribution={
+            '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
+            + ' &copy; <a href="http://viewfinderpanoramas.org" target="_blank">SRTM</a>,'
+            + ' &copy; <a href="https://opentopomap.org" target="_blank">OpenTopoMap</a>'
+          }
+        />
+      </LayersControl.BaseLayer>
+      <LayersControl.BaseLayer name="Gray Style">
+        <TileLayer
+          url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png"
+          maxZoom={19}
+          attribution={
+            '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
+            + ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
+          }
+          subdomains='abcd'
+        />
+      </LayersControl.BaseLayer>
+      <LayersControl.BaseLayer name="Light Color">
+        <TileLayer
+          url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager/{z}/{x}/{y}{r}.pn"
+          maxZoom={19}
+          attribution={
+            '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
+            + ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
+          }
+          subdomains='abcd'
+        />
+      </LayersControl.BaseLayer>
+      <LayersControl.BaseLayer name="Dark Matter">
+        <TileLayer
+          url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png"
+          maxZoom={19}
+          attribution={
+            '&copy; <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
+            + ' &copy; <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
+          }
+          subdomains='abcd'
+        />
+      </LayersControl.BaseLayer>
+    </LayersControl>}
+      <GeoJsonLayer key={data.geoJSONs.length} data={data}/>
+    </>
+  );
+}
+TheMap.propTypes = {
+  data: PropTypes.shape({
+    geoJSONs: PropTypes.array,
+    selectedSRID: PropTypes.number,
+    getPopupContent: PropTypes.func,
+    infoList: PropTypes.array,
+  }),
+};
+
+
+export function GeometryViewer({rows, columns, column}) {
+  const classes = useStyles();
+  const data = parseData(rows, columns, column);
+  const crs = data.selectedSRID === 4326 ? CRS.EPSG3857 : CRS.Simple;
+  return (
+    <MapContainer
+      crs={crs}
+      zoom={2} center={[20, 100]}
+      preferCanvas={true}
+      scrollWheelZoom={false}
+      className={classes.mapContainer}
+    >
+      <TheMap data={data} />
+    </MapContainer>
+  );
+}
+
+GeometryViewer.propTypes = {
+  rows: PropTypes.array,
+  columns: PropTypes.array,
+  column: PropTypes.object,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
new file mode 100644
index 000000000..42d1222c5
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/MainToolBar.jsx
@@ -0,0 +1,607 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React, {useContext, useCallback, useEffect, useState} from 'react';
+import { makeStyles } from '@material-ui/styles';
+import { Box } from '@material-ui/core';
+import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
+import FolderRoundedIcon from '@material-ui/icons/FolderRounded';
+import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
+import SaveRoundedIcon from '@material-ui/icons/SaveRounded';
+import StopRoundedIcon from '@material-ui/icons/StopRounded';
+import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
+import { FilterIcon, CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
+import EditRoundedIcon from '@material-ui/icons/EditRounded';
+import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
+import ExplicitRoundedIcon from '@material-ui/icons/ExplicitRounded';
+import FormatListNumberedRoundedIcon from '@material-ui/icons/FormatListNumberedRounded';
+import HelpIcon from '@material-ui/icons/HelpRounded';
+import {QUERY_TOOL_EVENTS, CONNECTION_STATUS} from '../QueryToolConstants';
+import { QueryToolConnectionContext, QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
+import { PgMenu, PgMenuDivider, PgMenuItem } from '../../../../../../static/js/components/Menu';
+import gettext from 'sources/gettext';
+import { useKeyboardShortcuts } from '../../../../../../static/js/custom_hooks';
+import {shortcut_key} from 'sources/keyboard_shortcuts';
+import url_for from 'sources/url_for';
+import _ from 'lodash';
+import { InputSelectNonSearch } from '../../../../../../static/js/components/FormComponents';
+import PropTypes from 'prop-types';
+import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
+import ConfirmTransactionContent from '../dialogs/ConfirmTransactionContent';
+import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    padding: '2px 4px',
+    display: 'flex',
+    alignItems: 'center',
+    gap: '4px',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+    ...theme.mixins.panelBorder.bottom,
+  },
+}));
+
+const FIXED_PREF = {
+  find: {
+    'control': true,
+    ctrl_is_meta: true,
+    'shift': false,
+    'alt': false,
+    'key': {
+      'key_code': 70,
+      'char': 'F',
+    },
+  },
+  replace: {
+    'control': true,
+    ctrl_is_meta: true,
+    'shift': isMac() ? false : true,
+    'alt': isMac() ? true : false,
+    'key': {
+      'key_code': 70,
+      'char': 'F',
+    },
+  },
+  jump: {
+    'control': false,
+    'shift': false,
+    'alt': true,
+    'key': {
+      'key_code': 71,
+      'char': 'G',
+    },
+  },
+  indent: {
+    'control': false,
+    'shift': false,
+    'alt': false,
+    'key': {
+      'key_code': 9,
+      'char': 'Tab',
+    },
+  },
+  unindent: {
+    'control': false,
+    'shift': true,
+    'alt': false,
+    'key': {
+      'key_code': 9,
+      'char': 'Tab',
+    },
+  },
+  comment: {
+    'control': true,
+    ctrl_is_meta: true,
+    'shift': false,
+    'alt': false,
+    'key': {
+      'key_code': 191,
+      'char': '/',
+    },
+  },
+  uncomment: {
+    'control': true,
+    ctrl_is_meta: true,
+    'shift': false,
+    'alt': false,
+    'key': {
+      'key_code': 190,
+      'char': '.',
+    },
+  },
+  format_sql: {
+    'control': true,
+    'shift': true,
+    'alt': false,
+    'key': {
+      'key_code': 75,
+      'char': 'k',
+    },
+  },
+};
+
+function autoCommitRollback(type, api, transId, value) {
+  let url = url_for(`sqleditor.${type}`, {
+    'trans_id': transId,
+  });
+  return api.post(url, JSON.stringify(value));
+}
+
+export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
+  const classes = useStyles();
+  const eventBus = useContext(QueryToolEventsContext);
+  const queryToolCtx = useContext(QueryToolContext);
+  const queryToolConnCtx = useContext(QueryToolConnectionContext);
+
+  const [highlightFilter, setHighlightFilter] = useState(false);
+  const [limit, setLimit] = useState('-1');
+  const [buttonsDisabled, setButtonsDisabled] = useState({
+    'save': true,
+    'cancel': true,
+    'save-data': true,
+    'delete-rows': true,
+    'commit': true,
+    'rollback': true,
+    'filter': true,
+    'limit': false,
+  });
+  const [menuOpenId, setMenuOpenId] = React.useState(null);
+  const [checkedMenuItems, setCheckedMenuItems] = React.useState({});
+  /* Menu button refs */
+  const saveAsMenuRef = React.useRef(null);
+  const editMenuRef = React.useRef(null);
+  const autoCommitMenuRef = React.useRef(null);
+  const explainMenuRef = React.useRef(null);
+  const macrosMenuRef = React.useRef(null);
+  const filterMenuRef = React.useRef(null);
+
+  const queryToolPref = queryToolCtx.preferences.sqleditor;
+  const setDisableButton = useCallback((name, disable=true)=>{
+    setButtonsDisabled((prev)=>({...prev, [name]: disable}));
+  }, []);
+
+  const executeQuery = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+  }, []);
+  const cancelQuery = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_STOP_EXECUTION);
+  }, []);
+
+  const explain = useCallback((analyze=false)=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, {
+      format: 'json',
+      analyze: analyze,
+      verbose: Boolean(checkedMenuItems['explain_verbose']),
+      costs: Boolean(checkedMenuItems['explain_costs']),
+      buffers: analyze ? Boolean(checkedMenuItems['explain_buffers']) : false,
+      timing: analyze ? Boolean(checkedMenuItems['explain_timing']) : false,
+      summary: Boolean(checkedMenuItems['explain_summary']),
+      settings: Boolean(checkedMenuItems['explain_settings']),
+    });
+  }, [checkedMenuItems]);
+
+  const explainAnalyse = useCallback(()=>{
+    explain(true);
+  }, [explain]);
+
+  const openMenu = useCallback((e)=>{
+    setMenuOpenId(e.currentTarget.name);
+  }, []);
+
+  const handleMenuClose = useCallback(()=>{
+    setMenuOpenId(null);
+  }, []);
+
+  const checkMenuClick = useCallback((e)=>{
+    setCheckedMenuItems((prev)=>{
+      let newVal = !prev[e.value];
+      if(e.value === 'auto_commit' || e.value === 'auto_rollback') {
+        autoCommitRollback(e.value, queryToolCtx.api, queryToolCtx.params.trans_id, newVal)
+          .catch ((error)=>{
+            newVal = prev[e.value];
+            eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error, {
+              checkTransaction: true,
+            });
+          });
+      }
+      return {
+        ...prev,
+        [e.value]: newVal,
+      };
+    });
+  }, []);
+
+  const openFile = useCallback(()=>{
+    confirmDiscard(()=>{
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_LOAD_FILE);
+    });
+  }, []);
+
+  const saveFile = useCallback((saveAs=false)=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_FILE, saveAs);
+  }, []);
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_START, ()=>{
+      setDisableButton('execute', true);
+      setDisableButton('cancel', false);
+      setDisableButton('explain', true);
+      setDisableButton('explain_analyse', true);
+      setDisableButton('limit', true);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_END, ()=>{
+      setDisableButton('execute', false);
+      setDisableButton('cancel', true);
+      setDisableButton('explain', false);
+      setDisableButton('explain_analyse', false);
+      setDisableButton('limit', false);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS, ()=>{
+      setDisableButton('execute', true);
+      setDisableButton('explain', true);
+      setDisableButton('explain_analyse', true);
+      setDisableButton('limit', true);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS_END, ()=>{
+      setDisableButton('execute', false);
+      setDisableButton('explain', false);
+      setDisableButton('explain_analyse', false);
+      setDisableButton('limit', false);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.QUERY_CHANGED, (isDirty)=>{
+      setDisableButton('save', !isDirty);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (isDirty)=>{
+      setDisableButton('save-data', !isDirty);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows)=>{
+      setDisableButton('delete-rows', !rows);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SET_FILTER_INFO, (canFilter, filterApplied)=>{
+      setDisableButton('filter', !canFilter);
+      setHighlightFilter(filterApplied);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SET_LIMIT_VALUE, (l)=>{
+      setLimit(l);
+    });
+  }, []);
+
+  useEffect(()=>{
+    setDisableButton('execute', queryToolConnCtx.obtainingConn);
+    setDisableButton('explain', queryToolConnCtx.obtainingConn);
+    setDisableButton('explain_analyse', queryToolConnCtx.obtainingConn);
+  }, [queryToolConnCtx.obtainingConn]);
+
+  const isInTxn = ()=>(queryToolConnCtx.connectionStatus == CONNECTION_STATUS.TRANSACTION_STATUS_INTRANS
+    || queryToolConnCtx.connectionStatus == CONNECTION_STATUS.TRANSACTION_STATUS_INERROR);
+
+  const onExecutionDone = ()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_END, (success)=>{
+      if(success) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL);
+      }
+    }, true);
+  };
+  const warnTxnClose = ()=>{
+    if(!isInTxn() || !queryToolCtx.preferences?.sqleditor.prompt_commit_transaction) {
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.FORCE_CLOSE_PANEL);
+      return;
+    }
+    queryToolCtx.modal.showModal(gettext('Commit transaction?'), (closeModal)=>(
+      <ConfirmTransactionContent
+        closeModal={closeModal}
+        text={gettext('The current transaction is not commited to the database. '
+          +'Do you want to commit or rollback the transaction?')}
+        onRollback={()=>{
+          onExecutionDone();
+          onRollbackClick();
+        }}
+        onCommit={()=>{
+          onExecutionDone();
+          onCommitClick();
+        }}
+      />
+    ));
+  };
+  useEffect(()=>{
+    if(isInTxn()) {
+      setDisableButton('commit', false);
+      setDisableButton('rollback', false);
+    } else {
+      setDisableButton('commit', true);
+      setDisableButton('rollback', true);
+    }
+    eventBus.registerListener(QUERY_TOOL_EVENTS.WARN_TXN_CLOSE, warnTxnClose);
+    return ()=>{
+      eventBus.deregisterListener(QUERY_TOOL_EVENTS.WARN_TXN_CLOSE, warnTxnClose);
+    };
+  }, [queryToolConnCtx.connectionStatus]);
+
+  const onCommitClick=()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, 'COMMIT;', null, true);
+  };
+  const onRollbackClick=()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, 'ROLLBACK;', null, true);
+  };
+  const executeMacro = (m)=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, m.sql, null, true);
+  };
+  const onLimitChange=(e)=>{
+    setLimit(e.target.value);
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SET_LIMIT,e.target.value);
+  };
+  const formatSQL=()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL);
+  };
+  const clearQuery=()=>{
+    confirmDiscard(()=>{
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, '');
+    });
+  };
+  const onHelpClick=()=>{
+    let url = url_for('help.static', {'filename': 'query_tool.html'});
+    window.open(url, 'pgadmin_help');
+  };
+  const confirmDiscard=(callback)=>{
+    queryToolCtx.modal.confirm(
+      gettext('Unsaved changes'),
+      gettext('Are you sure you wish to discard the current changes?'),
+      function() {
+        callback();
+      },
+      function() {
+        return true;
+      }
+    );
+  };
+
+  useEffect(()=>{
+    if(queryToolPref) {
+      /* Get the prefs first time */
+      if(_.isUndefined(checkedMenuItems.auto_commit)) {
+        setCheckedMenuItems({
+          auto_commit: queryToolPref.auto_commit,
+          auto_rollback: queryToolPref.auto_rollback,
+          explain_verbose: queryToolPref.explain_verbose,
+          explain_costs: queryToolPref.explain_costs,
+          explain_buffers: queryToolPref.explain_buffers,
+          explain_timing: queryToolPref.explain_timing,
+          explain_summary: queryToolPref.explain_summary,
+          explain_settings: queryToolPref.explain_settings,
+        });
+      }
+    }
+  }, [queryToolPref]);
+
+  /* Button shortcuts */
+  useKeyboardShortcuts([
+    {
+      shortcut: queryToolPref.execute_query,
+      options: {
+        callback: ()=>{executeQuery();}
+      }
+    },
+    {
+      shortcut: queryToolPref.explain_query,
+      options: {
+        callback: (e)=>{e.preventDefault();explain();}
+      }
+    },
+    {
+      shortcut: queryToolPref.explain_analyze_query,
+      options: {
+        callback: ()=>{explainAnalyse();}
+      }
+    },
+    {
+      shortcut: queryToolPref.commit_transaction,
+      options: {
+        callback: ()=>{onCommitClick();}
+      }
+    },
+    {
+      shortcut: queryToolPref.rollback_transaction,
+      options: {
+        callback: ()=>{onRollbackClick();}
+      }
+    },
+    {
+      shortcut: FIXED_PREF.format_sql,
+      options: {
+        callback: ()=>{formatSQL();}
+      }
+    },
+    {
+      shortcut: queryToolPref.clear_query,
+      options: {
+        callback: ()=>{clearQuery();}
+      }
+    },
+  ], containerRef);
+
+  /* Macro shortcuts */
+  useKeyboardShortcuts(
+    queryToolCtx.params?.macros?.map((m)=>{
+      return {
+        shortcut: {
+          ...m,
+          'key': {
+            'key_code': m.key_code,
+            'char': m.key,
+          },
+        },
+        options: {
+          callback: ()=>{executeMacro(m);}
+        }
+      };
+    }) || [],
+    containerRef
+  );
+
+  return (
+    <>
+      <Box className={classes.root}>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Open File')} icon={<FolderRoundedIcon />} disabled={!queryToolCtx.params.is_query_tool}
+            accesskey={shortcut_key(queryToolPref.btn_open_file)} onClick={openFile} />
+          <PgIconButton title={gettext('Save File')} icon={<SaveRoundedIcon />}
+            accesskey={shortcut_key(queryToolPref.btn_save_file)} disabled={buttonsDisabled['save'] || !queryToolCtx.params.is_query_tool}
+            onClick={()=>{saveFile(false);}} />
+          <PgIconButton title={gettext('File')} icon={<KeyboardArrowDownIcon />} splitButton disabled={!queryToolCtx.params.is_query_tool}
+            name="menu-saveas" ref={saveAsMenuRef} onClick={openMenu}
+          />
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Edit')} icon={
+            <><EditRoundedIcon /><KeyboardArrowDownIcon style={{marginLeft: '-10px'}} /></>}
+          disabled={!queryToolCtx.params.is_query_tool}
+          name="menu-edit" ref={editMenuRef} onClick={openMenu} />
+        </PgButtonGroup>
+        <PgButtonGroup size="small" color={highlightFilter ? 'primary' : 'default'}>
+          <PgIconButton title={gettext('Sort/Filter')} icon={<FilterIcon />}
+            onClick={onFilterClick} disabled={buttonsDisabled['filter']} accesskey={shortcut_key(queryToolPref.btn_filter_dialog)}/>
+          <PgIconButton title={gettext('Filter options')} icon={<KeyboardArrowDownIcon />} splitButton
+            disabled={buttonsDisabled['filter']} name="menu-filter" ref={filterMenuRef} accesskey={shortcut_key(queryToolPref.btn_filter_options)}
+            onClick={openMenu} />
+        </PgButtonGroup>
+        <InputSelectNonSearch options={[
+          {label: gettext('No limit'), value: '-1'},
+          {label: gettext('1000 rows'), value: '1000'},
+          {label: gettext('500 rows'), value: '500'},
+          {label: gettext('100 rows'), value: '100'},
+        ]} value={limit} onChange={onLimitChange} disabled={buttonsDisabled['limit'] || queryToolCtx.params.is_query_tool} />
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Cancel query')} icon={<StopRoundedIcon style={{height: 'unset'}} />}
+            onClick={cancelQuery} disabled={buttonsDisabled['cancel']} accesskey={shortcut_key(queryToolPref.btn_cancel_query)} />
+          <PgIconButton title={gettext('Execute/Refresh')} icon={<PlayArrowRoundedIcon style={{height: 'unset'}} />}
+            onClick={executeQuery} disabled={buttonsDisabled['execute']} shortcut={queryToolPref.execute_query}/>
+          <PgIconButton title={gettext('Execute options')} icon={<KeyboardArrowDownIcon />} splitButton
+            name="menu-autocommit" ref={autoCommitMenuRef} accesskey={shortcut_key(queryToolPref.btn_delete_row)}
+            onClick={openMenu} />
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Explain')} icon={<ExplicitRoundedIcon />}
+            onClick={()=>{explain();}} disabled={buttonsDisabled['explain'] || !queryToolCtx.params.is_query_tool} shortcut={queryToolPref.explain_query}/>
+          <PgIconButton title={gettext('Explain Analyze')} icon={<AssessmentRoundedIcon />}
+            onClick={()=>{explainAnalyse();}} disabled={buttonsDisabled['explain_analyse'] || !queryToolCtx.params.is_query_tool} shortcut={queryToolPref.explain_analyze_query}/>
+          <PgIconButton title={gettext('Explain Settings')} icon={<KeyboardArrowDownIcon />} splitButton
+            disabled={!queryToolCtx.params.is_query_tool}
+            name="menu-explain" ref={explainMenuRef} onClick={openMenu} />
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Commit')} icon={<CommitIcon />}
+            onClick={onCommitClick} disabled={buttonsDisabled['commit']} shortcut={queryToolPref.commit_transaction}/>
+          <PgIconButton title={gettext('Rollback')} icon={<RollbackIcon />}
+            onClick={onRollbackClick} disabled={buttonsDisabled['rollback']} shortcut={queryToolPref.rollback_transaction}/>
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Macros')} icon={
+            <><FormatListNumberedRoundedIcon /><KeyboardArrowDownIcon style={{marginLeft: '-10px'}} /></>}
+          disabled={!queryToolCtx.params.is_query_tool} name="menu-macros" ref={macrosMenuRef} onClick={openMenu} />
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Help')} icon={<HelpIcon />} onClick={onHelpClick} />
+        </PgButtonGroup>
+      </Box>
+      <PgMenu
+        anchorRef={saveAsMenuRef}
+        open={menuOpenId=='menu-saveas'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem onClick={()=>{saveFile(true);}}>{gettext('Save as')}</PgMenuItem>
+      </PgMenu>
+      <PgMenu
+        anchorRef={editMenuRef}
+        open={menuOpenId=='menu-edit'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem shortcut={FIXED_PREF.find}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, false);}}>{gettext('Find')}</PgMenuItem>
+        <PgMenuItem shortcut={FIXED_PREF.replace}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, true);}}>{gettext('Replace')}</PgMenuItem>
+        <PgMenuItem shortcut={FIXED_PREF.jump}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'jumpToLine');}}>{gettext('Jump')}</PgMenuItem>
+        <PgMenuDivider />
+        <PgMenuItem shortcut={FIXED_PREF.indent}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'indentMore');}}>{gettext('Indent')}</PgMenuItem>
+        <PgMenuItem shortcut={FIXED_PREF.unindent}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'indentLess');}}>{gettext('Unindent')}</PgMenuItem>
+        <PgMenuItem shortcut={FIXED_PREF.comment}
+          onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, 'toggleComment');}}>{gettext('Toggle comment')}</PgMenuItem>
+        <PgMenuItem shortcut={queryToolPref.clear_query}
+          onClick={clearQuery}>{gettext('Clear query')}</PgMenuItem>
+        <PgMenuDivider />
+        <PgMenuItem shortcut={FIXED_PREF.format_sql}onClick={formatSQL}>{gettext('Format SQL')}</PgMenuItem>
+      </PgMenu>
+      <PgMenu
+        anchorRef={filterMenuRef}
+        open={menuOpenId=='menu-filter'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, true);}}>{gettext('Filter by Selection')}</PgMenuItem>
+        <PgMenuItem onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, false);}}>{gettext('Exclude by Selection')}</PgMenuItem>
+        <PgMenuItem onClick={()=>{eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_REMOVE_FILTER);}}>{gettext('Remove Sort/Filter')}</PgMenuItem>
+      </PgMenu>
+      <PgMenu
+        anchorRef={autoCommitMenuRef}
+        open={menuOpenId=='menu-autocommit'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem hasCheck value="auto_commit" checked={checkedMenuItems['auto_commit']}
+          onClick={checkMenuClick}>{gettext('Auto commit?')}</PgMenuItem>
+        <PgMenuItem hasCheck value="auto_rollback" checked={checkedMenuItems['auto_rollback']}
+          onClick={checkMenuClick}>{gettext('Auto rollback on error?')}</PgMenuItem>
+      </PgMenu>
+      <PgMenu
+        anchorRef={explainMenuRef}
+        open={menuOpenId=='menu-explain'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem hasCheck value="explain_verbose" checked={checkedMenuItems['explain_verbose']}
+          onClick={checkMenuClick}>{gettext('Verbose')}</PgMenuItem>
+        <PgMenuItem hasCheck value="explain_costs" checked={checkedMenuItems['explain_costs']}
+          onClick={checkMenuClick}>{gettext('Costs')}</PgMenuItem>
+        <PgMenuItem hasCheck value="explain_buffers" checked={checkedMenuItems['explain_buffers']}
+          onClick={checkMenuClick}>{gettext('Buffers')}</PgMenuItem>
+        <PgMenuItem hasCheck value="explain_timing" checked={checkedMenuItems['explain_timing']}
+          onClick={checkMenuClick}>{gettext('Timing')}</PgMenuItem>
+        <PgMenuItem hasCheck value="explain_summary" checked={checkedMenuItems['explain_summary']}
+          onClick={checkMenuClick}>{gettext('Summary')}</PgMenuItem>
+        <PgMenuItem hasCheck value="explain_settings" checked={checkedMenuItems['explain_settings']}
+          onClick={checkMenuClick}>{gettext('Settings')}</PgMenuItem>
+      </PgMenu>
+      <PgMenu
+        anchorRef={macrosMenuRef}
+        open={menuOpenId=='menu-macros'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem onClick={onManageMacros}>{gettext('Manage macros')}</PgMenuItem>
+        <PgMenuDivider />
+        {queryToolCtx.params?.macros?.map((m, i)=>{
+          return (
+            <PgMenuItem shortcut={{
+              ...m,
+              'key': {
+                'key_code': m.key_code,
+                'char': m.key,
+              },
+            }} onClick={()=>executeMacro(m)} key={i}>
+              {m.name}
+            </PgMenuItem>
+          );
+        })}
+      </PgMenu>
+    </>
+  );
+}
+
+MainToolBar.propTypes = {
+  containerRef: CustomPropTypes.ref,
+  onFilterClick: PropTypes.func,
+  onManageMacros: PropTypes.func,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/Messages.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/Messages.jsx
new file mode 100644
index 000000000..ea3d3b657
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Messages.jsx
@@ -0,0 +1,46 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/styles';
+import React from 'react';
+import { QueryToolEventsContext } from '../QueryToolComponent';
+import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    whiteSpace: 'pre-wrap',
+    fontFamily: '"Source Code Pro", SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
+    padding: '5px 10px',
+    overflow: 'auto',
+    height: '100%',
+    fontSize: '12px',
+    userSelect: 'text',
+    backgroundColor: theme.palette.background.default,
+    color: theme.palette.text.primary,
+    ...theme.mixins.fontSourceCode,
+  }
+}));
+
+export function Messages() {
+  const classes = useStyles();
+  const [messageText, setMessageText] = React.useState('');
+  const eventBus = React.useContext(QueryToolEventsContext);
+  React.useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SET_MESSAGE, (text, append=false)=>{
+      setMessageText((prev)=>{
+        if(append) {
+          return prev+text;
+        }
+        return text;
+      });
+    });
+  }, []);
+  return (
+    <div className={classes.root} tabIndex="0">{messageText}</div>
+  );
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/Notifications.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/Notifications.jsx
new file mode 100644
index 000000000..ea6a1607d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Notifications.jsx
@@ -0,0 +1,58 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React from 'react';
+import { commonTableStyles } from '../../../../../../static/js/Theme';
+import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
+import gettext from 'sources/gettext';
+import _ from 'lodash';
+import clsx from 'clsx';
+import { QueryToolEventsContext } from '../QueryToolComponent';
+
+export function Notifications() {
+  const [notices, setNotices] = React.useState([]);
+  const tableClasses = commonTableStyles();
+  const eventBus = React.useContext(QueryToolEventsContext);
+  React.useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.PUSH_NOTICE, (notice)=>{
+      if(_.isArray(notice)) {
+        setNotices((prev)=>[
+          ...prev,
+          ...notice,
+        ]);
+      } else {
+        setNotices((prev)=>[
+          ...prev,
+          notice,
+        ]);
+      }
+    });
+  }, []);
+
+
+  return <table className={clsx(tableClasses.table, tableClasses.borderBottom)}>
+    <thead>
+      <tr>
+        <th>{gettext('Recorded time')}</th>
+        <th>{gettext('Event')}</th>
+        <th>{gettext('Process ID')}</th>
+        <th>{gettext('Payload')}</th>
+      </tr>
+    </thead>
+    <tbody>
+      {notices.map((notice, i)=>{
+        return <tr key={i}>
+          <td>{notice.recorded_time}</td>
+          <td>{notice.channel}</td>
+          <td>{notice.pid}</td>
+          <td>{notice.payload}</td>
+        </tr>;
+      })}
+    </tbody>
+  </table>;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
new file mode 100644
index 000000000..e37f91a8a
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/Query.jsx
@@ -0,0 +1,401 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/styles';
+import React, {useContext, useCallback, useEffect } from 'react';
+import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
+import CodeMirror from '../../../../../../static/js/components/CodeMirror';
+import {PANELS, QUERY_TOOL_EVENTS} from '../QueryToolConstants';
+import url_for from 'sources/url_for';
+import { LayoutEventsContext, LAYOUT_EVENTS } from '../../../../../../static/js/helpers/Layout';
+import ConfirmSaveContent from '../dialogs/ConfirmSaveContent';
+import gettext from 'sources/gettext';
+import OrigCodeMirror from 'bundled_codemirror';
+import Notifier from '../../../../../../static/js/helpers/Notifier';
+import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
+
+const useStyles = makeStyles(()=>({
+  sql: {
+    height: '100%',
+  }
+}));
+
+function registerAutocomplete(api, transId, onFailure) {
+  OrigCodeMirror.registerHelper('hint', 'sql', function (editor) {
+    var data = [],
+      doc = editor.getDoc(),
+      cur = doc.getCursor(),
+      // Get the current cursor position
+      current_cur = cur.ch,
+      // function context
+      ctx = {
+        editor: editor,
+        // URL for auto-complete
+        url: url_for('sqleditor.autocomplete', {
+          'trans_id': transId,
+        }),
+        data: data,
+        // Get the line number in the cursor position
+        current_line: cur.line,
+        /*
+         * Render function for hint to add our own class
+         * and icon as per the object type.
+         */
+        hint_render: function (elt, data_arg, cur_arg) {
+          var el = document.createElement('span');
+
+          switch (cur_arg.type) {
+          case 'database':
+            el.className = 'sqleditor-hint pg-icon-' + cur_arg.type;
+            break;
+          case 'datatype':
+            el.className = 'sqleditor-hint icon-type';
+            break;
+          case 'keyword':
+            el.className = 'fa fa-key';
+            break;
+          case 'table alias':
+            el.className = 'fa fa-at';
+            break;
+          default:
+            el.className = 'sqleditor-hint icon-' + cur_arg.type;
+          }
+
+          el.appendChild(document.createTextNode(cur_arg.text));
+          elt.appendChild(el);
+        },
+      };
+
+    data.push(doc.getValue());
+    // Get the text from start to the current cursor position.
+    data.push(
+      doc.getRange({
+        line: 0,
+        ch: 0,
+      }, {
+        line: ctx.current_line,
+        ch: current_cur,
+      })
+    );
+
+    return {
+      then: function (cb) {
+        var self_local = this;
+        // Make ajax call to find the autocomplete data
+        api.post(self_local.url, JSON.stringify(self_local.data))
+          .then((res) => {
+            var result = [];
+
+            _.each(res.data.data.result, function (obj, key) {
+              result.push({
+                text: key,
+                type: obj.object_type,
+                render: self_local.hint_render,
+              });
+            });
+
+            // Sort function to sort the suggestion's alphabetically.
+            result.sort(function (a, b) {
+              var textA = a.text.toLowerCase(),
+                textB = b.text.toLowerCase();
+              if (textA < textB) //sort string ascending
+                return -1;
+              if (textA > textB)
+                return 1;
+              return 0; //default return value (no sorting)
+            });
+
+            /*
+           * Below logic find the start and end point
+           * to replace the selected auto complete suggestion.
+           */
+            var token = self_local.editor.getTokenAt(cur),
+              start, end, search;
+            if (token.end > cur.ch) {
+              token.end = cur.ch;
+              token.string = token.string.slice(0, cur.ch - token.start);
+            }
+
+            if (token.string.match(/^[.`\w@]\w*$/)) {
+              search = token.string;
+              start = token.start;
+              end = token.end;
+            } else {
+              start = end = cur.ch;
+              search = '';
+            }
+
+            /*
+           * Added 1 in the start position if search string
+           * started with "." or "`" else auto complete of code mirror
+           * will remove the "." when user select any suggestion.
+           */
+            if (search.charAt(0) == '.' || search.charAt(0) == '``')
+              start += 1;
+
+            cb({
+              list: result,
+              from: {
+                line: self_local.current_line,
+                ch: start,
+              },
+              to: {
+                line: self_local.current_line,
+                ch: end,
+              },
+            });
+          })
+          .catch((err) => {
+            onFailure?.(err);
+          });
+      }.bind(ctx),
+    };
+  });
+}
+
+export default function Query() {
+  const classes = useStyles();
+  const editor = React.useRef();
+  const eventBus = useContext(QueryToolEventsContext);
+  const queryToolCtx = useContext(QueryToolContext);
+  const layoutEvenBus = useContext(LayoutEventsContext);
+  const lastSavedText = React.useRef('');
+  const markedLine = React.useRef(0);
+
+  const removeHighlightError = (cmObj)=>{
+    // Remove already existing marker
+    cmObj.removeLineClass(markedLine.current, 'wrap', 'CodeMirror-activeline-background');
+    markedLine.current = 0;
+  };
+  const highlightError = (cmObj, result)=>{
+    let errorLineNo = 0,
+      startMarker = 0,
+      endMarker = 0,
+      selectedLineNo = 0;
+
+    removeHighlightError(cmObj);
+
+    // In case of selection we need to find the actual line no
+    if (cmObj.getSelection().length > 0)
+      selectedLineNo = cmObj.getCursor(true).line;
+
+    // Fetch the LINE string using regex from the result
+    var line = /LINE (\d+)/.exec(result),
+      // Fetch the Character string using regex from the result
+      char = /Character: (\d+)/.exec(result);
+
+    // If line and character is null then no need to mark
+    if (line != null && char != null) {
+      errorLineNo = (parseInt(line[1]) - 1) + selectedLineNo;
+      var errorCharNo = (parseInt(char[1]) - 1);
+
+      /* We need to loop through each line till the error line and
+        * count the total no of character to figure out the actual
+        * starting/ending marker point for the individual line. We
+        * have also added 1 per line for the "\n" character.
+        */
+      var prevLineChars = 0;
+      for (let i = selectedLineNo > 0 ? selectedLineNo : 0; i < errorLineNo; i++)
+        prevLineChars += cmObj.getLine(i).length + 1;
+
+      /* Marker starting point for the individual line is
+        * equal to error character index minus total no of
+        * character till the error line starts.
+        */
+      startMarker = errorCharNo - prevLineChars;
+
+      // Find the next space from the character or end of line
+      var errorLine = cmObj.getLine(errorLineNo);
+
+      if (_.isUndefined(errorLine)) return;
+      endMarker = errorLine.indexOf(' ', startMarker);
+      if (endMarker < 0)
+        endMarker = errorLine.length;
+
+      // Mark the error text
+      cmObj.markText({
+        line: errorLineNo,
+        ch: startMarker,
+      }, {
+        line: errorLineNo,
+        ch: endMarker,
+      }, {
+        className: 'sql-editor-mark',
+      });
+
+      markedLine.current = errorLineNo;
+      cmObj.addLineClass(errorLineNo, 'wrap', 'CodeMirror-activeline-background');
+      cmObj.focus();
+      cmObj.setCursor(errorLineNo, 0);
+    }
+  };
+
+  const triggerExecution = (explainObject)=>{
+    if(queryToolCtx.params.is_query_tool) {
+      let query = editor.current?.getSelection() || editor.current?.getValue() || '';
+      if(query) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject);
+      }
+    } else {
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, null, null);
+    }
+  };
+
+  useEffect(()=>{
+    layoutEvenBus.registerListener(LAYOUT_EVENTS.ACTIVE, (currentTabId)=>{
+      currentTabId == PANELS.QUERY && editor.current.focus();
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION, triggerExecution);
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, (result)=>{
+      if(result) {
+        highlightError(editor.current, result);
+      } else {
+        removeHighlightError(editor.current);
+      }
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.LOAD_FILE, (fileName)=>{
+      queryToolCtx.api.post(url_for('sqleditor.load_file'), {
+        'file_name': decodeURI(fileName),
+      }).then((res)=>{
+        editor.current.setValue(res.data);
+        lastSavedText.current = res.data;
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, fileName, true);
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, isDirty());
+      }).catch((err)=>{
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.LOAD_FILE_DONE, null, false);
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+      });
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SAVE_FILE, (fileName)=>{
+      let editorValue = editor.current.getValue();
+      queryToolCtx.api.post(url_for('sqleditor.save_file'), {
+        'file_name': decodeURI(fileName),
+        'file_content': editor.current.getValue(),
+      }).then(()=>{
+        lastSavedText.current = editorValue;
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE_DONE, fileName, true);
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, isDirty());
+        Notifier.success(gettext('File saved successfully.'));
+      }).catch((err)=>{
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_FILE_DONE, null, false);
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+      });
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_EXEC_CMD, (cmd='')=>{
+      editor.current?.execCommand(cmd);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.COPY_TO_EDITOR, (text)=>{
+      editor.current?.setValue(text);
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.QUERY);
+      setTimeout(()=>{
+        editor.current?.focus();
+        editor.current?.setCursor(editor.current.lineCount(), 0);
+      }, 250);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_FIND_REPLACE, (replace=false)=>{
+      editor.current?.focus();
+      let key = {
+        keyCode: 70, metaKey: false, ctrlKey: true, shiftKey: replace, altKey: false,
+      };
+      if(isMac()) {
+        key.metaKey = true;
+        key.ctrlKey = false;
+        key.shiftKey = false;
+        key.altKey = true;
+      }
+      editor.current?.triggerOnKeyDown(
+        new KeyboardEvent('keydown', key)
+      );
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, (value, focus=true)=>{
+      focus && editor.current?.focus();
+      editor.current?.setValue(value);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_QUERY_CHANGE, ()=>{
+      change();
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE, ()=>{
+      if(!isDirty() || !queryToolCtx.preferences?.sqleditor.prompt_save_query_changes) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_TXN_CLOSE);
+        return;
+      }
+      queryToolCtx.modal.showModal(gettext('Save query changes?'), (closeModal)=>(
+        <ConfirmSaveContent
+          closeModal={closeModal}
+          text={gettext('The query text has changed. Do you want to save changes?')}
+          onDontSave={()=>{
+            eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_TXN_CLOSE);
+          }}
+          onSave={()=>{
+            eventBus.registerListener(QUERY_TOOL_EVENTS.SAVE_FILE_DONE, (_f, success)=>{
+              if(success) {
+                eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_TXN_CLOSE);
+              }
+            }, true);
+            eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_FILE);
+          }}
+        />
+      ));
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_FORMAT_SQL, ()=>{
+      let selection = true, sql = editor.current?.getSelection();
+      if(sql == '') {
+        sql = editor.current.getValue();
+        selection = false;
+      }
+      queryToolCtx.api.post(url_for('sql.format'), {
+        'sql': sql,
+      }).then((res)=>{
+        if(selection) {
+          editor.current.replaceSelection(res.data.data.sql, 'around');
+        } else {
+          editor.current.setValue(res.data.data.sql);
+        }
+      }).catch(()=>{/* failure should be ignored */});
+    });
+
+    editor.current.focus();
+  }, []);
+
+  useEffect(()=>{
+    registerAutocomplete(queryToolCtx.api, queryToolCtx.params.trans_id, (err)=>{
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err);
+    });
+  }, [queryToolCtx.params.trans_id]);
+
+  const isDirty = ()=>lastSavedText.current !== editor.current.getValue();
+
+  const cursorActivity = useCallback((cmObj)=>{
+    const c = cmObj.getCursor();
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.CURSOR_ACTIVITY, [c.line+1, c.ch+1]);
+  }, []);
+
+  const change = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.QUERY_CHANGED, isDirty());
+  }, []);
+
+  return <CodeMirror
+    currEditor={(obj)=>{
+      editor.current=obj;
+    }}
+    value={''}
+    className={classes.sql}
+    events={{
+      'focus': cursorActivity,
+      'cursorActivity': cursorActivity,
+      'change': change,
+    }}
+    disabled={!queryToolCtx.params.is_query_tool}
+    autocomplete={true}
+  />;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx
new file mode 100644
index 000000000..cbbca3486
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/QueryHistory.jsx
@@ -0,0 +1,500 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import { makeStyles } from '@material-ui/styles';
+import React from 'react';
+import { PANELS, QUERY_TOOL_EVENTS } from '../QueryToolConstants';
+import gettext from 'sources/gettext';
+import _ from 'lodash';
+import clsx from 'clsx';
+import { Box, Grid, List, ListItem, ListSubheader } from '@material-ui/core';
+import url_for from 'sources/url_for';
+import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
+import moment from 'moment';
+import PlayArrowRoundedIcon from '@material-ui/icons/PlayArrowRounded';
+import AssessmentRoundedIcon from '@material-ui/icons/AssessmentRounded';
+import ExplicitRoundedIcon from '@material-ui/icons/ExplicitRounded';
+import { SaveDataIcon, CommitIcon, RollbackIcon } from '../../../../../../static/js/components/ExternalIcon';
+import { InputSwitch } from '../../../../../../static/js/components/FormComponents';
+import CodeMirror from '../../../../../../static/js/components/CodeMirror';
+import { DefaultButton } from '../../../../../../static/js/components/Buttons';
+import { useDelayedCaller } from '../../../../../../static/js/custom_hooks';
+import Notifier from '../../../../../../static/js/helpers/Notifier';
+import Loader from 'sources/components/Loader';
+import { LayoutEventsContext, LAYOUT_EVENTS } from '../../../../../../static/js/helpers/Layout';
+import PropTypes from 'prop-types';
+import { parseApiError } from '../../../../../../static/js/api_instance';
+import * as clipboard from '../../../../../../static/js/clipboard';
+
+const useStyles = makeStyles((theme)=>({
+  leftRoot: {
+    display: 'flex',
+    flexDirection: 'column',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+    ...theme.mixins.panelBorder.right,
+  },
+  listRoot: {
+    ...theme.mixins.panelBorder.top,
+  },
+  listSubheader: {
+    padding: '0.25rem',
+    lineHeight: 'unset',
+    color: theme.palette.text.muted,
+    backgroundColor: theme.palette.background.default,
+    ...theme.mixins.panelBorder.bottom,
+    ...theme.mixins.fontSourceCode,
+  },
+  removePadding: {
+    padding: 0,
+  },
+  fontSourceCode:{
+    ...theme.mixins.fontSourceCode,
+    userSelect: 'text',
+  },
+  itemError: {
+    backgroundColor: theme.palette.error.light,
+    '&.Mui-selected': {
+      backgroundColor: theme.palette.error.light,
+      '&:hover': {
+        backgroundColor: theme.palette.error.light,
+      }
+    }
+  },
+  detailsQuery: {
+    marginTop: '0.5rem',
+    ...theme.mixins.panelBorder.all,
+  },
+  copyBtn: {
+    borderRadius: 0,
+    paddingLeft: '8px',
+    paddingRight: '8px',
+    borderTop: 'none',
+    borderLeft: 'none',
+    borderColor: theme.otherVars.borderColor,
+    fontSize: '13px',
+  },
+  infoHeader: {
+    fontSize: '13px',
+    padding: '0.5rem',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+  },
+  removeBtnMargin: {
+    marginLeft: '0.25rem',
+  }
+}));
+
+export const QuerySources = {
+  EXECUTE: {
+    ICON_CSS_CLASS: 'fa fa-play',
+  },
+  EXPLAIN: {
+    ICON_CSS_CLASS: 'fa fa-hand-pointer',
+  },
+  EXPLAIN_ANALYZE: {
+    ICON_CSS_CLASS: 'fa fa-list-alt',
+  },
+  COMMIT: {
+    ICON_CSS_CLASS: 'pg-font-icon icon-commit',
+  },
+  ROLLBACK: {
+    ICON_CSS_CLASS: 'pg-font-icon icon-rollback',
+  },
+  SAVE_DATA: {
+    ICON_CSS_CLASS: 'pg-font-icon icon-save_data_changes',
+  },
+  VIEW_DATA: {
+    ICON_CSS_CLASS: 'pg-font-icon icon-view_data',
+  },
+};
+
+
+class QueryHistoryUtils {
+  constructor() {
+    this._entries = [];
+    this.showInternal = true;
+  }
+
+  dateAsGroupKey(date) {
+    return moment(date).format('YYYY MM DD');
+  }
+
+  getItemKey(entry) {
+    return this.dateAsGroupKey(entry.start_time) + this.formatEntryDate(entry.start_time) + (entry.subKey ?? '');
+  }
+
+  getDateFormatted(date) {
+    return date.toLocaleDateString();
+  }
+
+  formatEntryDate(date) {
+    return moment(date).format('HH:mm:ss');
+  }
+
+  isDaysBefore(date, before) {
+    return (
+      this.getDateFormatted(date) ===
+      this.getDateFormatted(moment().subtract(before, 'days').toDate())
+    );
+  }
+
+  getDatePrefix(date) {
+    let prefix = '';
+    if (this.isDaysBefore(date, 0)) {
+      prefix = 'Today - ';
+    } else if (this.isDaysBefore(date, 1)) {
+      prefix = 'Yesterday - ';
+    }
+    return prefix;
+  }
+
+  addEntry(entry) {
+    entry.groupKey = this.dateAsGroupKey(entry.start_time);
+    entry.itemKey = this.getItemKey(entry);
+    let existEntry = _.find(this._entries, (e)=>e.itemKey==entry.itemKey);
+    if(existEntry) {
+      entry.itemKey = this.getItemKey(entry) + _.uniqueId();
+    }
+    let insertIndex = _.sortedIndexBy(this._entries, entry, (e)=>e.itemKey);
+    this._entries = [
+      ...this._entries.slice(0, insertIndex),
+      entry,
+      ...this._entries.slice(insertIndex),
+    ];
+  }
+
+  getEntries() {
+    if(!this.showInternal) {
+      return this._entries.filter((e)=>!e.is_pgadmin_query);
+    }
+    return this._entries;
+  }
+
+  getEntry(itemKey) {
+    return _.find(this.getEntries(), (e)=>e.itemKey==itemKey);
+  }
+
+  getGroupHeader(entry) {
+    return this.getDatePrefix(entry.start_time)+this.getDateFormatted(entry.start_time);
+  }
+
+  getGroups() {
+    return _.sortedUniqBy(this.getEntries().map((e)=>[e.groupKey, this.getGroupHeader(e)]), (g)=>g[0]).reverse();
+  }
+
+  getGroupEntries(groupKey) {
+    return this.getEntries().filter((e)=>e.groupKey==groupKey).reverse();
+  }
+
+  getNextItemKey(currKey) {
+    let nextIndex = this.getEntries().length-1;
+    if(currKey) {
+      let currIndex = _.findIndex(this.getEntries(), (e)=>e.itemKey==currKey);
+      if(currIndex == 0) {
+        nextIndex = currIndex;
+      } else {
+        nextIndex = currIndex - 1;
+      }
+    }
+    return this.getEntries()[nextIndex]?.itemKey;
+  }
+
+  getPrevItemKey(currKey) {
+    let nextIndex = this.getEntries().length-1;
+    if(currKey) {
+      let currIndex = _.findIndex(this.getEntries(), (e)=>e.itemKey==currKey);
+      if(currIndex == this.getEntries().length-1) {
+        nextIndex = currIndex;
+      } else {
+        nextIndex = currIndex + 1;
+      }
+    }
+    return this.getEntries()[nextIndex]?.itemKey;
+  }
+
+  clear(itemKey) {
+    if(itemKey) {
+      let nextKey = this.getNextItemKey(itemKey);
+      let removeIdx = _.findIndex(this._entries, (e)=>e.itemKey==itemKey);
+      this._entries.splice(removeIdx, 1);
+      return nextKey;
+    } else {
+      this._entries = [];
+    }
+  }
+
+  size() {
+    return this._entries.length;
+  }
+}
+
+function QuerySourceIcon({source}) {
+  switch(JSON.stringify(source)) {
+  case JSON.stringify(QuerySources.EXECUTE):
+    return <PlayArrowRoundedIcon style={{marginLeft: '-4px'}}/>;
+  case JSON.stringify(QuerySources.EXPLAIN):
+    return <ExplicitRoundedIcon/>;
+  case JSON.stringify(QuerySources.EXPLAIN_ANALYZE):
+    return <AssessmentRoundedIcon/>;
+  case JSON.stringify(QuerySources.COMMIT):
+    return <CommitIcon style={{marginLeft: '-4px'}}/>;
+  case JSON.stringify(QuerySources.ROLLBACK):
+    return <RollbackIcon style={{marginLeft: '-4px'}}/>;
+  case JSON.stringify(QuerySources.SAVE_DATA):
+    return <SaveDataIcon style={{marginLeft: '-4px'}}/>;
+  case JSON.stringify(QuerySources.VIEW_DATA):
+    return <SaveDataIcon style={{marginLeft: '-4px'}}/>;
+  default:
+    return <></>;
+  }
+}
+QuerySourceIcon.propTypes = {
+  source: PropTypes.object,
+};
+
+function HistoryEntry({entry, formatEntryDate, itemKey, selectedItemKey, onClick}) {
+  const classes = useStyles();
+  return <ListItem tabIndex="0" ref={(ele)=>{
+    selectedItemKey==itemKey && ele && ele.scrollIntoView({
+      block: 'center',
+      behavior: 'smooth',
+    });
+  }} className={clsx(classes.fontSourceCode, entry.status ? '' : classes.itemError)} selected={selectedItemKey==itemKey} onClick={onClick}>
+    <Box whiteSpace="nowrap" textOverflow="ellipsis" overflow="hidden" >
+      <QuerySourceIcon source={entry.query_source}/>
+      {entry.query}
+    </Box>
+    <Box fontSize="12px">
+      {formatEntryDate(entry.start_time)}
+    </Box>
+  </ListItem>;
+}
+
+const EntryPropType = PropTypes.shape({
+  info: PropTypes.string,
+  status: PropTypes.bool,
+  start_time: PropTypes.objectOf(Date),
+  query: PropTypes.string,
+  row_affected: PropTypes.number,
+  total_time: PropTypes.string,
+  message: PropTypes.string,
+  query_source: PropTypes.object,
+  is_pgadmin_query: PropTypes.bool,
+});
+HistoryEntry.propTypes = {
+  entry: EntryPropType,
+  formatEntryDate: PropTypes.func,
+  itemKey: PropTypes.string,
+  selectedItemKey: PropTypes.string,
+  onClick: PropTypes.func,
+};
+
+function QueryHistoryDetails({entry}) {
+  const classes = useStyles();
+  const [copyText, setCopyText] = React.useState(gettext('Copy'));
+  const eventBus = React.useContext(QueryToolEventsContext);
+  const revertCopiedText = useDelayedCaller(()=>{
+    setCopyText(gettext('Copy'));
+  });
+
+  const onCopyClick = React.useCallback(()=>{
+    clipboard.copyToClipboard(entry.query);
+    setCopyText(gettext('Copied!'));
+    revertCopiedText(1500);
+  }, [entry]);
+
+  const onCopyToEditor = React.useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_TO_EDITOR, entry.query);
+  }, [entry]);
+
+  if(!entry) {
+    return <></>;
+  }
+
+  return (
+    <>
+      {entry.info && <Box className={classes.infoHeader}>{entry.info}</Box>}
+      <Box padding="0.5rem">
+        <Grid container>
+          <Grid item sm={4}>{entry.start_time.toLocaleDateString() + ' ' + entry.start_time.toLocaleTimeString()}</Grid>
+          <Grid item sm={4}>{entry?.row_affected > 0 && entry.row_affected}</Grid>
+          <Grid item sm={4}>{entry.total_time}</Grid>
+        </Grid>
+        <Grid container>
+          <Grid item sm={4}>{gettext('Date')}</Grid>
+          <Grid item sm={4}>{gettext('Rows affected')}</Grid>
+          <Grid item sm={4}>{gettext('Duration')}</Grid>
+        </Grid>
+        <Box className={classes.detailsQuery}>
+          <DefaultButton size="xs" className={classes.copyBtn} onClick={onCopyClick}>{copyText}</DefaultButton>
+          <DefaultButton size="xs" className={classes.copyBtn} onClick={onCopyToEditor}>{gettext('Copy to Query Editor')}</DefaultButton>
+          <CodeMirror
+            value={entry.query}
+            options={{
+              foldGutter: false,
+              lineNumbers: false,
+              gutters: [],
+              readOnly: true,
+            }}
+          />
+        </Box>
+        <Box marginTop="0.5rem">
+          <Box>{gettext('Messages')}</Box>
+          <Box className={classes.fontSourceCode} fontSize="13px" whiteSpace="pre-wrap">{entry.message}</Box>
+        </Box>
+      </Box>
+    </>
+  );
+}
+
+QueryHistoryDetails.propTypes = {
+  entry: EntryPropType,
+};
+
+export function QueryHistory() {
+  const qhu = React.useRef(new QueryHistoryUtils());
+  const queryToolCtx = React.useContext(QueryToolContext);
+  const classes = useStyles();
+  const eventBus = React.useContext(QueryToolEventsContext);
+  const [selectedItemKey, setSelectedItemKey] = React.useState(1);
+  const [showInternal, setShowInternal] = React.useState(true);
+  const [loaderText, setLoaderText] = React.useState('');
+  const [,refresh] = React.useState({});
+  const selectedEntry = qhu.current.getEntry(selectedItemKey);
+  const layoutEvenBus = React.useContext(LayoutEventsContext);
+  const listRef = React.useRef();
+
+  React.useEffect(async ()=>{
+    layoutEvenBus.registerListener(LAYOUT_EVENTS.ACTIVE, (currentTabId)=>{
+      currentTabId == PANELS.HISTORY && listRef.current.focus();
+    });
+
+    setLoaderText(gettext('Fetching history...'));
+    try {
+      let {data: respData} = await queryToolCtx.api.get(url_for('sqleditor.get_query_history', {
+        'trans_id': queryToolCtx.params.trans_id,
+      }));
+      respData.data.result.forEach((h)=>{
+        h = JSON.parse(h);
+        h.start_time_orig = h.start_time;
+        h.start_time = new Date(h.start_time);
+        qhu.current.addEntry(h);
+      });
+      setSelectedItemKey(qhu.current.getNextItemKey());
+    } catch (error) {
+      console.error(error);
+      Notifier.error(gettext('Failed to fetch query history.') + parseApiError(error));
+    }
+    setLoaderText('');
+
+    const pushHistory = (h)=>{
+      qhu.current.addEntry(h);
+      refresh({});
+    };
+
+    listRef.current.focus();
+    eventBus.registerListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
+  }, []);
+
+  const onRemove = async ()=>{
+    setLoaderText(gettext('Removing history entry...'));
+    try {
+      await queryToolCtx.api.delete(url_for('sqleditor.clear_query_history', {
+        'trans_id': queryToolCtx.params.trans_id,
+      }), {
+        data: {
+          query: selectedEntry.query,
+          start_time: selectedEntry.start_time,
+        }
+      });
+      setSelectedItemKey(qhu.current.clear(selectedItemKey));
+    } catch (error) {
+      console.error(error);
+      Notifier.error(gettext('Failed to remove query history.') + parseApiError(error));
+    }
+    setLoaderText('');
+  };
+
+  const onRemoveAll = React.useCallback(()=>{
+    queryToolCtx.modal.confirm(gettext('Clear history'),
+      gettext('Are you sure you wish to clear the history?') + '</br>' +
+      gettext('This will remove all of your query history from this and other sessions for this database.'),
+      async function() {
+        setLoaderText(gettext('Removing history...'));
+        try {
+          await queryToolCtx.api.delete(url_for('sqleditor.clear_query_history', {
+            'trans_id': queryToolCtx.params.trans_id,
+          }));
+          qhu.current.clear();
+          setSelectedItemKey(null);
+        } catch (error) {
+          console.error(error);
+          Notifier.error(gettext('Failed to remove query history.') + parseApiError(error));
+        }
+        setLoaderText('');
+      },
+      function() {
+        return true;
+      }
+    );
+
+  }, []);
+
+  const onKeyPressed = (e) => {
+    if (e.keyCode == '38') {
+      e.preventDefault();
+      setSelectedItemKey(qhu.current.getPrevItemKey(selectedItemKey));
+    } else if (e.keyCode == '40') {
+      e.preventDefault();
+      setSelectedItemKey(qhu.current.getNextItemKey(selectedItemKey));
+    }
+  };
+
+  return (
+    <>
+      <Loader message={loaderText} />
+      {React.useMemo(()=>(
+        <Box display="flex" height="100%">
+          <Box flexBasis="50%" maxWidth="50%" className={classes.leftRoot}>
+            <Box padding="0.25rem" display="flex">
+              {gettext('Show queries generated internally by pgAdmin?')}
+              <InputSwitch value={showInternal} onChange={(e)=>{
+                setShowInternal(e.target.checked);
+                qhu.current.showInternal = e.target.checked;
+                setSelectedItemKey(qhu.current.getNextItemKey());
+              }} />
+              <Box marginLeft="auto">
+                <DefaultButton size="small" disabled={!selectedItemKey} onClick={onRemove}>Remove</DefaultButton>
+                <DefaultButton size="small" disabled={!qhu.current?.getGroups()?.length}
+                  className={classes.removeBtnMargin} onClick={onRemoveAll}>Remove All</DefaultButton>
+              </Box>
+            </Box>
+            <Box flexGrow="1" overflow="auto" className={classes.listRoot}>
+              <List innerRef={listRef} className={classes.root} subheader={<li />} tabIndex="0" onKeyDown={onKeyPressed}>
+                {qhu.current.getGroups().map(([groupKey, groupHeader]) => (
+                  <ListItem key={`section-${groupKey}`} className={classes.removePadding}>
+                    <List className={classes.removePadding}>
+                      <ListSubheader className={classes.listSubheader}>{groupHeader}</ListSubheader>
+                      {qhu.current.getGroupEntries(groupKey).map((entry) => (
+                        <HistoryEntry key={entry.itemKey} entry={entry} formatEntryDate={qhu.current.formatEntryDate}
+                          itemKey={entry.itemKey} selectedItemKey={selectedItemKey} onClick={()=>{setSelectedItemKey(entry.itemKey);}}/>
+                      ))}
+                    </List>
+                  </ListItem>
+                ))}
+              </List>
+            </Box>
+          </Box>
+          <Box flexBasis="50%" maxWidth="50%" overflow="auto">
+            <QueryHistoryDetails entry={selectedEntry}/>
+          </Box>
+        </Box>
+      ), [selectedItemKey, showInternal, qhu.current.size()])}
+    </>
+  );
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
new file mode 100644
index 000000000..ee5b2245a
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSet.jsx
@@ -0,0 +1,1225 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import _ from 'lodash';
+import React, { useContext, useEffect, useRef, useState }  from 'react';
+import QueryToolDataGrid, { GRID_ROW_SELECT_KEY } from '../QueryToolDataGrid';
+import {CONNECTION_STATUS, PANELS, QUERY_TOOL_EVENTS} from '../QueryToolConstants';
+import url_for from 'sources/url_for';
+import getApiInstance, { parseApiError } from '../../../../../../static/js/api_instance';
+import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
+import gettext from 'sources/gettext';
+import Loader from 'sources/components/Loader';
+import { Box } from '@material-ui/core';
+import { ResultSetToolbar } from './ResultSetToolbar';
+import { LayoutHelper } from '../../../../../../static/js/helpers/Layout';
+import { GeometryViewer } from './GeometryViewer';
+import Explain from '../../../../../../static/js/Explain';
+import Notifier from '../../../../../../static/js/helpers/Notifier';
+import { QuerySources } from './QueryHistory';
+import { getBrowser } from '../../../../../../static/js/utils';
+import CopyData from '../QueryToolDataGrid/CopyData';
+import moment from 'moment';
+import ConfirmSaveContent from '../dialogs/ConfirmSaveContent';
+import { makeStyles } from '@material-ui/styles';
+import EmptyPanelMessage from '../../../../../../static/js/components/EmptyPanelMessage';
+
+export class ResultSetUtils {
+  constructor(api, transId, isQueryTool=true) {
+    this.api = api;
+    this.transId = transId;
+    this.startTime = new Date();
+    this.clientPK = null;
+    this.isQueryTool = isQueryTool;
+    this.clientPKLastIndex = 0;
+    this.historyQuerySource = null;
+  }
+
+  static generateURLReconnectionFlag(baseUrl, transId, shouldReconnect) {
+    let url = url_for(baseUrl, {
+      'trans_id': transId,
+    });
+
+    if (shouldReconnect) {
+      url += '?connect=1';
+    }
+    return url;
+  }
+
+  static prepareAnalyzeSql(sqlStatement, analyzeSql) {
+    return {
+      sql: sqlStatement,
+      explain_plan: analyzeSql,
+    };
+  }
+
+  static wasConnectionLostToPythonServer(httpResponse) {
+    return _.isUndefined(httpResponse) || _.isUndefined(httpResponse.data);
+  }
+
+  static wasDatabaseConnectionLost(httpMessage) {
+    return httpMessage.response.status === 503 &&
+      httpMessage.response.data.info !== undefined &&
+      httpMessage.response.data.info === 'CONNECTION_LOST';
+  }
+
+  static isCryptKeyMissing(httpMessage) {
+    return httpMessage.response.status === 503 &&
+      httpMessage.response.data.info !== undefined &&
+      httpMessage.response.data.info === 'CRYPTKEY_MISSING';
+  }
+
+  static isSqlCorrect(httpMessageData) {
+    return httpMessageData.data.status;
+  }
+
+  static extractErrorMessage(httpMessage) {
+    let msg = httpMessage.errormsg;
+    if (httpMessage.responseJSON !== undefined &&
+      httpMessage.responseJSON.errormsg !== undefined)
+      msg = httpMessage.responseJSON.errormsg;
+
+    return msg;
+  }
+
+  static isQueryFinished(httpMessage) {
+    return httpMessage.data.data.status === 'Success';
+  }
+
+  static isQueryStillRunning(httpMessage) {
+    return httpMessage.data.data.status === 'Busy';
+  }
+
+  static isQueryCancelled(httpMessage) {
+    return httpMessage.data.data.status === 'Cancel';
+  }
+
+  static isConnectionToServerLostWhilePolling(httpMessage) {
+    return httpMessage.data.data.status === 'NotConnected';
+  }
+
+  setStartTime(start) {
+    this.startTime = start;
+  }
+
+  setEndTime(end) {
+    this.endTime = end;
+  }
+
+  queryRunTime() {
+    let total_ms = moment(this.endTime).diff(this.startTime);
+    let result = '';
+    let secs, mins, hrs;
+
+    /* Extract seconds from millisecs */
+    secs = parseInt(total_ms/1000);
+    total_ms = total_ms%1000;
+
+    /* Extract mins from seconds */
+    mins = parseInt(secs/60);
+    secs = secs%60;
+
+    /* Extract hrs from mins */
+    hrs = parseInt(mins/60);
+    mins = mins%60;
+
+    result = (hrs>0 ? hrs + ' ' + gettext('hr') + ' ': '')
+            + (mins>0 ? mins + ' ' + gettext('min') + ' ': '')
+            + (hrs<=0 && secs>0 ? secs + ' ' + gettext('secs') + ' ': '')
+            + (hrs<=0 && mins<=0 ? total_ms + ' ' + gettext('msec') + ' ':'');
+    return result.trim();
+  }
+
+  setEventBus(eventBus) {
+    this.eventBus = eventBus;
+  }
+
+  setStartData(data) {
+    this.startData = data;
+  }
+
+  resetClientPKIndex() {
+    this.clientPKLastIndex = 0;
+  }
+
+  setClientPK(clientPK) {
+    this.clientPK = clientPK || GRID_ROW_SELECT_KEY;
+  }
+
+  hasResultsToDisplay(res) {
+    return res.colinfo != null;
+  }
+
+  isAtBottom({ currentTarget }) {
+    return currentTarget.scrollTop + 10 >= currentTarget.scrollHeight - currentTarget.clientHeight;
+  }
+
+  postExecutionApi(query, explainObject, isQueryTool=true, reconnect=false) {
+    if(isQueryTool) {
+      return this.api.post(
+        ResultSetUtils.generateURLReconnectionFlag('sqleditor.query_tool_start', this.transId, reconnect),
+        JSON.stringify({
+          sql: query,
+          explain_plan: explainObject,
+        })
+      );
+    } else {
+      return this.api.get(
+        ResultSetUtils.generateURLReconnectionFlag('sqleditor.view_data_start', this.transId, reconnect)
+      );
+    }
+  }
+
+  async startExecution(query, explainObject, onIncorrectSQL, flags={
+    isQueryTool: true, external: false, reconnect: false,
+  }) {
+    let startTime = new Date();
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, '');
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TASK_START, gettext('Waiting for the query to complete...'), startTime);
+    this.setStartTime(startTime);
+    this.query = query;
+    this.historyQuerySource = flags.isQueryTool ? QuerySources.EXECUTE : QuerySources.VIEW_DATA;
+    if(explainObject) {
+      if(explainObject.analyze) {
+        this.historyQuerySource = QuerySources.EXPLAIN_ANALYZE;
+      } else {
+        this.historyQuerySource = QuerySources.EXPLAIN;
+      }
+    } else if(query == 'COMMIT;') {
+      this.historyQuerySource = QuerySources.COMMIT;
+    } else if(query == 'ROLLBACK;') {
+      this.historyQuerySource = QuerySources.ROLLBACK;
+    }
+    try {
+      let {data: httpMessageData} = await this.postExecutionApi(query, explainObject, flags.isQueryTool, flags.reconnect);
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, httpMessageData.data.transaction_status);
+
+      if (ResultSetUtils.isSqlCorrect(httpMessageData)) {
+        this.setStartData(httpMessageData.data);
+        if(!flags.isQueryTool) {
+          this.query = httpMessageData.data.sql;
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_FILTER_INFO, httpMessageData.data.can_filter, httpMessageData.data.filter_applied);
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_LIMIT_VALUE, httpMessageData.data.limit);
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EDITOR_SET_SQL, httpMessageData.data.sql, false);
+        }
+        return true;
+      } else {
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, httpMessageData.data.result);
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
+        onIncorrectSQL();
+        this.resetClientPKIndex();
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_HISTORY, {
+          status: false,
+          start_time: this.startTime,
+          query: this.query,
+          row_affected: null,
+          total_time: null,
+          message: httpMessageData.data.result,
+          query_source: this.historyQuerySource,
+          is_pgadmin_query: false,
+        });
+        if(!flags.external) {
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, httpMessageData.data.result);
+        }
+      }
+      if(httpMessageData.data.notifies) {
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_NOTICE, httpMessageData.data.notifies);
+      }
+    } catch(e) {
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR,
+        e,
+        {
+          connectionLostCallback: ()=>{
+            this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_START, query, explainObject, flags.external, true);
+          },
+          checkTransaction: true,
+        }
+      );
+    }
+    return false;
+  }
+
+  poll() {
+    let delay = 1;
+    var seconds = parseInt((Date.now() - this.startTime.getTime()) / 1000);
+    // calculate & return fall back polling timeout
+    if (seconds >= 10 && seconds < 30) {
+      delay = 500;
+    } else if (seconds >= 30 && seconds < 60) {
+      delay = 1000;
+    } else if (seconds >= 60 && seconds < 90) {
+      delay = 2000;
+    } else if (seconds >= 90) {
+      delay = 5000;
+    } else {
+      delay = 1;
+    }
+
+    return new Promise((resolve)=>{
+      setTimeout(() => {
+        resolve(this.api.get(
+          url_for('sqleditor.poll', {
+            'trans_id': this.transId,
+          })
+        ));
+      }, delay);
+    });
+  }
+
+  handlePollError(error) {
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, CONNECTION_STATUS.TRANSACTION_STATUS_INERROR);
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_HISTORY, {
+      status: false,
+      start_time: this.startTime,
+      query: this.query,
+      row_affected: null,
+      total_time: this.queryRunTime(),
+      message: parseApiError(error, false),
+      query_source: this.historyQuerySource,
+      is_pgadmin_query: false,
+    });
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error);
+  }
+
+  async pollForResult(onResultsAvailable, onExplain, onPollError) {
+    try {
+      let httpMessage = await this.poll();
+      let msg = '';
+      if(httpMessage.data.data.notifies) {
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_NOTICE, httpMessage.data.data.notifies);
+      }
+      if (ResultSetUtils.isQueryFinished(httpMessage)) {
+        msg = this.queryFinished(httpMessage, onResultsAvailable, onExplain);
+      } else if (ResultSetUtils.isQueryStillRunning(httpMessage)) {
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, httpMessage.data.data.transaction_status);
+        if(httpMessage.data.data.result) {
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, httpMessage.data.data.result, true);
+        }
+        return Promise.resolve(this.pollForResult(onResultsAvailable, onExplain, onPollError));
+      } else if (ResultSetUtils.isConnectionToServerLostWhilePolling(httpMessage)) {
+        msg = httpMessage.data.data.result;
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, msg, true);
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+      } else if (ResultSetUtils.isQueryCancelled(httpMessage)) {
+        msg = httpMessage.data.data.result || 'Execution Cancelled!';
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, httpMessage.data.data.result || 'Execution Cancelled!', true);
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+      }
+      Notifier.success(msg);
+      if(!ResultSetUtils.isQueryStillRunning(httpMessage)) {
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_HISTORY, {
+          status: true,
+          start_time: this.startTime,
+          query: this.query,
+          row_affected: httpMessage.data.data?.rows_affected,
+          total_time: this.queryRunTime(),
+          message: msg,
+          query_source: this.historyQuerySource,
+          is_pgadmin_query: false,
+        });
+      }
+    } catch (error) {
+      onPollError();
+      this.handlePollError(error);
+    }
+  }
+
+  getMoreRows(all=false) {
+    let url = url_for('sqleditor.fetch', {
+      'trans_id': this.transId,
+    });
+    if(all) {
+      url = url_for('sqleditor.fetch_all', {
+        'trans_id': this.transId,
+        'fetch_all': 1,
+      });
+    }
+    return this.api.get(url);
+  }
+
+  stopExecution() {
+    return this.api.post(
+      url_for('sqleditor.cancel_transaction', {'trans_id': this.transId})
+    );
+  }
+
+  saveData(reqData) {
+    return this.api.post(
+      url_for('sqleditor.save', {
+        'trans_id': this.transId
+      }),
+      JSON.stringify(reqData)
+    );
+  }
+
+  async saveResultsToFile(fileName) {
+    try {
+      let {data: respData} = await this.api.post(
+        url_for('sqleditor.query_tool_download', {
+          'trans_id': this.transId,
+        }),
+        {filename: fileName}
+      );
+
+      if(!_.isUndefined(respData.data)) {
+        if(!respData.status) {
+          this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, respData.data.result);
+        }
+      } else {
+        let respBlob = new Blob([respData], {type : 'text/csv'}),
+          urlCreator = window.URL || window.webkitURL,
+          download_url = urlCreator.createObjectURL(respBlob),
+          link = document.createElement('a');
+
+        document.body.appendChild(link);
+
+        if (getBrowser() === 'IE' && window.navigator.msSaveBlob) {
+        // IE10+ : (has Blob, but not a[download] or URL)
+          window.navigator.msSaveBlob(respBlob, fileName);
+        } else {
+          link.setAttribute('href', download_url);
+          link.setAttribute('download', fileName);
+          link.click();
+        }
+        document.body.removeChild(link);
+      }
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS_END);
+    } catch (error) {
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS_END);
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error);
+    }
+  }
+
+  includeFilter(reqData) {
+    return this.api.post(
+      url_for('sqleditor.inclusive_filter', {
+        'trans_id': this.transId,
+      }),
+      JSON.stringify(reqData)
+    );
+  }
+
+  excludeFilter(reqData) {
+    return this.api.post(
+      url_for('sqleditor.exclusive_filter', {
+        'trans_id': this.transId,
+      }),
+      JSON.stringify(reqData)
+    );
+  }
+
+  removeFilter() {
+    return this.api.post(
+      url_for('sqleditor.remove_filter', {
+        'trans_id': this.transId,
+      })
+    );
+  }
+
+  setLimit(limit) {
+    return this.api.post(
+      url_for('sqleditor.set_limit', {
+        'trans_id': this.transId,
+      }), JSON.stringify(parseInt(limit))
+    );
+  }
+
+  getFinalColumn(c, isEditable, isPK, pgTypesOidMap) {
+    let columnTypeInternal = pgTypesOidMap[c.type_code] || 'unknown';
+    let columnType = (isPK ? '[PK] ' : '') + columnTypeInternal;
+    let cellType;
+
+    if (c.precision && c.precision >= 0 && c.precision != 65535) {
+      columnType += ' (' + c.precision;
+      columnType += c.scale && c.scale != 65535 ?
+        ',' + c.scale + ')' :
+        ')';
+    }
+    // Identify cell type of column.
+    switch (columnTypeInternal) {
+    case 'oid':
+      cellType = 'oid';
+      break;
+    case 'json':
+    case 'json[]':
+    case 'jsonb':
+    case 'jsonb[]':
+      cellType = 'Json';
+      break;
+    case 'smallint':
+    case 'smallint[]':
+    case 'integer':
+    case 'integer[]':
+    case 'bigint':
+    case 'bigint[]':
+    case 'decimal':
+    case 'decimal[]':
+    case 'numeric':
+    case 'numeric[]':
+    case 'real':
+    case 'real[]':
+    case 'double precision':
+    case 'double precision[]':
+      cellType = 'number';
+      break;
+    case 'boolean':
+      cellType = 'boolean';
+      break;
+    case 'character':
+    case 'character[]':
+    case '"char"':
+    case '"char"[]':
+    case 'character varying':
+    case 'character varying[]':
+      if (c.internal_size && c.internal_size >= 0 && c.internal_size != 65535) {
+        // Update column type to display length on column header
+        columnType += ' (' + c.internal_size + ')';
+      }
+      cellType = 'string';
+      break;
+    case 'bytea':
+    case 'bytea[]':
+      cellType = 'binary';
+      break;
+    case 'geometry':
+      // PostGIS geometry type
+      cellType = 'geometry';
+      isEditable = false;
+      break;
+    case 'geography':
+      // PostGIS geography type
+      cellType = 'geography';
+      isEditable = false;
+      break;
+    default:
+      cellType = 'string';
+    }
+
+    let arrayBracketIdx = columnTypeInternal.lastIndexOf('[]');
+    return {
+      'key': c.name,
+      'name': c.name,
+      'display_name': c.display_name,
+      'type': columnTypeInternal,
+      'display_type': columnType,
+      'column_type_internal': columnTypeInternal,
+      'pos': c.pos,
+      'cell': cellType,
+      'can_edit': (c.name == 'oid') ? false : isEditable,
+      'not_null': c.not_null,
+      'has_default_val': c.has_default_val,
+      'is_array': arrayBracketIdx > -1 && arrayBracketIdx + 2 == columnTypeInternal.length,
+    };
+  }
+
+  processColumns(data) {
+    let columns = [];
+    let self = this;
+
+    let pgTypesOidMap = {};
+    _.isArray(data.types) && data.types?.forEach((t)=>{
+      pgTypesOidMap[t.oid] = t.typname;
+    });
+
+    // Create columns
+    data.colinfo.forEach(function(c) {
+      var isPK = false,
+        isEditable = data.can_edit && (!self.isQueryTool || c.is_editable);
+
+      // Check whether this column is a primary key
+      if (isEditable) {
+        isPK = _.some(data.primary_keys||[], (_v, key)=>key === c.name);
+      }
+
+      // Create column label and type.
+      columns.push(self.getFinalColumn(c, isEditable, isPK, pgTypesOidMap));
+    });
+
+    return columns;
+  }
+
+  processClipboardVal(columnVal, col) {
+    if(columnVal === '') {
+      if(col.has_default_val) {
+        columnVal = undefined;
+      }
+    }
+    if(col.cell === 'boolean') {
+      if(columnVal == 'true') {
+        columnVal = true;
+      } else if(columnVal == 'false') {
+        columnVal = false;
+      } else {
+        columnVal = null;
+      }
+    }
+    return columnVal;
+  }
+
+  processRows(result, columns, fromClipboard=false) {
+    let retVal = [];
+    if(!_.isArray(result) || !_.size(result)) {
+      return retVal;
+    }
+    for(const rec of result) {
+      // Convert 2darray to dict.
+      let rowObj = {};
+      for(const col of columns) {
+        let columnVal = rec[col.pos];
+        /* If the source is clipboard, then it needs some extra handling */
+        if(fromClipboard) {
+          columnVal = this.processClipboardVal(columnVal, col);
+        }
+        rowObj[col.key] = columnVal;
+      }
+      /* This will used for uniquely identifying selected rows */
+      rowObj[this.clientPK] = this.clientPKLastIndex.toString();
+      retVal.push(rowObj);
+      this.clientPKLastIndex++;
+    }
+    return retVal;
+  }
+
+  getPlanJson(result, data) {
+    if(result && !_.isEmpty(data.colinfo)
+      && data.colinfo[0].name == 'QUERY PLAN' && !_.isEmpty(data.types)
+      && data.types[0] && data.types[0].typname === 'json') {
+      /* json is sent as text, parse it */
+      let planJson = JSON.parse(data.result[0][0]);
+      if (planJson && planJson[0] && planJson[0].hasOwnProperty('Plan') &&
+            _.isObject(planJson[0]['Plan'])
+      ) {
+        return planJson;
+      }
+    }
+    return null;
+  }
+
+  queryFinished(httpMessage, onResultsAvailable, onExplain) {
+    let msg;
+    let endTime = new Date();
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END, true);
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, httpMessage.data.data.transaction_status);
+    this.eventBus.fireEvent(QUERY_TOOL_EVENTS.TASK_END, gettext('Query complete'), endTime);
+    this.setEndTime(endTime);
+
+    msg = gettext('Query returned successfully in %s.', this.queryRunTime());
+    if(this.hasResultsToDisplay(httpMessage.data.data)) {
+      if(!_.isNull(httpMessage.data.data.additional_messages)){
+        msg = httpMessage.data.data.additional_messages + '\n' + msg;
+      }
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, msg);
+      this.setClientPK(httpMessage.data.data.client_primary_key);
+      let {result} = httpMessage.data.data;
+      let data = {
+        ...this.startData,
+        ...httpMessage.data.data,
+      };
+      data.primary_keys = (_.isEmpty(data.primary_keys) && data.has_oids) ? data.oids : data.primary_keys;
+      data.can_edit = !_.isEmpty(data.primary_keys);
+      let procColumns = this.processColumns(data);
+      onResultsAvailable(data, procColumns, this.processRows(result, procColumns, this.processRows(result, procColumns)));
+      this.setStartData(null);
+      let planJson = this.getPlanJson(result, data);
+      if(planJson) {
+        onExplain(planJson);
+      } else {
+        onExplain(null);
+        this.eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.DATA_OUTPUT);
+      }
+    } else {
+      if (httpMessage.data.data.result) {
+        msg = httpMessage.data.data.result + '\n\n' + msg;
+      }
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, msg, true);
+      this.eventBus.fireEvent(QUERY_TOOL_EVENTS.FOCUS_PANEL, PANELS.MESSAGES);
+    }
+    return msg;
+  }
+}
+
+function dataChangeReducer(state, action) {
+  let dataChange = _.clone(state);
+  let count;
+  switch (action.type) {
+  case 'updated':
+    dataChange.updated[action.clientPK] = {
+      ...dataChange.updated[action.clientPK],
+      ...action.payload,
+      data: {
+        ...dataChange.updated[action.clientPK]?.data,
+        ...action.payload.data,
+      }
+    };
+    break;
+  case 'added':
+    action.add = action.add || {};
+    action.remove = action.remove || [];
+    dataChange.added = _.pickBy(dataChange.added, (_v, k)=>(action.remove.indexOf(k) == -1));
+    dataChange.added_index = _.pickBy(dataChange.added_index, (v)=>(action.remove.indexOf(v) == -1));
+    count = _.max(Object.keys(dataChange.added_index))||0;
+    Object.keys(action.add).forEach((k)=>{
+      dataChange.added_index[++count] = k;
+    });
+    dataChange.added = {
+      ...dataChange.added,
+      ...action.add,
+    };
+    break;
+  case 'deleted':
+    dataChange.deleted = _.pickBy(dataChange.deleted, (_v, k)=>(action.remove.indexOf(k) == -1));
+    dataChange.deleted = {
+      ...dataChange.deleted,
+      ...action.add,
+    };
+    break;
+  case 'reset':
+    dataChange = {
+      updated: {},
+      added: {},
+      added_index: {},
+      deleted: {},
+    };
+    break;
+  default:
+    break;
+  }
+
+  return dataChange;
+}
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    display: 'flex',
+    height: '100%',
+    flexDirection: 'column',
+    backgroundColor: theme.otherVars.qtDatagridBg,
+  }
+}));
+
+export function ResultSet() {
+  const classes = useStyles();
+  const containerRef = React.useRef(null);
+  const eventBus = useContext(QueryToolEventsContext);
+  const queryToolCtx = useContext(QueryToolContext);
+  const [loaderText, setLoaderText] = useState('');
+  const [queryData, setQueryData] = useState(null);
+  const [rows, setRows] = useState([]);
+  const [columns, setColumns] = useState([]);
+  const [isLoadingMore, setIsLoadingMore] = useState(false);
+  const api = getApiInstance();
+  const rsu = React.useRef(new ResultSetUtils(api, queryToolCtx.params.trans_id, queryToolCtx.params.is_query_tool));
+  const [dataChangeStore, dispatchDataChange] = React.useReducer(dataChangeReducer, {});
+  const [selectedRows, setSelectedRows] = useState(new Set());
+  const [selectedColumns, setSelectedColumns] = useState(new Set());
+  const selectedCell = useRef([]);
+  const setSelectedCell = (val)=>selectedCell.current=val;
+  const [rowsResetKey, setRowsResetKey] = useState(true);
+
+  rsu.current.setEventBus(eventBus);
+
+  const executionStartCallback = async (query, explainObject, external=false, reconnect=false)=>{
+    /* Reset */
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.HIGHLIGHT_ERROR, null);
+    dispatchDataChange({type: 'reset'});
+    setSelectedRows(new Set());
+    setSelectedColumns(new Set());
+    setLoaderText(gettext('Waiting for the query to complete...'));
+    let goForPoll = await rsu.current.startExecution(
+      query, explainObject,
+      ()=>{
+        setColumns([]);
+        setRows([]);
+      },
+      {isQueryTool: queryToolCtx.params.is_query_tool, external: external, reconnect: reconnect}
+    );
+    if(goForPoll) {
+      rsu.current.pollForResult(
+        (procQueryData, procColumns, procRows)=>{
+          setQueryData(procQueryData);
+          setColumns(procColumns);
+          setRows(procRows);
+          setRowsResetKey(!rowsResetKey);
+        },
+        (planJson)=>{
+          /* No need to open if plan is empty */
+          if(!LayoutHelper.isTabOpen(queryToolCtx.docker, PANELS.EXPLAIN) && !planJson) {
+            return;
+          }
+          LayoutHelper.openTab(queryToolCtx.docker, {
+            id: PANELS.EXPLAIN,
+            title:'Explain',
+            content: <Explain plans={planJson} />,
+            closable: true,
+          }, PANELS.MESSAGES, 'after-tab', true);
+        },
+        ()=>{
+          setColumns([]);
+          setRows([]);
+        }
+      );
+    }
+  };
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_STOP_EXECUTION, async ()=>{
+      try {
+        await rsu.current.stopExecution();
+      } catch(e) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, e);
+      }
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, CONNECTION_STATUS.TRANSACTION_STATUS_IDLE);
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.EXECUTION_END);
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_END, ()=>{
+      setLoaderText(null);
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS, ()=>{
+      let extension = queryToolCtx.preferences?.sqleditor?.csv_field_separator === ',' ? '.csv': '.txt';
+      let fileName = 'data-' + new Date().getTime() + extension;
+      if(!queryToolCtx.params.is_query_tool) {
+        fileName = queryToolCtx.params.node_name + extension;
+      }
+      setLoaderText(gettext('Downloading results...'));
+      rsu.current.saveResultsToFile(fileName);
+      setLoaderText('');
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_SET_LIMIT, async (limit)=>{
+      setLoaderText(gettext('Setting the limit on the result...'));
+      try {
+        await rsu.current.setLimit(limit);
+        setLoaderText('');
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+      } catch(err) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err, {
+          checkTransaction: true,
+        });
+        setLoaderText('');
+      }
+    });
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_REMOVE_FILTER, async ()=>{
+      setLoaderText(gettext('Removing the filter...'));
+      try {
+        await rsu.current.removeFilter();
+        setLoaderText('');
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+      } catch(err) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err, {
+          checkTransaction: true,
+        });
+        setLoaderText('');
+      }
+    });
+  }, []);
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_START, executionStartCallback);
+    return ()=>{
+      eventBus.deregisterListener(QUERY_TOOL_EVENTS.EXECUTION_START, executionStartCallback);
+    };
+  }, [queryToolCtx.docker]);
+
+  useEffect(()=>{
+    const triggerFilter = async (include)=>{
+      if(_.isEmpty(selectedCell.current)) {
+        return;
+      }
+      setLoaderText(gettext('Applying the new filter...'));
+      try {
+        let data = {
+          [selectedCell.current[1].key]: selectedCell.current[0][selectedCell.current[1].key],
+        };
+        if(include) {
+          await rsu.current.includeFilter(data);
+        } else {
+          await rsu.current.excludeFilter(data);
+        }
+        setSelectedCell([]);
+        setLoaderText('');
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_EXECUTION);
+      } catch(err) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, err, {
+          checkTransaction: true,
+        });
+        setLoaderText('');
+      }
+    };
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, triggerFilter);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_INCLUDE_EXCLUDE_FILTER, triggerFilter);
+  }, [selectedCell.current]);
+
+  useEffect(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, selectedRows.size, selectedColumns.size);
+  }, [selectedRows.size, selectedColumns.size]);
+
+  useEffect(()=>{
+    rsu.current.transId = queryToolCtx.params.trans_id;
+  }, [queryToolCtx.params.trans_id]);
+
+  const fetchMoreRows = async (all=false, callback)=>{
+    if(queryData.has_more_rows) {
+      setIsLoadingMore(true);
+      const res = await rsu.current.getMoreRows(all);
+      const newRows = rsu.current.processRows(res.data.data.result, columns);
+      setRows((prevRows)=>[...prevRows, ...newRows]);
+      setQueryData((prev)=>({
+        ...prev,
+        has_more_rows: res.data.data.has_more_rows,
+        rows_fetched_to: res.data.data.rows_fetched_to!=0 ? res.data.data.rows_fetched_to : prev.rows_fetched_to,
+      }));
+      setIsLoadingMore(false);
+    }
+    callback?.();
+  };
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, fetchMoreRows);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS, fetchMoreRows);
+  }, [queryData, columns]);
+
+  useEffect(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.ROWS_FETCHED, queryData?.rows_fetched_to, queryData?.rows_affected);
+  }, [queryData]);
+
+  const warnSaveDataClose = ()=>{
+    // No changes.
+    if(!_.size(dataChangeStore.updated) && !_.size(dataChangeStore.added) && !_.size(dataChangeStore.deleted)
+        || !queryToolCtx.preferences?.sqleditor.prompt_save_data_changes) {
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE);
+      return;
+    }
+    queryToolCtx.modal.showModal(gettext('Save data changes?'), (closeModal)=>(
+      <ConfirmSaveContent
+        closeModal={closeModal}
+        text={gettext('The data has changed. Do you want to save changes?')}
+        onDontSave={()=>{
+          eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE);
+        }}
+        onSave={async ()=>{
+          await triggerSaveData();
+          eventBus.fireEvent(QUERY_TOOL_EVENTS.WARN_SAVE_TEXT_CLOSE);
+        }}
+      />
+    ));
+  };
+  useEffect(()=>{
+    let isDirty = _.size(dataChangeStore.updated) || _.size(dataChangeStore.added) || _.size(dataChangeStore.deleted);
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, isDirty, dataChangeStore);
+
+    eventBus.registerListener(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE, warnSaveDataClose);
+    return ()=>{
+      eventBus.deregisterListener(QUERY_TOOL_EVENTS.WARN_SAVE_DATA_CLOSE, warnSaveDataClose);
+    };
+  }, [dataChangeStore]);
+
+  const triggerSaveData = async ()=>{
+    if(!_.size(dataChangeStore.updated) && !_.size(dataChangeStore.added) && !_.size(dataChangeStore.deleted)) {
+      return;
+    }
+    rsu.current.historyQuerySource = QuerySources.SAVE_DATA;
+    setLoaderText(gettext('Saving data...'));
+    try {
+      /* Convert the added info to actual rows */
+      let added = {...dataChangeStore.added};
+      Object.keys(added).map((clientPK)=>{
+        added[clientPK].data = _.find(rows, (r)=>rowKeyGetter(r)==clientPK);
+      });
+      let {data: respData} = await rsu.current.saveData({
+        updated: dataChangeStore.updated,
+        deleted: dataChangeStore.deleted,
+        added_index: dataChangeStore.added_index,
+        added: added,
+        columns: columns,
+      });
+
+      try {
+        respData.data.query_results.forEach((r)=>{
+          eventBus.fireEvent(QUERY_TOOL_EVENTS.PUSH_HISTORY, {
+            'status': r.status,
+            'start_time': rsu.current.startTime,
+            'query': r.sql,
+            'row_affected': r.rows_affected,
+            'total_time': null,
+            'message': r.result,
+            'query_source': QuerySources.SAVE_DATA,
+            'is_pgadmin_query': true,
+            'info': gettext('This query was generated by pgAdmin as part of a "Save Data" operation'),
+          });
+        });
+      } catch (_e) {/* History errors should not bother others */}
+
+      if(!respData.data.status) {
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_DATA_DONE, false);
+        eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, respData.data.result);
+        Notifier.error(respData.data.result, 20000);
+        // If the transaction is not idle, notify the user that previous queries are not rolled back,
+        // only the failed save queries.
+        if (respData.data.transaction_status != CONNECTION_STATUS.TRANSACTION_STATUS_IDLE) {
+          Notifier.info(gettext('Saving data changes was rolled back but the current transaction is ' +
+                                'still active; previous queries are unaffected.'));
+        }
+        setLoaderText(null);
+        return;
+      }
+
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_DATA_DONE, true);
+      if(!_.size(dataChangeStore.added)) {
+        // Update the rows in a grid after addition
+        respData.data.query_results.forEach((qr)=>{
+          if(!_.isNull(qr.row_added)) {
+            let rowClientPK = Object.keys(qr.row_added)[0];
+            setRows((prevRows)=>{
+              let rowIdx = prevRows.findIndex((r)=>rowKeyGetter(r)==rowClientPK);
+              return [
+                ...prevRows.slice(0, rowIdx),
+                {
+                  ...prevRows[rowIdx],
+                  ...qr.row_added[rowClientPK],
+                },
+                ...prevRows.slice(rowIdx+1),
+              ];
+            });
+          }
+        });
+      }
+      let deletedKeys = Object.keys(dataChangeStore.deleted);
+      if(deletedKeys.length == rows.length) {
+        setRows([]);
+      }
+      else if(deletedKeys.length > 0) {
+        setRows((prevRows)=>{
+          return prevRows.filter((row)=>{
+            return deletedKeys.indexOf(row[rsu.current.clientPK]) == -1;
+          });
+        });
+        setColumns(columns);
+      }
+      dispatchDataChange({type: 'reset'});
+      setSelectedRows(new Set());
+      setSelectedColumns(new Set());
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_CONNECTION_STATUS, respData.data.transaction_status);
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.SET_MESSAGE, '');
+      Notifier.success(gettext('Data saved successfully.'));
+      if(respData.data.transaction_status > CONNECTION_STATUS.TRANSACTION_STATUS_IDLE) {
+        Notifier.info(gettext('Auto-commit is off. You still need to commit changes to the database.'));
+      }
+    } catch (error) {
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.SAVE_DATA_DONE, false);
+      eventBus.fireEvent(QUERY_TOOL_EVENTS.HANDLE_API_ERROR, error, {
+        checkTransaction: true,
+      });
+    }
+    setLoaderText(null);
+  };
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_SAVE_DATA, triggerSaveData);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_SAVE_DATA, triggerSaveData);
+  }, [dataChangeStore, rows, columns]);
+
+  const copyDataFunc = (withHeaders=false)=>{
+    const queryToolPref = queryToolCtx.preferences.sqleditor;
+    let copyData = new CopyData({
+      quoting: queryToolPref.results_grid_quoting,
+      quote_char: queryToolPref.results_grid_quote_char,
+      field_separator: queryToolPref.results_grid_field_separator,
+    });
+    let copyRows=[], copyCols=[];
+    if(selectedRows.size > 0) {
+      copyCols = columns;
+      copyRows = rows.filter((r)=>selectedRows.has(r[rsu.current.clientPK]));
+    } else if(selectedColumns.size > 0) {
+      /* Row num col is added by QueryDataGrid, index will be +1 */
+      copyCols = _.filter(columns, (_c, i)=>selectedColumns.has(i+1));
+      copyRows = _.map(rows, (r)=>_.pick(r, _.map(copyCols, (c)=>c.key)));
+    } else if(selectedCell.current[0] && selectedCell.current[1]) {
+      copyCols = [selectedCell.current[1]];
+      copyRows = [{[selectedCell.current[1].key]: selectedCell.current[0][selectedCell.current[1].key]}];
+    }
+    if(copyRows.length > 0 && copyCols.length >0) {
+      copyData.copyRowsToCsv(copyRows, copyCols, withHeaders);
+    }
+  };
+
+  const triggerDeleteRows = ()=>{
+    if(!queryData.can_edit) {
+      return;
+    }
+    let add = {};
+    let remove = [];
+    let selRowsData = rows.filter((r)=>selectedRows.has(r[rsu.current.clientPK]));
+    let removeNewlyAdded = [];
+    for(let row of selRowsData) {
+      let clientPK = row[rsu.current.clientPK];
+      if(clientPK in dataChangeStore.deleted) {
+        remove.push(clientPK);
+      } else {
+        /* If deleted from newly added */
+        if(clientPK in dataChangeStore.added) {
+          removeNewlyAdded.push(clientPK);
+        } else {
+          let primaryKeys = {};
+          Object.keys(queryData.primary_keys).forEach((k)=>{
+            primaryKeys[k] = row[k];
+          });
+          add[clientPK] = primaryKeys;
+        }
+      }
+    }
+    if(removeNewlyAdded.length > 0) {
+      dispatchDataChange({
+        type: 'added',
+        remove: removeNewlyAdded,
+      });
+      setRows((prev)=>{
+        return prev.filter((r)=>removeNewlyAdded.indexOf(rowKeyGetter(r))==-1);
+      });
+      setSelectedRows((prev)=>{
+        let newRows = new Set(prev);
+        removeNewlyAdded.forEach((rowId)=>{
+          if(newRows.has(rowId)) {
+            newRows.delete(rowId);
+          }
+        });
+        return newRows;
+      });
+    }
+
+    dispatchDataChange({
+      type: 'deleted',
+      add: add,
+      remove: remove,
+    });
+  };
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_DELETE_ROWS, triggerDeleteRows);
+    eventBus.registerListener(QUERY_TOOL_EVENTS.COPY_DATA, copyDataFunc);
+    return ()=>{
+      eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_DELETE_ROWS, triggerDeleteRows);
+      eventBus.deregisterListener(QUERY_TOOL_EVENTS.COPY_DATA, copyDataFunc);
+    };
+  }, [selectedRows, selectedColumns, queryData, dataChangeStore, selectedCell.current]);
+
+  useEffect(()=>{
+    const triggerAddRows = (_rows)=>{
+      let newRows = rsu.current.processRows(_rows, columns, true);
+      setRows((prev)=>[...newRows, ...prev]);
+      let add = {};
+      newRows.forEach((row)=>{
+        add[rowKeyGetter(row)] = {
+          err: false,
+        };
+      });
+      dispatchDataChange({
+        type: 'added',
+        add: add,
+      });
+    };
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, triggerAddRows);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, triggerAddRows);
+  }, [columns]);
+
+  useEffect(()=>{
+    const renderGeometries = (column)=>{
+      let selRowsData = selectedRows.size === 0 ? rows : rows.filter((r)=>selectedRows.has(rowKeyGetter(r)));
+      LayoutHelper.openTab(queryToolCtx.docker, {
+        id: PANELS.GEOMETRY,
+        title:'Geometry Viewer',
+        content: <GeometryViewer rows={selRowsData} columns={columns} column={column} />,
+        closable: true,
+      }, PANELS.MESSAGES, 'after-tab', true);
+    };
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_RENDER_GEOMETRIES, renderGeometries);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_RENDER_GEOMETRIES, renderGeometries);
+  }, [rows, columns, selectedRows.size]);
+
+  const handleScroll = (e)=>{
+    if (isLoadingMore || !rsu.current.isAtBottom(e)) return;
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.FETCH_MORE_ROWS);
+  };
+
+  const onRowsChange = (newRows, otherInfo)=>{
+    let row = newRows[otherInfo.indexes[0]];
+    let clientPK = rowKeyGetter(row);
+
+    if(clientPK in (dataChangeStore.added || {})) {
+      /* No need to track this */
+    } else if(clientPK in (dataChangeStore.updated || {})) {
+      dispatchDataChange({
+        type: 'updated',
+        clientPK: clientPK,
+        payload: {
+          data: {[otherInfo.column.key]: row[otherInfo.column.key]},
+        }
+      });
+    } else {
+      let oldRow = rows[otherInfo.indexes[0]];
+      /* If there are no primary keys, then discard */
+      if(queryData.can_edit) {
+        let primaryKeys = {};
+        Object.keys(queryData.primary_keys).forEach((k)=>{
+          primaryKeys[k] = oldRow[k];
+        });
+        dispatchDataChange({
+          type: 'updated',
+          clientPK: clientPK,
+          payload: {
+            err: false,
+            data: {[otherInfo.column.key]: row[otherInfo.column.key]},
+            primary_keys: primaryKeys,
+          }
+        });
+      }
+    }
+    setRows(newRows);
+  };
+
+  const rowKeyGetter = React.useCallback((row)=>row[rsu.current.clientPK]);
+  return (
+    <Box className={classes.root} ref={containerRef}>
+      <Loader message={loaderText} />
+      <Loader message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
+      {(columns.length == 0 && rows.length == 0) &&
+        <EmptyPanelMessage text={gettext('No data output. Execute a query to get output.')}/>
+      }
+      {(columns.length != 0 || rows.length != 0) && <>
+        <ResultSetToolbar containerRef={containerRef} canEdit={queryData.can_edit}/>
+        <Box flexGrow="1" minHeight="0">
+          <QueryToolDataGrid
+            columns={columns}
+            rows={rows}
+            totalRowCount={queryData?.rows_affected}
+            columnWidthBy={
+              queryToolCtx.preferences?.sqleditor?.column_data_auto_resize == 'by_data' ?
+                queryToolCtx.preferences.sqleditor.column_data_max_width :
+                queryToolCtx.preferences?.sqleditor?.column_data_auto_resize
+            }
+            rowsResetKey={rowsResetKey}
+            rowKeyGetter={rowKeyGetter}
+            onScroll={handleScroll}
+            onRowsChange={onRowsChange}
+            dataChangeStore={dataChangeStore}
+            selectedRows={selectedRows}
+            onSelectedRowsChange={setSelectedRows}
+            selectedColumns={selectedColumns}
+            onSelectedColumnsChange={setSelectedColumns}
+            onSelectedCellChange={setSelectedCell}
+          />
+        </Box>
+      </>}
+    </Box>
+  );
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx
new file mode 100644
index 000000000..e51e2ad5a
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/ResultSetToolbar.jsx
@@ -0,0 +1,170 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React, {useContext, useCallback, useEffect, useState} from 'react';
+import { makeStyles } from '@material-ui/styles';
+import { Box } from '@material-ui/core';
+import { PgButtonGroup, PgIconButton } from '../../../../../../static/js/components/Buttons';
+import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown';
+import PlaylistAddRoundedIcon from '@material-ui/icons/PlaylistAddRounded';
+import FileCopyRoundedIcon from '@material-ui/icons/FileCopyRounded';
+import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded';
+import { PasteIcon, SaveDataIcon } from '../../../../../../static/js/components/ExternalIcon';
+import GetAppRoundedIcon from '@material-ui/icons/GetAppRounded';
+import {QUERY_TOOL_EVENTS} from '../QueryToolConstants';
+import { QueryToolContext, QueryToolEventsContext } from '../QueryToolComponent';
+import { PgMenu, PgMenuItem } from '../../../../../../static/js/components/Menu';
+import gettext from 'sources/gettext';
+import { useKeyboardShortcuts } from '../../../../../../static/js/custom_hooks';
+import {shortcut_key} from 'sources/keyboard_shortcuts';
+import CopyData from '../QueryToolDataGrid/CopyData';
+import PropTypes from 'prop-types';
+import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    padding: '2px',
+    display: 'flex',
+    alignItems: 'center',
+    gap: '4px',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+    ...theme.mixins.panelBorder.bottom,
+  },
+}));
+
+export function ResultSetToolbar({containerRef, canEdit}) {
+  const classes = useStyles();
+  const eventBus = useContext(QueryToolEventsContext);
+  const queryToolCtx = useContext(QueryToolContext);
+
+  const [buttonsDisabled, setButtonsDisabled] = useState({
+    'save-data': true,
+    'delete-rows': true,
+    'copy-rows': true,
+  });
+  const [menuOpenId, setMenuOpenId] = React.useState(null);
+  const [checkedMenuItems, setCheckedMenuItems] = React.useState({});
+  /* Menu button refs */
+  const copyMenuRef = React.useRef(null);
+
+  const queryToolPref = queryToolCtx.preferences.sqleditor;
+
+  const setDisableButton = useCallback((name, disable=true)=>{
+    setButtonsDisabled((prev)=>({...prev, [name]: disable}));
+  }, []);
+  const saveData = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_DATA);
+  }, []);
+  const deleteRows = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_DELETE_ROWS);
+  }, []);
+  const pasteRows = useCallback(async ()=>{
+    let copyUtils = new CopyData({
+      quoting: queryToolPref.results_grid_quoting,
+      quote_char: queryToolPref.results_grid_quote_char,
+      field_separator: queryToolPref.results_grid_field_separator,
+    });
+    let copiedRows = copyUtils.getCopiedRows();
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows);
+  }, [queryToolPref]);
+  const copyData = ()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_DATA, checkedMenuItems['copy_with_headers']);
+  };
+  const addRow = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, [[]]);
+  }, []);
+  const downloadResult = useCallback(()=>{
+    eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_SAVE_RESULTS);
+  }, []);
+
+  const openMenu = useCallback((e)=>{
+    setMenuOpenId(e.currentTarget.name);
+  }, []);
+  const handleMenuClose = useCallback(()=>{
+    setMenuOpenId(null);
+  }, []);
+
+  const checkMenuClick = useCallback((e)=>{
+    setCheckedMenuItems((prev)=>{
+      let newVal = !prev[e.value];
+      return {
+        ...prev,
+        [e.value]: newVal,
+      };
+    });
+  }, []);
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (isDirty)=>{
+      setDisableButton('save-data', !isDirty);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows, cols)=>{
+      setDisableButton('delete-rows', !rows);
+      setDisableButton('copy-rows', (!rows && !cols));
+    });
+  }, []);
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TRIGGER_COPY_DATA, copyData);
+    return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.TRIGGER_COPY_DATA, copyData);
+  }, [checkedMenuItems['copy_with_headers']]);
+
+  useKeyboardShortcuts([
+    {
+      shortcut: queryToolPref.save_data,
+      options: {
+        callback: ()=>{saveData();}
+      }
+    },
+    {
+      shortcut: queryToolPref.download_results,
+      options: {
+        callback: (e)=>{e.preventDefault(); downloadResult();}
+      }
+    },
+  ], containerRef);
+
+  return (
+    <>
+      <Box className={classes.root}>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Add row')} icon={<PlaylistAddRoundedIcon style={{height: 'unset'}}/>}
+            accesskey={shortcut_key(queryToolPref.btn_add_row)} disabled={!canEdit} onClick={addRow} />
+          <PgIconButton title={gettext('Copy')} icon={<FileCopyRoundedIcon />}
+            accesskey={shortcut_key(queryToolPref.btn_copy_row)} disabled={buttonsDisabled['copy-rows']} onClick={copyData} />
+          <PgIconButton title={gettext('Copy options')} icon={<KeyboardArrowDownIcon />} splitButton
+            name="menu-copyheader" ref={copyMenuRef} onClick={openMenu} />
+          <PgIconButton title={gettext('Paste')} icon={<PasteIcon />}
+            accesskey={shortcut_key(queryToolPref.btn_paste_row)} disabled={!canEdit} onClick={pasteRows} />
+          <PgIconButton title={gettext('Delete')} icon={<DeleteRoundedIcon />}
+            accesskey={shortcut_key(queryToolPref.btn_delete_row)} disabled={buttonsDisabled['delete-rows'] || !canEdit} onClick={deleteRows} />
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Save Data Changes')} icon={<SaveDataIcon />}
+            shortcut={queryToolPref.save_data} disabled={buttonsDisabled['save-data'] || !canEdit} onClick={saveData}/>
+        </PgButtonGroup>
+        <PgButtonGroup size="small">
+          <PgIconButton title={gettext('Save results to file')} icon={<GetAppRoundedIcon />}
+            onClick={downloadResult} shortcut={queryToolPref.download_results}/>
+        </PgButtonGroup>
+      </Box>
+      <PgMenu
+        anchorRef={copyMenuRef}
+        open={menuOpenId=='menu-copyheader'}
+        onClose={handleMenuClose}
+      >
+        <PgMenuItem hasCheck value="copy_with_headers" checked={checkedMenuItems['copy_with_headers']} onClick={checkMenuClick}>Copy with headers</PgMenuItem>
+      </PgMenu>
+    </>
+  );
+}
+
+ResultSetToolbar.propTypes = {
+  containerRef: CustomPropTypes.ref,
+  canEdit: PropTypes.bool,
+};
diff --git a/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx b/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx
new file mode 100644
index 000000000..df562884e
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/components/sections/StatusBar.jsx
@@ -0,0 +1,117 @@
+
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import React, { useEffect, useState, useContext }  from 'react';
+import { makeStyles } from '@material-ui/styles';
+import { Box } from '@material-ui/core';
+import clsx from 'clsx';
+import _ from 'lodash';
+import { QUERY_TOOL_EVENTS } from '../QueryToolConstants';
+import { useStopwatch } from '../../../../../../static/js/custom_hooks';
+import { QueryToolEventsContext } from '../QueryToolComponent';
+
+
+const useStyles = makeStyles((theme)=>({
+  root: {
+    display: 'flex',
+    alignItems: 'center',
+    ...theme.mixins.panelBorder.top,
+    flexWrap: 'wrap',
+    backgroundColor: theme.otherVars.editorToolbarBg,
+    userSelect: 'text',
+  },
+  padding: {
+    padding: '2px 12px',
+  },
+  divider: {
+    ...theme.mixins.panelBorder.right,
+  },
+  mlAuto: {
+    marginLeft: 'auto',
+  }
+}));
+
+export function StatusBar() {
+  const classes = useStyles();
+  const eventBus = useContext(QueryToolEventsContext);
+  const [position, setPosition] = useState([1, 1]);
+  const [lastTaskText, setLastTaskText] = useState(null);
+  const [rowsCount, setRowsCount] = useState([0, 0]);
+  const [selectedRowsCount, setSelectedRowsCount] = useState(0);
+  const [dataRowChangeCounts, setDataRowChangeCounts] = useState({
+    isDirty: false,
+    added: 0,
+    updated: 0,
+    deleted: 0,
+  });
+  const {seconds, minutes, hours, msec, start:startTimer, pause:pauseTimer, reset:resetTimer} = useStopwatch({});
+
+  useEffect(()=>{
+    eventBus.registerListener(QUERY_TOOL_EVENTS.CURSOR_ACTIVITY, (newPos)=>{
+      setPosition(newPos||[1, 1]);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.EXECUTION_END, ()=>{
+      pauseTimer();
+      setLastTaskText('Query complete');
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TASK_START, (taskText, startTime)=>{
+      resetTimer();
+      startTimer(startTime);
+      setLastTaskText(taskText);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.TASK_END, (taskText, endTime)=>{
+      pauseTimer(endTime);
+      setLastTaskText(taskText);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.ROWS_FETCHED, (fetched, total)=>{
+      setRowsCount([fetched||0, total||0]);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.SELECTED_ROWS_COLS_CHANGED, (rows)=>{
+      setSelectedRowsCount(rows);
+    });
+    eventBus.registerListener(QUERY_TOOL_EVENTS.DATAGRID_CHANGED, (_isDirty, dataChangeStore)=>{
+      setDataRowChangeCounts({
+        added: Object.keys(dataChangeStore.added||{}).length,
+        updated: Object.keys(dataChangeStore.updated||{}).length,
+        deleted: Object.keys(dataChangeStore.deleted||{}).length,
+      });
+    });
+  }, []);
+
+  let stagedText = '';
+  if(dataRowChangeCounts.added > 0) {
+    stagedText += ` Added: ${dataRowChangeCounts.added};`;
+  }
+  if(dataRowChangeCounts.updated > 0) {
+    stagedText += ` Updated: ${dataRowChangeCounts.updated};`;
+  }
+  if(dataRowChangeCounts.deleted > 0) {
+    stagedText += ` Deleted: ${dataRowChangeCounts.deleted};`;
+  }
+
+  return (
+    <Box className={classes.root}>
+      <Box className={clsx(classes.padding, classes.divider)}>Total rows: {rowsCount[0]} of {rowsCount[1]}</Box>
+      {lastTaskText &&
+        <Box className={clsx(classes.padding, classes.divider)}>{lastTaskText} {hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}.{msec.toString().padStart(3, '0')}</Box>
+      }
+      {!lastTaskText && !_.isNull(lastTaskText) &&
+        <Box className={clsx(classes.padding, classes.divider)}>{lastTaskText} {hours.toString().padStart(2, '0')}:{minutes.toString().padStart(2, '0')}:{seconds.toString().padStart(2, '0')}.{msec.toString().padStart(3, '0')}</Box>
+      }
+      {Boolean(selectedRowsCount) &&
+        <Box className={clsx(classes.padding, classes.divider)}>Rows selected: {selectedRowsCount}</Box>}
+      {stagedText &&
+        <Box className={clsx(classes.padding, classes.divider)}>
+          <span>Changes staged: {stagedText}</span>
+        </Box>
+      }
+      <Box className={clsx(classes.padding, classes.mlAuto)}>Ln {position[0]}, Col {position[1]}</Box>
+    </Box>
+  );
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/index.js b/web/pgadmin/tools/sqleditor/static/js/index.js
new file mode 100644
index 000000000..6ad35fb34
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/static/js/index.js
@@ -0,0 +1,26 @@
+/////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2022, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////
+import pgAdmin from 'sources/pgadmin';
+import pgBrowser from 'top/browser/static/js/browser';
+import SQLEditor from './SQLEditorModule';
+
+/* eslint-disable */
+/* This is used to change publicPath of webpack at runtime for loading chunks */
+/* Do not add let, var, const to this variable */
+__webpack_public_path__ = window.resourceBasePath;
+/* eslint-enable */
+
+if(!pgAdmin.Tools) {
+  pgAdmin.Tools = {};
+}
+pgAdmin.Tools.SQLEditor = SQLEditor.getInstance(pgAdmin, pgBrowser);
+
+module.exports = {
+  SQLEditor: SQLEditor,
+};
diff --git a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js b/web/pgadmin/tools/sqleditor/static/js/show_query_tool.js
similarity index 64%
rename from web/pgadmin/tools/datagrid/static/js/show_query_tool.js
rename to web/pgadmin/tools/sqleditor/static/js/show_query_tool.js
index 157db3335..2f677d7d2 100644
--- a/web/pgadmin/tools/datagrid/static/js/show_query_tool.js
+++ b/web/pgadmin/tools/sqleditor/static/js/show_query_tool.js
@@ -9,7 +9,7 @@
 
 import gettext from '../../../../static/js/gettext';
 import url_for from '../../../../static/js/url_for';
-import {getPanelTitle} from './datagrid_panel_title';
+import {getPanelTitle} from './sqleditor_title';
 import {getRandomInt} from 'sources/utils';
 import $ from 'jquery';
 import Notify from '../../../../static/js/helpers/Notifier';
@@ -18,18 +18,20 @@ function hasDatabaseInformation(parentData) {
   return parentData.database;
 }
 
-function generateUrl(trans_id, title, parentData, sqlId) {
-  let url_endpoint = url_for('datagrid.panel', {
+export function generateUrl(trans_id, parentData, sqlId) {
+  let url_endpoint = url_for('sqleditor.panel', {
     'trans_id': trans_id,
   });
 
   url_endpoint += `?is_query_tool=${true}`
     +`&sgid=${parentData.server_group._id}`
-    +`&sid=${parentData.server._id}`
-    +`&server_type=${parentData.server.server_type}`;
+    +`&sid=${parentData.server._id}`;
 
   if (hasDatabaseInformation(parentData)) {
     url_endpoint += `&did=${parentData.database._id}`;
+    if(parentData.database.label) {
+      url_endpoint += `&database_name=${parentData.database.label}`;
+    }
   }
 
   if(sqlId) {
@@ -47,7 +49,7 @@ function generateTitle(pgBrowser, aciTreeIdentifier) {
   return getPanelTitle(pgBrowser, aciTreeIdentifier);
 }
 
-export function showQueryTool(datagrid, pgBrowser, url, aciTreeIdentifier, transId) {
+export function showQueryTool(queryToolMod, pgBrowser, url, aciTreeIdentifier, transId) {
   const sURL = url || '';
   const queryToolTitle = generateTitle(pgBrowser, aciTreeIdentifier);
 
@@ -66,15 +68,15 @@ export function showQueryTool(datagrid, pgBrowser, url, aciTreeIdentifier, trans
     return;
   }
 
-  const gridUrl = generateUrl(transId, queryToolTitle, parentData);
-  launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, sURL);
+  const gridUrl = generateUrl(transId, parentData);
+  launchQueryTool(queryToolMod, transId, gridUrl, queryToolTitle, sURL);
 }
 
-export function generateScript(parentData, datagrid) {
+export function generateScript(parentData, queryToolMod) {
   const queryToolTitle = `${parentData.database}/${parentData.user}@${parentData.server}`;
   const transId = getRandomInt(1, 9999999);
 
-  let url_endpoint = url_for('datagrid.panel', {
+  let url_endpoint = url_for('sqleditor.panel', {
     'trans_id': transId,
   });
 
@@ -82,12 +84,13 @@ export function generateScript(parentData, datagrid) {
     +`&sgid=${parentData.sgid}`
     +`&sid=${parentData.sid}`
     +`&server_type=${parentData.stype}`
-    +`&did=${parentData.did}`;
+    +`&did=${parentData.did}`
+    +`&sql_id=${parentData.sql_id}`;
 
-  launchDataGrid(datagrid, transId, url_endpoint, queryToolTitle, '');
+  launchQueryTool(queryToolMod, transId, url_endpoint, queryToolTitle, '');
 }
 
-export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, datagrid) {
+export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, queryToolMod) {
   const transId = getRandomInt(1, 9999999);
   parentData = {
     server_group: {
@@ -102,12 +105,12 @@ export function showERDSqlTool(parentData, erdSqlId, queryToolTitle, datagrid) {
     },
   };
 
-  const gridUrl = generateUrl(transId, queryToolTitle, parentData, erdSqlId);
-  launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, '');
+  const gridUrl = generateUrl(transId, parentData, erdSqlId);
+  launchQueryTool(queryToolMod, transId, gridUrl, queryToolTitle, '');
 }
 
-export function launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, sURL) {
-  let retVal = datagrid.launch_grid(transId, gridUrl, true, queryToolTitle, sURL);
+export function launchQueryTool(queryToolMod, transId, gridUrl, queryToolTitle, sURL) {
+  let retVal = queryToolMod.launch(transId, gridUrl, true, queryToolTitle, sURL);
 
   if(!retVal) {
     Notify.alert(
@@ -119,23 +122,17 @@ export function launchDataGrid(datagrid, transId, gridUrl, queryToolTitle, sURL)
   }
 }
 
-function setPanelTitle(panel, value) {
-  if(value) {
-    $('#' + panel.$title.index() + ' div:first').addClass('wcPanelTab-dynamic');
-  } else {
-    $('#' + panel.$title.index() + ' div:first').removeClass('wcPanelTab-dynamic');
-  }
-}
-
 export function _set_dynamic_tab(pgBrowser, value){
-  var datagrid_panels = pgBrowser.docker.findPanels('frm_datagrid');
-  datagrid_panels.forEach(panel => {
-    setPanelTitle(panel, value);
-  });
+  var sqleditor_panels = pgBrowser.docker.findPanels('frm_sqleditor');
+  const process = panel => {
+    if(value) {
+      $('#' + panel.$title.index() + ' div:first').addClass('wcPanelTab-dynamic');
+    } else {
+      $('#' + panel.$title.index() + ' div:first').removeClass('wcPanelTab-dynamic');
+    }
+  };
+  sqleditor_panels.forEach(process);
 
   var debugger_panels = pgBrowser.docker.findPanels('frm_debugger');
-  debugger_panels.forEach(panel => {
-    setPanelTitle(panel, value);
-  });
-
+  debugger_panels.forEach(process);
 }
diff --git a/web/pgadmin/tools/datagrid/static/js/show_data.js b/web/pgadmin/tools/sqleditor/static/js/show_view_data.js
similarity index 87%
rename from web/pgadmin/tools/datagrid/static/js/show_data.js
rename to web/pgadmin/tools/sqleditor/static/js/show_view_data.js
index f418e7da7..a76fd5f15 100644
--- a/web/pgadmin/tools/datagrid/static/js/show_data.js
+++ b/web/pgadmin/tools/sqleditor/static/js/show_view_data.js
@@ -8,24 +8,24 @@
 //////////////////////////////////////////////////////////////
 import gettext from '../../../../static/js/gettext';
 import url_for from '../../../../static/js/url_for';
-import {getDatabaseLabel, generateTitle} from './datagrid_panel_title';
+import {getDatabaseLabel, generateTitle} from './sqleditor_title';
 import CodeMirror from 'bundled_codemirror';
 import * as SqlEditorUtils from 'sources/sqleditor_utils';
 import $ from 'jquery';
 import _ from 'underscore';
 import Notify from '../../../../static/js/helpers/Notifier';
 
-export function showDataGrid(
-  datagrid,
+export function showViewData(
+  queryToolMod,
   pgBrowser,
   alertify,
   connectionData,
-  aciTreeIdentifier,
+  treeIdentifier,
   transId,
   filter=false,
   preferences=null
 ) {
-  const node = pgBrowser.tree.findNodeByDomElement(aciTreeIdentifier);
+  const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
   if (node === undefined || !node.getData()) {
     Notify.alert(
       gettext('Data Grid Error'),
@@ -34,7 +34,7 @@ export function showDataGrid(
     return;
   }
 
-  const parentData = pgBrowser.tree.getTreeNodeHierarchy(  aciTreeIdentifier
+  const parentData = pgBrowser.tree.getTreeNodeHierarchy(  treeIdentifier
   );
 
   if (hasServerOrDatabaseConfiguration(parentData)
@@ -48,30 +48,33 @@ export function showDataGrid(
   }
 
   const gridUrl = generateUrl(transId, connectionData, node.getData(), parentData);
-  const queryToolTitle = generateDatagridTitle(pgBrowser, aciTreeIdentifier);
+  const queryToolTitle = generateViewDataTitle(pgBrowser, treeIdentifier);
   if(filter) {
     initFilterDialog(alertify, pgBrowser);
 
     const validateUrl = generateFilterValidateUrl(node.getData(), parentData);
 
     let okCallback = function(sql) {
-      datagrid.launch_grid(transId, gridUrl, false, queryToolTitle, null, sql);
+      queryToolMod.launch(transId, gridUrl, false, queryToolTitle, null, sql);
     };
 
-    $.get(url_for('datagrid.filter'),
+    $.get(url_for('sqleditor.filter'),
       function(data) {
         alertify.filterDialog(gettext('Data Filter - %s', queryToolTitle), data, validateUrl, preferences, okCallback)
           .resizeTo(pgBrowser.stdW.sm,pgBrowser.stdH.sm);
       }
     );
   } else {
-    datagrid.launch_grid(transId, gridUrl, false, queryToolTitle);
+    queryToolMod.launch(transId, gridUrl, false, queryToolTitle);
   }
 }
 
 
 export function retrieveNameSpaceName(parentData) {
-  if (parentData.schema !== undefined) {
+  if(!parentData) {
+    return null;
+  }
+  else if (parentData.schema !== undefined) {
     return parentData.schema.label;
   }
   else if (parentData.view !== undefined) {
@@ -83,8 +86,24 @@ export function retrieveNameSpaceName(parentData) {
   return '';
 }
 
+export function retrieveNodeName(parentData) {
+  if(!parentData) {
+    return null;
+  }
+  else if (parentData.table !== undefined) {
+    return parentData.table.label;
+  }
+  else if (parentData.view !== undefined) {
+    return parentData.view.label;
+  }
+  else if (parentData.catalog !== undefined) {
+    return parentData.catalog.label;
+  }
+  return '';
+}
+
 function generateUrl(trans_id, connectionData, nodeData, parentData) {
-  let url_endpoint = url_for('datagrid.panel', {
+  let url_endpoint = url_for('sqleditor.panel', {
     'trans_id': trans_id,
   });
 
@@ -108,7 +127,7 @@ function generateFilterValidateUrl(nodeData, parentData) {
     'obj_id': nodeData._id,
   };
 
-  return url_for('datagrid.filter_validate', url_params);
+  return url_for('sqleditor.filter_validate', url_params);
 }
 
 function initFilterDialog(alertify, pgBrowser) {
@@ -286,15 +305,15 @@ function hasSchemaOrCatalogOrViewInformation(parentData) {
     parentData.catalog !== undefined;
 }
 
-export function generateDatagridTitle(pgBrowser, aciTreeIdentifier, custom_title=null, backend_entity=null) {
+export function generateViewDataTitle(pgBrowser, treeIdentifier, custom_title=null, backend_entity=null) {
   var preferences = pgBrowser.get_preferences_for_module('browser');
   const parentData = pgBrowser.tree.getTreeNodeHierarchy(
-    aciTreeIdentifier
+    treeIdentifier
   );
 
   const namespaceName = retrieveNameSpaceName(parentData);
   const db_label = !_.isUndefined(backend_entity) && backend_entity != null && backend_entity.hasOwnProperty('db_name') ? backend_entity['db_name'] : getDatabaseLabel(parentData);
-  const node = pgBrowser.tree.findNodeByDomElement(aciTreeIdentifier);
+  const node = pgBrowser.tree.findNodeByDomElement(treeIdentifier);
 
   var dtg_title_placeholder = '';
   if(custom_title) {
@@ -310,7 +329,7 @@ export function generateDatagridTitle(pgBrowser, aciTreeIdentifier, custom_title
     'server': parentData.server.label,
     'schema': namespaceName,
     'table': node.getData().label,
-    'type': 'datagrid',
+    'type': 'view_data',
   };
   return generateTitle(dtg_title_placeholder, title_data);
 }
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
deleted file mode 100644
index 35c6aad9a..000000000
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ /dev/null
@@ -1,5189 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-/* eslint-disable */
-/* This is used to change publicPath of webpack at runtime for loading chunks */
-/* Do not add let, var, const to this variable */
-__webpack_public_path__ = window.resourceBasePath;
-/* eslint-enable */
-
-import {launchDataGrid} from 'tools/datagrid/static/js/show_query_tool';
-import {generateDatagridTitle} from 'tools/datagrid/static/js/show_data';
-import Notify from '../../../../static/js/helpers/Notifier';
-
-define('tools.querytool', [
-  'sources/gettext', 'sources/url_for', 'jquery', 'jquery.ui',
-  'jqueryui.position', 'underscore', 'pgadmin.alertifyjs',
-  'sources/pgadmin', 'backbone', 'bundled_codemirror', 'sources/utils',
-  'pgadmin.misc.explain',
-  'pgadmin.user_management.current_user',
-  'sources/selection/grid_selector',
-  'sources/selection/active_cell_capture',
-  'sources/selection/clipboard',
-  'sources/selection/copy_data',
-  'sources/selection/range_selection_helper',
-  'sources/slickgrid/event_handlers/handle_query_output_keyboard_event',
-  'sources/selection/xcell_selection_model',
-  'sources/selection/set_staged_rows',
-  'sources/sqleditor_utils',
-  'sources/sqleditor/execute_query',
-  'sources/sqleditor/query_tool_http_error_handler',
-  'sources/sqleditor/filter_dialog',
-  'sources/sqleditor/new_connection_dialog',
-  'sources/sqleditor/geometry_viewer',
-  'sources/sqleditor/history/history_collection.js',
-  'sources/sqleditor/history/query_history',
-  'sources/sqleditor/history/query_sources',
-  'sources/keyboard_shortcuts',
-  'sources/sqleditor/query_tool_actions',
-  'sources/sqleditor/query_tool_notifications',
-  'pgadmin.datagrid',
-  'sources/modify_animation',
-  'sources/sqleditor/calculate_query_run_time',
-  'sources/sqleditor/call_render_after_poll',
-  'sources/sqleditor/query_tool_preferences',
-  'sources/sqleditor/query_txn_status_constants',
-  'sources/csrf',
-  'tools/datagrid/static/js/datagrid_panel_title',
-  'sources/window',
-  'sources/is_native',
-  'sources/sqleditor/macro',
-  'pgadmin.authenticate.kerberos',
-  'sources/../bundle/slickgrid',
-  'pgadmin.file_manager',
-  'slick.pgadmin.formatters',
-  'slick.pgadmin.editors',
-  'slick.pgadmin.plugins/slick.autocolumnsize',
-  'pgadmin.browser',
-  'pgadmin.tools.user_management',
-], function(
-  gettext, url_for, $, jqueryui, jqueryui_position, _, alertify, pgAdmin, Backbone, codemirror, pgadminUtils,
-  pgExplain, current_user, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler, newConnectionHandler,
-  GeometryViewer, historyColl, queryHist, querySources,
-  keyboardShortcuts, queryToolActions, queryToolNotifications, Datagrid,
-  modifyAnimation, calculateQueryRunTime, callRenderAfterPoll, queryToolPref, queryTxnStatus, csrfToken, panelTitleFunc,
-  pgWindow, isNative, MacroHandler, Kerberos) {
-  /* Return back, this has been called more than once */
-  if (pgAdmin.SqlEditor)
-    return pgAdmin.SqlEditor;
-
-  // Some scripts do export their object in the window only.
-  // Generally the one, which do no have AMD support.
-  var wcDocker = window.wcDocker,
-    pgBrowser = pgAdmin.Browser,
-    CodeMirror = codemirror.default,
-    Slick = window.Slick,
-    HistoryCollection = historyColl.default,
-    QueryHistory = queryHist.default,
-    QuerySources = querySources.QuerySources;
-
-  csrfToken.setPGCSRFToken(pgAdmin.csrf_token_header, pgAdmin.csrf_token);
-
-  var is_query_running = false;
-
-  const EMPTY_DATA_OUTPUT_CONTENT = '<div role="status" class="pg-panel-message">' +
-    gettext('No data output. Execute a query to get output.') +
-  '</div>';
-
-  const EMPTY_EXPLAIN_CONTENT = '<div role="status" class="pg-panel-message">' +
-    gettext('Use Explain/Explain analyze button to generate the plan for a query. Alternatively, you can also execute "EXPLAIN (FORMAT JSON) [QUERY]".') +
-  '</div>';
-
-  // Defining Backbone view for the sql grid.
-  var SQLEditorView = Backbone.View.extend({
-    initialize: function(opts) {
-      this.$el = opts.el;
-      this.handler = opts.handler;
-      this.handler['col_size'] = {};
-      let browser = pgWindow.default.pgAdmin.Browser;
-      this.preferences = browser.get_preferences_for_module('sqleditor');
-      this.browser_preferences = browser.get_preferences_for_module('browser');
-      this.handler.preferences = this.preferences;
-      this.handler.browser_preferences = this.browser_preferences;
-      this.connIntervalId = null;
-      this.layout = opts.layout;
-      this.set_server_version(opts.server_ver);
-      this.trigger('pgadmin-sqleditor:view:initialised');
-      this.connection_list = [
-        {'server_group': null,'server': null, 'database': null, 'user': null, 'role': null, 'conn_title': '<' + gettext('New Connection') + '>'},
-      ];
-    },
-
-    // Bind all the events
-    events: {
-      'click #btn-show-query-tool': 'on_show_query_tool',
-      'click .btn-load-file': 'on_file_load',
-      'click #btn-save-file': 'on_save_file',
-      'click #btn-file-menu-save': 'on_save_file',
-      'click #btn-file-menu-save-as': 'on_save_as',
-      'click #btn-find': 'on_find',
-      'click #btn-find-menu-find': 'on_find',
-      'click #btn-find-menu-find-next': 'on_find_next',
-      'click #btn-find-menu-find-previous': 'on_find_previous',
-      'click #btn-find-menu-replace': 'on_replace',
-      'click #btn-find-menu-replace-all': 'on_replace_all',
-      'click #btn-find-menu-find-persistent': 'on_find_persistent',
-      'click #btn-find-menu-jump': 'on_jump',
-      'click #btn-delete-row': 'on_delete',
-      'click #btn-save-data': 'on_save_data',
-      'click #btn-filter': 'on_show_filter',
-      'click #btn-filter-menu': 'on_show_filter',
-      'click #btn-include-filter': 'on_include_filter',
-      'click #btn-exclude-filter': 'on_exclude_filter',
-      'click #btn-remove-filter': 'on_remove_filter',
-      'click #btn-cancel': 'on_cancel',
-      'click #btn-copy-row': 'on_copy_row',
-      'click #btn-copy-with-header': 'on_copy_row_with_header',
-      'click #btn-paste-row': 'on_paste_row',
-      'click #btn-flash': 'on_flash',
-      'click #btn-flash-menu': 'on_flash',
-      'click #btn-cancel-query': 'on_cancel_query',
-      'click #btn-save-results-to-file': 'on_download',
-      'click #btn-clear': 'on_clear',
-      'click #btn-auto-commit': 'on_auto_commit',
-      'click #btn-auto-rollback': 'on_auto_rollback',
-      'click #btn-clear-history': 'on_clear_history',
-      'click .noclose': 'do_not_close_menu',
-      'click #btn-explain': 'on_explain',
-      'click #btn-explain-analyze': 'on_explain_analyze',
-      'click #btn-explain-verbose': 'on_explain_verbose',
-      'click #btn-explain-costs': 'on_explain_costs',
-      'click #btn-explain-buffers': 'on_explain_buffers',
-      'click #btn-explain-timing': 'on_explain_timing',
-      'click #btn-explain-summary': 'on_explain_summary',
-      'click #btn-explain-settings': 'on_explain_settings',
-      'change .limit': 'on_limit_change',
-      'keydown': 'keyAction',
-      // Comment options
-      'click #btn-comment-code': 'on_toggle_comment_block_code',
-      'click #btn-toggle-comment-block': 'on_toggle_comment_block_code',
-      'click #btn-comment-line': 'on_comment_line_code',
-      'click #btn-uncomment-line': 'on_uncomment_line_code',
-      // Indentation options
-      'click #btn-indent-code': 'on_indent_code',
-      'click #btn-unindent-code': 'on_unindent_code',
-      // Format
-      'click #btn-format-sql': 'on_format_sql',
-      // Transaction control
-      'click #btn-commit': 'on_commit_transaction',
-      'click #btn-rollback': 'on_rollback_transaction',
-      // Manage Macros
-      'click #btn-manage-macros': 'on_manage_macros',
-      'click .btn-macro': 'on_execute_macro',
-    },
-
-    render_connection: function(data_list) {
-      if(this.handler.is_query_tool) {
-        var dropdownElement = document.getElementById('connections-list');
-        dropdownElement.innerHTML = '';
-        data_list.forEach((option, index) => {
-          var opt = '';
-          if ('is_selected' in option && option['is_selected']) {
-            opt = '<li class="connection-list-item selected-connection" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ _.escape(option.conn_title) +'</a></li>';
-          } else {
-            opt = '<li class="connection-list-item" data-index='+ index +'><a class="dropdown-item" href="#" tabindex="0">'+ _.escape(option.conn_title) +'</a></li>';
-          }
-          $('#connections-list').append(opt);
-        });
-        var self = this;
-        $('.connection-list-item').click(function() {
-          self.get_connection_data(this);
-        });
-      } else {
-        $('.conn-info-dd').hide();
-        $('.connection-data').css({pointerEvents: 'none', cursor: 'arrow'});
-      }
-    },
-
-    get_connection_data: function(event){
-      var index = $(event).attr('data-index');
-      var connection_details = this.connection_list[index];
-      if(connection_details.server_group) {
-        this.on_change_connection(connection_details);
-      } else {
-        this.on_new_connection();
-      }
-
-    },
-
-    reflectPreferences: function() {
-      let self = this,
-        browser = pgWindow.default.pgAdmin.Browser,
-        browser_preferences = browser.get_preferences_for_module('browser');
-
-      /* pgBrowser is different obj from pgWindow.default.pgAdmin.Browser
-       * Make sure to get only the latest update. Older versions will be discarded
-       * if function is called by older events.
-       * This works for new tab sql editor also as it polls if latest version available
-       * This is required because sql editor can update preferences directly
-       */
-      if(pgBrowser.preference_version() < browser.preference_version()){
-        pgBrowser.preference_version(browser.preference_version());
-        self.preferences = browser.get_preferences_for_module('sqleditor');
-        self.preferences.show_query_tool = browser_preferences.sub_menu_query_tool;
-        self.handler.preferences = self.preferences;
-        queryToolPref.updateUIPreferences(self);
-      }
-    },
-
-    buildDefaultLayout: function(docker) {
-      let sql_panel_obj = docker.addPanel('sql_panel', wcDocker.DOCK.TOP);
-
-      docker.addPanel('scratch', wcDocker.DOCK.RIGHT, sql_panel_obj);
-      docker.addPanel('history', wcDocker.DOCK.STACKED, sql_panel_obj);
-
-      let data_output_panel = docker.addPanel('data_output', wcDocker.DOCK.BOTTOM);
-      docker.addPanel('explain', wcDocker.DOCK.STACKED, data_output_panel);
-      docker.addPanel('messages', wcDocker.DOCK.STACKED, data_output_panel);
-      docker.addPanel('notifications', wcDocker.DOCK.STACKED, data_output_panel);
-    },
-
-    set_server_version: function(server_ver) {
-      let self = this;
-      self.server_ver = server_ver;
-
-      this.$el.find('*[data-min-ver]').map(function() {
-        let minVer = 0,
-          ele = $(this);
-        minVer = parseInt(ele.attr('data-min-ver'));
-        if(minVer > self.server_ver) {
-          ele.addClass('d-none');
-        } else {
-          ele.removeClass('d-none');
-        }
-      });
-    },
-
-    set_editor_title: function(title) {
-      this.$el.find('.editor-title').text(_.unescape(title));
-      this.render_connection(this.connection_list);
-    },
-
-    // This function is used to render the template.
-    render: function() {
-      var self = this;
-
-      // Updates connection status flag
-      self.gain_focus = function() {
-        setTimeout(function() {
-          SqlEditorUtils.updateConnectionStatusFlag('visible');
-        }, 100);
-      };
-      // Updates connection status flag
-      self.lost_focus = function() {
-        setTimeout(function() {
-          SqlEditorUtils.updateConnectionStatusFlag('hidden');
-        }, 100);
-      };
-
-      // Create main wcDocker instance
-      self.docker = new wcDocker(
-        '#editor-panel', {
-          allowContextMenu: true,
-          allowCollapse: false,
-          loadingClass: 'pg-sp-icon',
-          themePath: url_for('static', {
-            'filename': 'css',
-          }),
-          theme: 'webcabin.overrides.css',
-        }
-      );
-
-      // Create the panels
-      var sql_panel = new pgAdmin.Browser.Panel({
-        name: 'sql_panel',
-        title: gettext('Query Editor'),
-        width: '75%',
-        height: '33%',
-        isCloseable: false,
-        isPrivate: true,
-      });
-
-      var data_output = new pgAdmin.Browser.Panel({
-        name: 'data_output',
-        title: gettext('Data Output'),
-        width: '100%',
-        height: '100%',
-        isCloseable: false,
-        isPrivate: true,
-        extraClasses: 'hide-vertical-scrollbar',
-        content: `<div class="data-output-container">
-          <div id="fetching_data" class="pg-sp-container sql-editor-busy-fetching">
-              <div class="pg-sp-content">
-                  <div class="row">
-                      <div class="col-12 pg-sp-icon sql-editor-busy-icon"></div>
-                  </div>
-                  <div class="row"><div class="col-12 pg-sp-text sql-editor-busy-text">${gettext('Loading...')}</div></div>
-              </div>
-          </div>
-          <div id ="datagrid" class="sql-editor-grid-container text-12" tabindex="0">${EMPTY_DATA_OUTPUT_CONTENT}</div>
-        </div>`,
-      });
-
-      var explain = new pgAdmin.Browser.Panel({
-        name: 'explain',
-        title: gettext('Explain'),
-        width: '100%',
-        height: '100%',
-        isCloseable: false,
-        isPrivate: true,
-        content: `<div class="sql-editor-explain pg-el-container" tabindex="0">${EMPTY_EXPLAIN_CONTENT}</div>`,
-      });
-
-      var messages = new pgAdmin.Browser.Panel({
-        name: 'messages',
-        title: gettext('Messages'),
-        width: '100%',
-        height: '100%',
-        isCloseable: false,
-        isPrivate: true,
-        content: '<div role="status" class="sql-editor-message" tabindex="0"></div>',
-      });
-
-      var history = new pgAdmin.Browser.Panel({
-        name: 'history',
-        title: gettext('Query History'),
-        width: '100%',
-        height: '33%',
-        isCloseable: false,
-        isPrivate: true,
-        content: '<div id ="history_grid" class="sql-editor-history-container" tabindex="0"></div>',
-      });
-
-      var scratch = new pgAdmin.Browser.Panel({
-        name: 'scratch',
-        title: gettext('Scratch Pad'),
-        width: '25%',
-        height: '33%',
-        isCloseable: true,
-        isPrivate: false,
-        content: '<div class="sql-scratch"><textarea wrap="off" tabindex="0"></textarea></div>',
-      });
-
-      var notifications = new pgAdmin.Browser.Panel({
-        name: 'notifications',
-        title: gettext('Notifications'),
-        width: '100%',
-        height: '100%',
-        isCloseable: false,
-        isPrivate: true,
-        content: '<div id ="notification_grid" class="sql-editor-notifications" tabindex="0"></div>',
-      });
-
-      var geometry_viewer = new pgAdmin.Browser.Panel({
-        name: 'geometry_viewer',
-        title: gettext('Geometry Viewer'),
-        width: '100%',
-        height: '100%',
-        isCloseable: true,
-        isPrivate: true,
-        isLayoutMember: false,
-        content: '<div id ="geometry_viewer_panel" class="sql-editor-geometry-viewer" tabindex="0"></div>',
-      });
-
-      // Load all the created panels
-      sql_panel.load(self.docker);
-      data_output.load(self.docker);
-      explain.load(self.docker);
-      messages.load(self.docker);
-      history.load(self.docker);
-      scratch.load(self.docker);
-      notifications.load(self.docker);
-      geometry_viewer.load(self.docker);
-
-      // restore the layout if present else fallback to buildDefaultLayout
-      pgBrowser.restore_layout(self.docker, this.layout, this.buildDefaultLayout.bind(this));
-
-      self.docker.on(wcDocker.EVENT.LAYOUT_CHANGED, function() {
-        pgBrowser.save_current_layout('SQLEditor/Layout', self.docker);
-      });
-
-      self.sql_panel_obj = self.docker.findPanels('sql_panel')[0];
-      self.history_panel = self.docker.findPanels('history')[0];
-      self.data_output_panel = self.docker.findPanels('data_output')[0];
-      self.explain_panel = self.docker.findPanels('explain')[0];
-      self.messages_panel = self.docker.findPanels('messages')[0];
-      self.notifications_panel = self.docker.findPanels('notifications')[0];
-
-      if (_.isUndefined(self.sql_panel_obj) || _.isUndefined(self.history_panel) ||
-       _.isUndefined(self.data_output_panel) || _.isUndefined(self.explain_panel) ||
-       _.isUndefined(self.messages_panel) || _.isUndefined(self.notifications_panel)) {
-        Notify.alert(
-          gettext('Panel Loading Error'),
-          gettext('Something went wrong while loading the panels.'
-          + ' Please make sure to reset the layout (File > Reset Layout) for the better user experience.')
-        );
-      }
-
-      // Refresh Code mirror on SQL panel resize to
-      // display its value properly
-      self.sql_panel_obj.on(wcDocker.EVENT.RESIZE_ENDED, function() {
-        setTimeout(function() {
-          if (self && self.query_tool_obj) {
-            self.query_tool_obj.refresh();
-          }
-        }, 200);
-      });
-
-      self.render_history_grid();
-      pgBrowser.Events.on('pgadmin:query_tool:connected:'+self.handler.transId, ()=>{
-        self.fetch_query_history();
-      });
-
-      self.handler.on('pgadmin-sqleditor:check_synchronous_db_name_change', (result)=>{
-        self.handler.check_db_name_change(result);
-      });
-
-      queryToolNotifications.renderNotificationsGrid(self.notifications_panel);
-
-      var text_container = $('<textarea id="sql_query_tool" tabindex="-1"></textarea>');
-      var output_container = $('<label for="sql_query_tool" class="sr-only">SQL Editor</label><div id="output-panel" tabindex="0"></div>').append(text_container);
-      self.sql_panel_obj.$container.find('.pg-panel-content').append(output_container);
-
-      self.query_tool_obj = CodeMirror.fromTextArea(text_container.get(0), {
-        tabindex: '0',
-        lineNumbers: true,
-        styleSelectedText: true,
-        mode: 'text/x-pgsql',
-        foldOptions: {
-          widget: '\u2026',
-        },
-        foldGutter: true,
-        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
-        extraKeys: pgBrowser.editor_shortcut_keys,
-        scrollbarStyle: 'simple',
-        dragDrop: false,
-        screenReaderLabel: gettext('SQL editor'),
-      });
-
-      if(self.handler.is_query_tool) {
-        self.query_tool_obj.setOption('dragDrop', true);
-        self.query_tool_obj.on('drop', (editor, e) => {
-          var dropDetails = null;
-          try {
-            dropDetails = JSON.parse(e.dataTransfer.getData('text'));
-
-            /* Stop firefox from redirecting */
-
-            if(e.preventDefault) {
-              e.preventDefault();
-            }
-            if (e.stopPropagation) {
-              e.stopPropagation();
-            }
-          } catch(error) {
-            /* if parsing fails, it must be the drag internal of codemirror text */
-            return;
-          }
-
-          var cursor = editor.coordsChar({
-            left: e.x,
-            top: e.y,
-          });
-          editor.replaceRange(dropDetails.text, cursor);
-          editor.focus();
-          editor.setSelection({
-            ...cursor,
-            ch: cursor.ch + dropDetails.cur.from,
-          },{
-            ...cursor,
-            ch: cursor.ch +dropDetails.cur.to,
-          });
-        });
-      }
-
-      pgBrowser.Events.on('pgadmin:query_tool:sql_panel:focus', ()=>{
-        self.query_tool_obj.focus();
-      });
-
-      pgBrowser.Events.on('pgadmin:query_tool:explain:focus', ()=>{
-        setTimeout(function () {
-          $('.sql-editor-explain .backform-tab .nav-link.active').focus();
-        }, 200);
-      });
-
-      // Prevent browser from opening the drag file.
-      $('#datagrid').bind('dragover drop', function (event) {
-        event.stopPropagation();
-        event.preventDefault();
-      });
-
-      var open_new_tab = self.browser_preferences.new_browser_tab_open;
-      if (_.isNull(open_new_tab) || _.isUndefined(open_new_tab) || !open_new_tab.includes('qt')) {
-        // Listen on the panel closed event and notify user to save modifications.
-        _.each(pgWindow.default.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
-          if (p.trans_id == self.transId) {
-            self.currentPanel = p;
-            p.on(wcDocker.EVENT.CLOSING, function() {
-              return self.handler.check_needed_confirmations_before_closing_panel(true);
-            });
-
-            // Set focus on query tool of active panel
-            p.on(wcDocker.EVENT.GAIN_FOCUS, function() {
-              var $container = $(this.$container),
-                transId = self.handler.transId;
-
-              if (!$container.hasClass('wcPanelTabContentHidden')) {
-
-                let modal_list = ['fileSelectionDlg', 'createModeDlg',
-                  'confirm', 'alert', 'confirmSave', 'newConnectionDialog',
-                  'macroDialog'];
-
-                /* check the modals inside the sqleditor are open, if not,
-                focus on the editor instead. */
-                if(!SqlEditorUtils.isModalOpen(modal_list)) {
-                  setTimeout(function () {
-                    self.handler.gridView.query_tool_obj.focus();
-                  }, 200);
-
-                  // Trigger an event to update connection status flag
-                  pgBrowser.Events.trigger(
-                    'pgadmin:query_tool:panel:gain_focus:' + transId
-                  );
-                }
-              }
-            });
-
-            // When any query tool panel lost it focus then
-            p.on(wcDocker.EVENT.LOST_FOCUS, function () {
-              var $container = $(this.$container),
-                transId = self.handler.transId;
-              // Trigger an event to update connection status flag
-              if ($container.hasClass('wcPanelTabContentHidden')) {
-                pgBrowser.Events.trigger(
-                  'pgadmin:query_tool:panel:lost_focus:' + transId
-                );
-              }
-            });
-          }
-        });
-
-        // Code to update connection status polling flag
-        pgBrowser.Events.on(
-          'pgadmin:query_tool:panel:gain_focus:' + self.handler.transId,
-          self.gain_focus, self
-        );
-        pgBrowser.Events.on(
-          'pgadmin:query_tool:panel:lost_focus:' + self.handler.transId,
-          self.lost_focus, self
-        );
-      }
-
-      // set focus on query tool once loaded
-      setTimeout(function() {
-        self.query_tool_obj.focus();
-      }, 500);
-
-      /* We have override/register the hint function of CodeMirror
-       * to provide our own hint logic.
-       */
-      CodeMirror.registerHelper('hint', 'sql', function(editor) {
-        var data = [],
-          doc = editor.getDoc(),
-          cur = doc.getCursor(),
-          // Get the current cursor position
-          current_cur = cur.ch,
-          // function context
-          ctx = {
-            editor: editor,
-            // URL for auto-complete
-            url: url_for('sqleditor.autocomplete', {
-              'trans_id': self.transId,
-            }),
-            data: data,
-            // Get the line number in the cursor position
-            current_line: cur.line,
-            /*
-             * Render function for hint to add our own class
-             * and icon as per the object type.
-             */
-            hint_render: function(elt, data_arg, cur_arg) {
-              var el = document.createElement('span');
-
-              switch (cur_arg.type) {
-              case 'database':
-                el.className = 'sqleditor-hint pg-icon-' + cur_arg.type;
-                break;
-              case 'datatype':
-                el.className = 'sqleditor-hint icon-type';
-                break;
-              case 'keyword':
-                el.className = 'fa fa-key';
-                break;
-              case 'table alias':
-                el.className = 'fa fa-at';
-                break;
-              default:
-                el.className = 'sqleditor-hint icon-' + cur_arg.type;
-              }
-
-              el.appendChild(document.createTextNode(cur_arg.text));
-              elt.appendChild(el);
-            },
-          };
-
-        data.push(doc.getValue());
-        // Get the text from start to the current cursor position.
-        data.push(
-          doc.getRange({
-            line: 0,
-            ch: 0,
-          }, {
-            line: ctx.current_line,
-            ch: current_cur,
-          })
-        );
-
-        return {
-          then: function(cb) {
-            var self_local = this;
-            // Make ajax call to find the autocomplete data
-            $.ajax({
-              url: self_local.url,
-              method: 'POST',
-              contentType: 'application/json',
-              data: JSON.stringify(self_local.data),
-            })
-              .done(function(res) {
-                var result = [];
-
-                _.each(res.data.result, function(obj, key) {
-                  result.push({
-                    text: key,
-                    type: obj.object_type,
-                    render: self_local.hint_render,
-                  });
-                });
-
-                // Sort function to sort the suggestion's alphabetically.
-                result.sort(function(a, b) {
-                  var textA = a.text.toLowerCase(),
-                    textB = b.text.toLowerCase();
-                  if (textA < textB) //sort string ascending
-                    return -1;
-                  if (textA > textB)
-                    return 1;
-                  return 0; //default return value (no sorting)
-                });
-
-                /*
-               * Below logic find the start and end point
-               * to replace the selected auto complete suggestion.
-               */
-                var token = self_local.editor.getTokenAt(cur),
-                  start, end, search;
-                if (token.end > cur.ch) {
-                  token.end = cur.ch;
-                  token.string = token.string.slice(0, cur.ch - token.start);
-                }
-
-                if (token.string.match(/^[.`\w@]\w*$/)) {
-                  search = token.string;
-                  start = token.start;
-                  end = token.end;
-                } else {
-                  start = end = cur.ch;
-                  search = '';
-                }
-
-                /*
-               * Added 1 in the start position if search string
-               * started with "." or "`" else auto complete of code mirror
-               * will remove the "." when user select any suggestion.
-               */
-                if (search.charAt(0) == '.' || search.charAt(0) == '``')
-                  start += 1;
-
-                cb({
-                  list: result,
-                  from: {
-                    line: self_local.current_line,
-                    ch: start,
-                  },
-                  to: {
-                    line: self_local.current_line,
-                    ch: end,
-                  },
-                });
-              })
-              .fail(function(e) {
-                return httpErrorHandler.handleLoginRequiredAndTransactionRequired(
-                  pgAdmin, self_local, e, null, [], false
-                );
-              });
-          }.bind(ctx),
-        };
-      });
-
-      /* If the screen width is small and we hover over the Explain Options,
-        * the submenu goes behind the screen on the right side.
-        * Below logic will make it appear on the left.
-        */
-      $('.dropdown-submenu').on('mouseenter',function() {
-        var menu = $(this).find('ul.dropdown-menu');
-        var menupos = $(menu).offset();
-
-        if (menupos.left + menu.width() > $(window).width()) {
-          var newpos = -$(menu).width();
-          menu.css('left',newpos);
-        }
-      }).on('mouseleave', function() {
-        var menu = $(this).find('ul.dropdown-menu');
-        menu.css('left','');
-      });
-
-      self.reflectPreferences();
-
-      /* Set Auto-commit and auto-rollback on query editor */
-      if (self.preferences.auto_commit) {
-        $('.auto-commit').removeClass('visibility-hidden');
-      }
-      else {
-        $('.auto-commit').addClass('visibility-hidden');
-      }
-      if (self.preferences.auto_rollback) {
-        $('.auto-rollback').removeClass('visibility-hidden');
-      }
-      else {
-        $('.auto-rollback').addClass('visibility-hidden');
-      }
-
-      /* Register for preference changed event broadcasted in parent
-       * to reload the shorcuts. As sqleditor is in iFrame of wcDocker
-       * window parent is referred
-       */
-      pgWindow.default.pgAdmin.Browser.onPreferencesChange('sqleditor', function() {
-        self.reflectPreferences();
-      });
-
-      /* If sql editor is in a new tab, event fired is not available
-       * instead, a poller is set up who will check
-       */
-      var open_new_tab_qt = self.browser_preferences.new_browser_tab_open;
-      if(open_new_tab_qt && open_new_tab_qt.includes('qt')) {
-        pgBrowser.bind_beforeunload();
-        setInterval(()=>{
-          if(pgWindow.default.pgAdmin) {
-            self.reflectPreferences();
-          }
-        }, 1000);
-      }
-
-      /* Register to log the activity */
-      pgBrowser.register_to_activity_listener(document, ()=>{
-        Notify.alert(gettext('Timeout'), gettext('Your session has timed out due to inactivity. Please close the window and login again.'));
-      });
-
-      self.render_connection(self.connection_list);
-    },
-
-    /* Regarding SlickGrid usage in render_grid function.
-
-     SlickGrid Plugins:
-     ------------------
-     1) Slick.AutoTooltips
-     - This plugin is useful for displaying cell data as tooltip when
-     user hover mouse on cell if data is large
-     2) Slick.CheckboxSelectColumn
-     - This plugin is useful for selecting rows using checkbox
-     3) RowSelectionModel
-     - This plugin is needed by CheckboxSelectColumn plugin to select rows
-     4) Slick.HeaderButtons
-     - This plugin is useful for add buttons in column header
-
-     Grid Options:
-     -------------
-     1) editable
-     - This option allow us to make grid editable
-     2) enableAddRow
-     - This option allow us to add new rows at the end of grid
-     3) enableCellNavigation
-     - This option allow us to navigate cells using keyboard
-     4) enableColumnReorder
-     - This option allow us to record column
-     5) asyncEditorLoading
-     - This option allow us to open editor async
-     6) autoEdit
-     - This option allow us to enter in edit mode directly when user clicks on it
-     otherwise user have to double click or manually press enter on cell to go
-     in cell edit mode
-
-     Handling of data:
-     -----------------
-     We are doing data handling manually,what user adds/updates/deletes etc
-     we will use `data_store` object to store everything user does within grid data
-
-     - updated:
-     This will hold all the data which user updates in grid
-     - added:
-     This will hold all the new row(s) data which user adds in grid
-     - staged_rows:
-     This will hold all the data which user copies/pastes/deletes in grid
-     - deleted:
-     This will hold all the data which user deletes in grid
-
-     Events handling:
-     ----------------
-     1) onCellChange
-     - We are using this event to listen to changes on individual cell.
-     2) onAddNewRow
-     - We are using this event to listen to new row adding functionality.
-     3) onSelectedRangesChanged
-     - We are using this event to listen when user selects rows for copy/delete operation.
-     4) onBeforeEditCell
-     - We are using this event to save the data before users modified them
-     5) onKeyDown
-     - We are using this event for Copy operation on grid
-     */
-
-    // This function is responsible to create and render the SlickGrid.
-    render_grid: function(collection, columns, is_editable, client_primary_key, rows_affected) {
-      var self = this;
-
-      self.handler.numberOfModifiedCells = 0;
-
-      self.handler.reset_data_store();
-
-      // keep track of newly added rows
-      self.handler.rows_to_disable = new Array();
-      // Temporarily hold new rows added
-      self.handler.temp_new_rows = new Array();
-
-      // To store primary keys before they gets changed
-      self.handler.primary_keys_data = {};
-
-      self.client_primary_key = client_primary_key;
-
-      self.client_primary_key_counter = 0;
-
-      // Remove any existing grid first
-      if (self.handler.slickgrid) {
-        self.handler.slickgrid.destroy();
-      }
-
-      if (!_.isArray(collection) || !_.size(collection)) {
-        collection = [];
-      }
-
-      var grid_columns = [],
-        table_name;
-      var column_size = self.handler['col_size'],
-        query = self.handler.query,
-        // Extract table name from query
-        table_list = query.match(/select.*from\s+\w+\.*(\w+)/i);
-
-      if (!table_list) {
-        table_name = SqlEditorUtils.getHash(query);
-      } else {
-        table_name = table_list[1];
-      }
-
-      self.handler['table_name'] = table_name;
-      column_size[table_name] = column_size[table_name] || {};
-
-      // Keep track of column_data_auto_resize preferences value
-      if (_.isUndefined(self.auto_resize_column_based_on_data) || self.auto_resize_column_based_on_data !== self.preferences.column_data_auto_resize) {
-        self.auto_resize_column_based_on_data = self.preferences.column_data_auto_resize;
-        column_size[table_name] = {};
-      }
-
-      // Keep track of column_data_max_width
-      self.max_width_changed = false;
-      if (_.isUndefined(self.old_column_data_max_width) || self.old_column_data_max_width != self.preferences.column_data_max_width) {
-        self.old_column_data_max_width = self.preferences.column_data_max_width;
-        self.max_width_changed = true;
-      }
-
-
-      _.each(columns, function(c) {
-        c.display_name = _.escape(c.display_name);
-        c.column_type = _.escape(c.column_type);
-
-        // If the keys have name from existing JS keywords then it may
-        // create problem. eg - contructor, hasOwnProperty.
-        // nonative_field is field with extra double quotes
-        var options = {
-          id:  _.escape(c.name),
-          pos: c.pos,
-          field: c.name,
-          nonative_field: `"${c.name}"`,
-          name: c.label,
-          display_name: c.display_name,
-          column_type: c.column_type,
-          column_type_internal: c.column_type_internal,
-          cell: c.cell,
-          not_null: c.not_null,
-          has_default_val: c.has_default_val,
-          is_array: c.is_array,
-          can_edit: c.can_edit,
-        };
-
-        // Get the columns width based on longer string among data type or
-        // column name.
-        var column_type = c.column_type.trim();
-        var label = c.name.length > column_type.length ? _.escape(c.display_name) : column_type;
-        var iconWidth = 0;
-        // increase width to add 'view' button
-        if (c.cell == 'geometry' || c.cell == 'geography') {
-          iconWidth += 28;
-        }
-        // Increase width for editable/read-only icon
-        if(!_.isUndefined(c.can_edit)) {
-          iconWidth += 12;
-        }
-
-        if (_.isUndefined(column_size[table_name][options.nonative_field])) {
-          /* If column_data_auto_resize is 'by_data' then for the first time set
-           * the addWidth parameter to iconWidth and if it is 'by_name' then
-           * calculate width based on longer string among data type or
-           * column name.
-           */
-          if (self.preferences.column_data_auto_resize === 'by_data') {
-            options['addWidth'] = iconWidth;
-            options['width'] = NaN;
-          } else {
-            options['width'] = SqlEditorUtils.calculateColumnWidth(label);
-            options['width'] += iconWidth;
-            column_size[table_name][c.nonative_field] = options['width'];
-          }
-        } else {
-          options['width'] = column_size[table_name][options.nonative_field];
-        }
-
-        // If grid is editable then add editor else make it readonly
-        if (c.cell == 'oid' && c.name == 'oid') {
-          options['editor'] = null;
-        } else if (c.cell == 'Json') {
-          options['editor'] = c.can_edit ? Slick.Editors.JsonText :
-            Slick.Editors.ReadOnlyJsonText;
-          options['formatter'] = Slick.Formatters.JsonString;
-        } else if (c.cell == 'number' || c.cell == 'oid' ||
-          $.inArray(c.type, ['xid', 'real']) !== -1
-        ) {
-          options['editor'] = c.can_edit ? Slick.Editors.CustomNumber :
-            Slick.Editors.ReadOnlyText;
-          options['formatter'] = Slick.Formatters.Numbers;
-        } else if (c.cell == 'boolean') {
-          options['editor'] = c.can_edit ? Slick.Editors.Checkbox :
-            Slick.Editors.ReadOnlyCheckbox;
-          options['formatter'] = Slick.Formatters.Checkmark;
-        } else if (c.cell == 'binary') {
-          // We do not support editing binary data in SQL editor and data grid.
-          options['formatter'] = Slick.Formatters.Binary;
-        } else if (c.cell == 'geometry' || c.cell == 'geography') {
-          // increase width to add 'view' button
-          options['can_edit'] = false;
-        } else {
-          options['editor'] = c.can_edit ? Slick.Editors.pgText :
-            Slick.Editors.ReadOnlypgText;
-          options['formatter'] = Slick.Formatters.Text;
-        }
-
-        if(!_.isUndefined(c.can_edit)) {
-          let tooltip = '';
-          if(c.can_edit)
-            tooltip = gettext('Editable column');
-          else
-            tooltip = gettext('Read-only column');
-
-          options['toolTip'] = tooltip;
-        }
-
-        grid_columns.push(options);
-      });
-
-      var gridSelector = new GridSelector();
-      grid_columns = self.grid_columns = gridSelector.getColumnDefinitions(grid_columns);
-
-      _.each(grid_columns, function (c) {
-        // Add 'view' button in geometry and geography type column headers
-        if (c.column_type_internal == 'geometry' || c.column_type_internal == 'geography') {
-          GeometryViewer.add_header_button(c);
-        }
-        // Add editable/read-only icon to columns
-        if (!_.isUndefined(c.can_edit)) {
-          SqlEditorUtils.addEditableIcon(c, c.can_edit);
-        }
-      });
-
-      if (rows_affected) {
-        // calculate with for header row column.
-        grid_columns[0]['width'] = SqlEditorUtils.calculateColumnWidth(rows_affected);
-      }
-
-      var grid_options = {
-        editable: true,
-        enableAddRow: is_editable,
-        enableCellNavigation: true,
-        enableColumnReorder: false,
-        asyncEditorLoading: false,
-        autoEdit: false,
-      };
-
-      var $data_grid = $('#datagrid');
-      // Calculate height based on panel size at runtime & set it
-      var grid_height = $(this.data_output_panel.$container.parent().parent()).height() - 35;
-      $data_grid.height(grid_height);
-
-      var dataView = self.dataView = new Slick.Data.DataView(),
-        grid = self.grid = new Slick.Grid($data_grid, dataView, grid_columns, grid_options);
-
-      // Add-on function which allow us to identify the faulty row after insert/update
-      // and apply css accordingly
-
-      dataView.getItemMetadata = function(rows) {
-        var cssClass = '',
-          data_store = self.handler.data_store;
-
-        if (_.has(self.handler, 'data_store')) {
-          if (rows in data_store.added_index &&
-            data_store.added_index[rows] in data_store.added) {
-            cssClass = 'new_row';
-            if (data_store.added[data_store.added_index[rows]].err) {
-              cssClass += ' error';
-            }
-          } else if (rows in data_store.updated_index && rows in data_store.updated) {
-            cssClass = 'updated_row';
-            if (data_store.updated[data_store.updated_index[rows]].err) {
-              cssClass += ' error';
-            }
-          }
-        }
-
-        // Disable rows having default values
-        if (!_.isUndefined(self.handler.rows_to_disable) &&
-          self.handler.rows_to_disable.length > 0 &&
-          _.indexOf(self.handler.rows_to_disable, rows) !== -1) {
-          cssClass += ' disabled_row';
-        }
-
-        return {
-          'cssClasses': cssClass,
-        };
-      };
-
-      grid.registerPlugin(new Slick.AutoTooltips({
-        enableForHeaderCells: false,
-      }));
-      grid.registerPlugin(new ActiveCellCapture());
-      grid.setSelectionModel(new XCellSelectionModel());
-      grid.registerPlugin(gridSelector);
-      grid.registerPlugin(new Slick.AutoColumnSize());
-      var headerButtonsPlugin = new Slick.Plugins.HeaderButtons();
-      headerButtonsPlugin.onCommand.subscribe(function (e, args) {
-        let command = args.command;
-        if (command === 'view-geometries') {
-          let cols = args.grid.getColumns();
-          let columnIndex = cols.indexOf(args.column);
-          let selectedRows = args.grid.getSelectedRows();
-          if (selectedRows.length === 0) {
-            // if no rows are selected, load and render all the rows
-            if (self.handler.has_more_rows) {
-              self.fetch_next_all(function () {
-                // trigger onGridSelectAll manually with new event data.
-                gridSelector.onGridSelectAll.notify(args, new Slick.EventData());
-                let items = args.grid.getData().getItems();
-                GeometryViewer.render_geometries(self.handler, items, cols, columnIndex);
-              });
-            } else {
-              gridSelector.onGridSelectAll.notify(args, new Slick.EventData());
-              let items = args.grid.getData().getItems();
-              GeometryViewer.render_geometries(self.handler, items, cols, columnIndex);
-            }
-          } else {
-            // render selected rows
-            let items = args.grid.getData().getItems();
-            let selectedItems = _.map(selectedRows, function (row) {
-              return items[row];
-            });
-            GeometryViewer.render_geometries(self.handler, selectedItems, cols, columnIndex);
-          }
-        }
-      });
-      grid.registerPlugin(headerButtonsPlugin);
-
-      var editor_data = {
-        keys: (_.isEmpty(self.handler.primary_keys) && self.handler.has_oids) ? self.handler.oids : self.handler.primary_keys,
-        vals: collection,
-        columns: columns,
-        grid: grid,
-        selection: grid.getSelectionModel(),
-        editor: self,
-        client_primary_key: self.client_primary_key,
-        has_oids: self.handler.has_oids,
-      };
-
-      self.handler.slickgrid = grid;
-      // Add listener on data-grid table scroll.
-      self.handler.slickgrid.onScroll.subscribe(function() {
-        // Mark selected rows cells as selected.
-        setTimeout(() => {
-          // Can't use setSelectedRows as we are using cellSelectionModel.
-          var cellSelectionModel = self.handler.gridView.grid.getSelectionModel();
-          var ranges = cellSelectionModel.getSelectedRanges();
-
-          if (ranges.length > 1) {
-            // Set selected rows cell as selected.
-            cellSelectionModel.setSelectedRanges(ranges);
-          }
-        }, 100);
-
-        if(Object.keys(self.handler.data_store.deleted).length > 0) {
-          setTimeout(() => {
-            $(self.handler.gridView.grid.getCanvasNode()).find('div.selected').removeClass('strikeout');
-            $(self.handler.gridView.grid.getCanvasNode()).find('div.selected').addClass('strikeout');
-          }, 100);
-        }
-      });
-      self.handler.slickgrid.CSVOptions = {
-        quoting: self.preferences.results_grid_quoting,
-        quote_char: self.preferences.results_grid_quote_char,
-        field_separator: self.preferences.results_grid_field_separator,
-      };
-
-      // Listener function to watch selected rows from grid
-      if (editor_data.selection) {
-        editor_data.selection.onSelectedRangesChanged.subscribe(
-          setStagedRows.bind(editor_data));
-      }
-
-      grid.onColumnsResized.subscribe(function() {
-        var cols = this.getColumns();
-        _.each(cols, function(col) {
-          var col_size = self.handler['col_size'];
-          col_size[self.handler['table_name']][col['nonative_field']] = col['width'];
-        });
-      }.bind(grid));
-
-      gridSelector.onBeforeGridSelectAll.subscribe(function(e, args) {
-        if (self.handler.has_more_rows) {
-          // this will prevent selection un-till we load all data
-          e.stopImmediatePropagation();
-          self.fetch_next_all(function() {
-            // since we've stopped event propagation we need to
-            // trigger onGridSelectAll manually with new event data.
-            gridSelector.onGridSelectAll.notify(args, new Slick.EventData());
-          });
-        }
-      });
-
-      gridSelector.onBeforeGridColumnSelectAll.subscribe(function(e, args) {
-        if (self.handler.has_more_rows) {
-          // this will prevent selection un-till we load all data
-          e.stopImmediatePropagation();
-          self.fetch_next_all(function() {
-            // since we've stopped event propagation we need to
-            // trigger onGridColumnSelectAll manually with new event data.
-            gridSelector.onGridColumnSelectAll.notify(args, new Slick.EventData());
-          });
-        }
-      });
-
-      // listen for row count change.
-      dataView.onRowCountChanged.subscribe(function() {
-        grid.updateRowCount();
-        grid.render();
-      });
-
-      // listen for rows change.
-      dataView.onRowsChanged.subscribe(function(e, args) {
-        grid.invalidateRows(args.rows);
-        grid.render();
-        // Resize all columns if column_data_auto_resize is 'by_data'.
-        if (self.preferences.column_data_auto_resize === 'by_data') {
-          grid.resizeAllColumns && grid.resizeAllColumns(self.preferences.column_data_max_width, self.max_width_changed);
-        }
-      });
-
-      // Listener function which will be called before user updates existing cell
-      // This will be used to collect primary key for that row
-      grid.onBeforeEditCell.subscribe(function(e, args) {
-        if (args.column.column_type_internal == 'bytea' ||
-          args.column.column_type_internal == 'bytea[]') {
-          return false;
-        }
-
-        var before_data = args.item;
-
-        // If newly added row is saved but grid is not refreshed,
-        // then disable cell editing for that row
-        if (self.handler.rows_to_disable &&
-          _.contains(self.handler.rows_to_disable, args.row)) {
-          return false;
-        }
-
-        if (self.handler.can_edit && before_data && self.client_primary_key in before_data) {
-          var _pk = before_data[self.client_primary_key],
-            _keys = self.handler.primary_keys,
-            current_pk = {};
-
-          // If already have primary key data then no need to go ahead
-          if (_pk in self.handler.primary_keys_data) {
-            return;
-          }
-
-          // Fetch primary keys for the row before they gets modified
-          _.each(_keys, function(value, key) {
-            current_pk[key] = before_data[key];
-          });
-          // Place it in main variable for later use
-          self.handler.primary_keys_data[_pk] = current_pk;
-        }
-      });
-
-      grid.onKeyDown.subscribe(function(event, args) {
-        var KEY_A = 65;
-        var modifiedKey = event.keyCode;
-        var isModifierDown = event.ctrlKey || event.metaKey;
-        // Intercept Ctrl/Cmd + A key board event.
-        // As we might want to load all rows before selecting all.
-        if (isModifierDown && modifiedKey == KEY_A && self.handler.has_more_rows) {
-          self.fetch_next_all(function() {
-            handleQueryOutputKeyboardEvent(event, args);
-          });
-        } else {
-          handleQueryOutputKeyboardEvent(event, args);
-        }
-      });
-
-      // Handles blur event for slick grid cell
-      $('.slick-viewport').on('blur', 'input.editor-text', function () {
-        window.setTimeout(function() {
-          if (Slick.GlobalEditorLock.isActive())
-            Slick.GlobalEditorLock.commitCurrentEdit();
-        });
-      });
-
-      // Listener function which will be called when user updates existing rows
-      grid.onCellChange.subscribe(function(e, args) {
-        // self.handler.data_store.updated will holds all the updated data
-        var changed_column = args.grid.getColumns()[args.cell].field,
-          updated_data = args.item[changed_column], // New value for current field
-          _pk = args.item[self.client_primary_key] || null, // Unique key to identify row
-          column_data = {};
-
-        // Highlight the changed cell
-        self.handler.numberOfModifiedCells++;
-        args.grid.addCellCssStyles(self.handler.numberOfModifiedCells, {
-          [args.row] : {
-            [changed_column]: 'highlighted_grid_cells',
-          },
-        });
-
-        // Access to row/cell value after a cell is changed.
-        // The purpose is to remove row_id from temp_new_row
-        // if new row has primary key instead of [default_value]
-        // so that cell edit is enabled for that row.
-        var grid_edit = args.grid,
-          row_data = grid_edit.getDataItem(args.row),
-          is_primary_key = self.primary_keys &&
-          _.all(
-            _.values(
-              _.pick(
-                row_data, self.primary_keys
-              )
-            ),
-            function(val) {
-              return val != undefined;
-            }
-          );
-
-        // temp_new_rows is available only for view data.
-        if (is_primary_key && self.handler.temp_new_rows) {
-          var index = self.handler.temp_new_rows.indexOf(args.row);
-          if (index > -1) {
-            self.handler.temp_new_rows.splice(index, 1);
-          }
-        }
-
-        column_data[changed_column] = updated_data;
-
-
-        if (_pk) {
-          // Check if it is in newly added row by user?
-          if (_pk in self.handler.data_store.added) {
-            _.extend(
-              self.handler.data_store.added[_pk]['data'],
-              column_data);
-            //Find type for current column
-            self.handler.data_store.added[_pk]['err'] = false;
-            // Check if it is updated data from existing rows?
-          } else if (_pk in self.handler.data_store.updated) {
-            _.extend(
-              self.handler.data_store.updated[_pk]['data'],
-              column_data
-            );
-            self.handler.data_store.updated[_pk]['err'] = false;
-          } else {
-            // First updated data for this primary key
-            self.handler.data_store.updated[_pk] = {
-              'err': false,
-              'data': column_data,
-              'primary_keys': self.handler.primary_keys_data[_pk],
-            };
-            self.handler.data_store.updated_index[args.row] = _pk;
-          }
-        }
-        // Enable save button
-        $('#btn-save-data').prop('disabled', false);
-      }.bind(editor_data));
-
-      // Listener function which will be called when user adds new rows
-      grid.onAddNewRow.subscribe(function(e, args) {
-        // self.handler.data_store.added will holds all the newly added rows/data
-        var column = args.column,
-          item_current = args.item,
-          data_length = this.grid.getDataLength(),
-          _key = (self.client_primary_key_counter++).toString(),
-          data_view = this.grid.getData();
-
-        // Add new row in list to keep track of it
-        if (_.isUndefined(item_current[0])) {
-          self.handler.temp_new_rows.push(data_length);
-        }
-
-        // If copied item has already primary key, use it.
-        if (item_current) {
-          item_current[self.client_primary_key] = _key;
-        }
-
-        // When adding new rows, mark all native JS keywords undefined if not already set
-        _.each(args.grid.getColumns(), function(col){
-          if(isNative(item_current[col.field])) {
-            item_current[col.field] = undefined;
-          }
-        });
-
-        data_view.addItem(item_current);
-        self.handler.data_store.added[_key] = {
-          'err': false,
-          'data': item_current,
-        };
-        self.handler.data_store.added_index[data_length] = _key;
-
-        // Fetch data type & add it for the column
-        var temp = {};
-        temp[column.name] = _.where(this.columns, {
-          pos: column.pos,
-        })[0]['type'];
-        grid.updateRowCount();
-        grid.render();
-
-        // Highlight the first added cell of the new row
-        var row = data_view.getRowByItem(item_current);
-        self.handler.numberOfModifiedCells++;
-        args.grid.addCellCssStyles(self.handler.numberOfModifiedCells, {
-          [row] : {
-            [column.field]: 'highlighted_grid_cells',
-          },
-        });
-
-        // Enable save button
-        $('#btn-save-data').prop('disabled', false);
-      }.bind(editor_data));
-
-      // Listen grid viewportChanged event to load next chunk of data.
-      grid.onViewportChanged.subscribe(function(e, args) {
-        var rendered_range = args.grid.getRenderedRange(),
-          data_len = args.grid.getDataLength();
-        // start fetching next batch of records before reaching to bottom.
-        if (self.handler.has_more_rows && !self.handler.fetching_rows && rendered_range.bottom > data_len - 100) {
-          // fetch asynchronous
-          setTimeout(self.fetch_next.bind(self));
-        }
-      });
-
-      grid.onValidationError.subscribe(function (e, args) {
-        Notify.error(args.validationResults.msg);
-      });
-
-      // Resize SlickGrid when window resize
-      $(window).resize(function() {
-        // Resize grid only when 'Data Output' panel is visible.
-        if (self.data_output_panel.isVisible()) {
-          self.grid_resize(grid);
-        }
-      });
-
-      // Resize SlickGrid when output Panel resize
-      self.data_output_panel.on(wcDocker.EVENT.RESIZE_ENDED, function() {
-        // Resize grid only when 'Data Output' panel is visible.
-        if (self.data_output_panel.isVisible()) {
-          self.grid_resize(grid);
-        }
-      });
-
-      self.data_output_panel.on(wcDocker.EVENT.RESIZED, function() {
-        // Resize grid only when 'Data Output' panel is visible.
-        if (self.data_output_panel.isVisible()) {
-          if (self.preferences.column_data_auto_resize === 'by_data') {
-            grid.resizeAllColumns && grid.resizeAllColumns(
-              self.preferences.column_data_max_width, self.max_width_changed);
-          }else{
-            self.grid.resizeAllColumns && self.grid.resizeAllColumns();
-          }
-        }
-      });
-
-      // Resize SlickGrid when output Panel gets focus
-      self.data_output_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function() {
-        // Resize grid only if output panel is visible
-        if (self.data_output_panel.isVisible())
-          self.grid_resize(grid);
-      });
-
-      for (var i = 0; i < collection.length; i++) {
-        // Convert to dict from 2darray
-        var item = {};
-        for (var j = 1; j < grid_columns.length; j++) {
-          item[grid_columns[j]['field']] = collection[i][grid_columns[j]['pos']];
-        }
-
-        item[self.client_primary_key] = (self.client_primary_key_counter++).toString();
-        collection[i] = item;
-      }
-      dataView.setItems(collection, self.client_primary_key);
-      /* Resize the columns once if data empty */
-      if (collection.length === 0 && self.preferences.column_data_auto_resize === 'by_data') {
-        self.grid.resizeAllColumns && self.grid.resizeAllColumns();
-      }
-    },
-
-    fetch_next_all: function(cb) {
-      this.fetch_next(true, cb);
-    },
-
-    fetch_next: function(fetch_all, cb) {
-      var self = this,
-        url = '';
-
-      // This will prevent fetch operation if previous fetch operation is
-      // already in progress.
-      self.handler.fetching_rows = true;
-
-      $('#btn-flash').prop('disabled', true);
-      $('#btn-save-results-to-file').prop('disabled', true);
-
-      if (fetch_all) {
-        self.handler.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Fetching all records...')
-        );
-        url = url_for('sqleditor.fetch_all', {
-          'trans_id': self.transId,
-          'fetch_all': 1,
-        });
-      } else {
-        url = url_for('sqleditor.fetch', {
-          'trans_id': self.transId,
-        });
-      }
-
-      $.ajax({
-        url: url,
-        method: 'GET',
-      })
-        .done(function(res) {
-          self.handler.has_more_rows = res.data.has_more_rows;
-          setTimeout(() => {
-            $('#btn-flash').prop('disabled', false);
-          }, 700);
-          $('#btn-save-results-to-file').prop('disabled', false);
-          self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
-          self.update_grid_data(res.data.result);
-          self.handler.fetching_rows = false;
-          if (typeof cb == 'function') {
-            cb();
-          }
-        })
-        .fail(function(e) {
-          $('#btn-flash').prop('disabled', false);
-          $('#btn-save-results-to-file').prop('disabled', false);
-          self.handler.trigger('pgadmin-sqleditor:loading-icon:hide');
-          self.handler.has_more_rows = false;
-          self.handler.fetching_rows = false;
-          if (typeof cb == 'function') {
-            cb();
-          }
-
-          let msg = httpErrorHandler.handleQueryToolAjaxError(
-            pgAdmin, self, e, null, [], false
-          );
-          if (msg)
-            self.update_msg_history(false, msg);
-        });
-    },
-
-    update_grid_data: function(data) {
-      this.dataView.beginUpdate();
-
-      for (let data_val of data) {
-        // Convert 2darray to dict.
-        var item = {};
-        for (var j = 1; j < this.grid_columns.length; j++) {
-          item[this.grid_columns[j]['field']] = data_val[this.grid_columns[j]['pos']];
-        }
-
-        item[this.client_primary_key] = (this.client_primary_key_counter++).toString();
-        this.dataView.addItem(item);
-      }
-
-      this.dataView.endUpdate();
-    },
-
-    /* This function is responsible to render output grid */
-    grid_resize: function(grid) {
-      var prev_height = $('#datagrid').height(),
-        h = $(this.data_output_panel.$container.parent().parent()).height() - 35,
-        prev_viewport = grid.getViewport(),
-        prev_viewport_rows = grid.getRenderedRange(),
-        prev_cell = grid.getActiveCell();
-
-      // Apply css only if necessary, To avoid DOM operation
-      if (prev_height != h) {
-        $('#datagrid').css({
-          'height': h + 'px',
-        });
-      }
-
-      grid.resizeCanvas();
-
-      /*
-       * If there is an active cell from user then we have to go to that cell
-       */
-      if (prev_cell) {
-        grid.scrollCellIntoView(prev_cell.row, prev_cell.cell);
-      }
-
-      // If already displaying from first row
-      if (prev_viewport.top == prev_viewport_rows.top) {
-        return;
-      }
-      // if user has scroll to the end/last row
-      else if (prev_viewport.bottom - 2 == prev_viewport_rows.bottom) {
-        grid.scrollRowIntoView(prev_viewport.bottom);
-      } else {
-        grid.scrollRowIntoView(prev_viewport.bottom - 2);
-      }
-    },
-
-    fetch_query_history: function() {
-      let self = this;
-      $.ajax({
-        url: url_for('sqleditor.get_query_history', {
-          'trans_id': self.handler.transId,
-        }),
-        method: 'GET',
-        contentType: 'application/json',
-      }).done(function(res) {
-        res.data.result.map((entry) => {
-          let newEntry = JSON.parse(entry);
-          newEntry.start_time = new Date(newEntry.start_time);
-          self.history_collection.add(newEntry);
-        });
-      }).fail(function() {
-      /* history fetch fail should not affect query tool */
-      });
-    },
-    /* This function is responsible to create and render the history tab. */
-    render_history_grid: function() {
-      var self = this;
-
-      /* Should not reset if function called again */
-      if(!self.history_collection) {
-        self.history_collection = new HistoryCollection([]);
-      }
-
-      if(!self.historyComponent) {
-        self.historyComponent = new QueryHistory($('#history_grid'), self.history_collection);
-
-        /* Copy query to query editor, set the focus to editor and move cursor to end */
-        self.historyComponent.onCopyToEditorClick((query)=>{
-          self.query_tool_obj.setValue(query);
-          self.sql_panel_obj.focus();
-          setTimeout(() => {
-            self.query_tool_obj.focus();
-            self.query_tool_obj.setCursor(self.query_tool_obj.lineCount(), 0);
-          }, 100);
-        });
-
-        self.historyComponent.render();
-
-        self.history_panel.off(wcDocker.EVENT.VISIBILITY_CHANGED);
-        self.history_panel.on(wcDocker.EVENT.VISIBILITY_CHANGED, function() {
-          if (self.history_panel.isVisible()) {
-            setTimeout(()=>{
-              self.historyComponent.focus();
-            }, 100);
-          }
-        });
-      }
-
-      if(!self.handler.is_query_tool) {
-        self.historyComponent.setEditorPref({'copy_to_editor':false});
-      }
-    },
-
-    // Callback function for delete button click.
-    on_delete: function() {
-      var self = this;
-
-      // Trigger the deleterow signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:deleterow',
-        self,
-        self.handler
-      );
-    },
-
-    _stopEventPropogation: function(ev) {
-      ev = ev || window.event;
-      ev.cancelBubble = true;
-      ev.stopPropagation();
-      ev.stopImmediatePropagation();
-      ev.preventDefault();
-    },
-
-    _closeDropDown: function(ev) {
-      var target = ev && (ev.currentTarget || ev.target);
-      if (target) {
-        $(target).closest('.editor-toolbar').find('.show').removeClass('show');
-      }
-    },
-
-    // Callback function for Save button click.
-    on_save_file: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.handler.close_on_save = false;
-      // Trigger the save signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:save_file'
-      );
-    },
-
-    // Callback function for Save button click.
-    on_save_as: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.handler.close_on_save = false;
-      // Trigger the save signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:save_file',
-        self,
-        self.handler,
-        true
-      );
-    },
-
-    // Callback function for the find button click.
-    on_find: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('find');
-    },
-
-    // Callback function for the find next button click.
-    on_find_next: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('findNext');
-    },
-
-    // Callback function for the find previous button click.
-    on_find_previous: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('findPrev');
-    },
-
-    // Callback function for the replace button click.
-    on_replace: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('replace');
-    },
-
-    // Callback function for the replace all button click.
-    on_replace_all: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('replaceAll');
-    },
-
-    // Callback function for the find persistent button click.
-    on_find_persistent: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('findPersistent');
-    },
-
-    // Callback function for the jump button click.
-    on_jump: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.query_tool_obj.execCommand('jumpToLine');
-    },
-
-    // Callback function for filter button click.
-    on_show_filter: function() {
-      var self = this;
-
-      // Trigger the show_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:show_filter',
-        self,
-        self.handler
-      );
-    },
-
-    on_new_connection: function() {
-      var self = this;
-
-      // Trigger the show_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:show_new_connection',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for include filter button click.
-    on_include_filter: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      // Trigger the include_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:include_filter',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for exclude filter button click.
-    on_exclude_filter: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      // Trigger the exclude_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:exclude_filter',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for remove filter button click.
-    on_remove_filter: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      // Trigger the remove_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:remove_filter',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for cancel button click.
-    on_cancel: function() {
-      $('#filter').addClass('d-none');
-      $('#editor-panel').removeClass('sql-editor-busy-fetching');
-    },
-
-    // Callback function for copy button click.
-    on_copy_row: function() {
-      var self = this;
-
-      // Trigger the copy signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:copy_row',
-        self,
-        self.handler
-      );
-
-    },
-
-    // Callback function for copy with header button click.
-    on_copy_row_with_header: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Toggle the button
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:copy_row_with_header',
-        self,
-        self.handler
-      );
-
-    },
-
-    // Callback function for paste button click.
-    on_paste_row: function() {
-      var self = this;
-
-      // Trigger the paste signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:paste_row',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for the change event of combo box
-    on_limit_change: function() {
-      var self = this;
-
-      // Trigger the limit signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:limit',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for Save Data Changes button click.
-    on_save_data: function() {
-      this.handler.history_query_source = QuerySources.SAVE_DATA;
-
-      queryToolActions.saveDataChanges(this.handler);
-    },
-
-    // Callback function for the flash button click.
-    on_flash: function() {
-      $('#btn-flash').prop('disabled', true);
-      let data_click_counter = $('#btn-flash').attr('data-click-counter');
-      data_click_counter = (parseInt(data_click_counter) + 1)%10;
-      $('#btn-flash').attr('data-click-counter', data_click_counter);
-
-      this.handler.history_query_source = QuerySources.EXECUTE;
-
-      queryToolActions.executeQuery(this.handler);
-    },
-
-    // Callback function for the cancel query button click.
-    on_cancel_query: function() {
-      var self = this;
-
-      // Trigger the cancel-query signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:cancel-query',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for the line comment code
-    on_comment_line_code: function() {
-      queryToolActions.commentLineCode(this.handler);
-    },
-
-    // Callback function for the line uncomment code
-    on_uncomment_line_code: function() {
-      queryToolActions.uncommentLineCode(this.handler);
-    },
-
-    // Callback function for the block comment/uncomment code
-    on_toggle_comment_block_code: function() {
-      queryToolActions.commentBlockCode(this.handler);
-    },
-
-    on_indent_code: function() {
-      var self = this;
-      // Trigger the comment signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:indent_selected_code',
-        self,
-        self.handler
-      );
-    },
-
-    on_unindent_code: function() {
-      var self = this;
-      // Trigger the comment signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:unindent_selected_code',
-        self,
-        self.handler
-      );
-    },
-
-    on_format_sql: function() {
-      var self = this;
-      // Trigger the format signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:format_sql',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for the clear button click.
-    on_clear: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      /* If is_query_changed flag is set to false then no need to
-       * confirm with the user for unsaved changes.
-       */
-      if (self.handler.is_query_changed) {
-        Notify.confirm(
-          gettext('Unsaved changes'),
-          gettext('Are you sure you wish to discard the current changes?'),
-          function() {
-            // Do nothing as user do not want to save, just continue
-            self.query_tool_obj.setValue('');
-            setTimeout(() => { self.query_tool_obj.focus(); }, 200);
-          },
-          function() {
-            return true;
-          }
-        );
-      } else {
-        self.query_tool_obj.setValue('');
-      }
-    },
-
-    // Callback function for the clear history button click.
-    on_clear_history: function(ev) {
-      var self = this;
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-      // ask for confirmation only if anything to clear
-      if (!self.history_collection.length()) {
-        return;
-      }
-
-      Notify.confirm(gettext('Clear history'),
-        gettext('Are you sure you wish to clear the history?') + '</br>' +
-        gettext('This will remove all of your query history from this and other sessions for this database.'),
-        function() {
-          if (self.history_collection) {
-            self.history_collection.reset();
-          }
-
-          if(self.handler.is_query_tool) {
-            $.ajax({
-              url: url_for('sqleditor.clear_query_history', {
-                'trans_id': self.handler.transId,
-              }),
-              method: 'DELETE',
-              contentType: 'application/json',
-            })
-              .done(function() { /* This is intentional */ })
-              .fail(function() {
-              /* history clear fail should not affect query tool */
-              });
-          }
-          setTimeout(() => { self.query_tool_obj.focus(); }, 200);
-        },
-        function() {
-          return true;
-        }
-      );
-    },
-
-    // Callback function for the auto commit button click.
-    on_auto_commit: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the auto-commit signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:auto_commit',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for the auto rollback button click.
-    on_auto_rollback: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the download signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:auto_rollback',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for explain button click.
-    on_explain: function(event) {
-      this._stopEventPropogation(event);
-      this._closeDropDown(event);
-
-      this.handler.history_query_source = QuerySources.EXPLAIN;
-      queryToolActions.explain(this.handler);
-    },
-
-    // Callback function for explain analyze button click.
-    on_explain_analyze: function(event) {
-      this._stopEventPropogation(event);
-      this._closeDropDown(event);
-
-      this.handler.history_query_source = QuerySources.EXPLAIN_ANALYZE;
-      queryToolActions.explainAnalyze(this.handler);
-    },
-
-    // Callback function for explain option "verbose" button click
-    on_explain_verbose: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the explain "verbose" signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-verbose',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for explain option "costs" button click
-    on_explain_costs: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the explain "costs" signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-costs',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for explain option "buffers" button click
-    on_explain_buffers: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the explain "buffers" signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-buffers',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for explain option "timing" button click
-    on_explain_timing: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      // Trigger the explain "timing" signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-timing',
-        self,
-        self.handler
-      );
-    },
-
-    on_explain_summary: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-summary',
-        self,
-        self.handler
-      );
-    },
-
-    on_explain_settings: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:explain-settings',
-        self,
-        self.handler
-      );
-    },
-
-
-    do_not_close_menu: function(ev) {
-      ev.stopPropagation();
-    },
-
-    // callback function for show query tool click.
-    on_show_query_tool: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:show_query_tool',
-        self,
-        self.handler
-      );
-    },
-
-    // callback function for load file button click.
-    on_file_load: function(ev) {
-      var self = this;
-
-      this._stopEventPropogation(ev);
-      this._closeDropDown(ev);
-
-      // Trigger the save signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:load_file',
-        self,
-        self.handler
-      );
-    },
-
-    on_download: function() {
-      queryToolActions.download(this.handler);
-    },
-
-    keyAction: function(event) {
-      var panel_type='';
-
-      panel_type = keyboardShortcuts.processEventQueryTool(
-        this.handler, queryToolActions, event, this.docker
-      );
-
-      if(!_.isNull(panel_type) && !_.isUndefined(panel_type) && panel_type != '') {
-        setTimeout(function() {
-          pgBrowser.Events.trigger(`pgadmin:query_tool:${panel_type}:focus`);
-        }, 100);
-      }
-    },
-
-    // Callback function for the commit button click.
-    on_commit_transaction: function() {
-      this.handler.close_on_idle_transaction = false;
-      this.handler.history_query_source = QuerySources.COMMIT;
-
-      queryToolActions.executeCommit(this.handler);
-    },
-
-    // Callback function for the rollback button click.
-    on_rollback_transaction: function() {
-      this.handler.close_on_idle_transaction = false;
-      this.handler.history_query_source = QuerySources.ROLLBACK;
-
-      queryToolActions.executeRollback(this.handler);
-    },
-
-    // Callback function for manage macros button click.
-    on_manage_macros: function() {
-      var self = this;
-
-      // Trigger the show_filter signal to the SqlEditorController class
-      self.handler.trigger(
-        'pgadmin-sqleditor:button:manage_macros',
-        self,
-        self.handler
-      );
-    },
-
-    // Callback function for manage macros button click.
-    on_execute_macro: function(e) {
-      let macroId = $(e.currentTarget).data('macro-id');
-      this.handler.history_query_source = QuerySources.EXECUTE;
-      queryToolActions.executeMacro(this.handler, macroId);
-    },
-
-    set_selected_option: function(selected_config) {
-      this.connection_list.forEach(option =>{
-        if(option['server'] == selected_config['server'] && option['database'] == selected_config['database'] && option['user'] == selected_config['user'] && option['role'] == selected_config['role']) {
-          selected_config['is_selected'] = true;
-        } else {
-          option['is_selected'] = false;
-        }
-      });
-    },
-
-    on_change_connection: function(connection_details, ref, add_new_connection=true) {
-      if(!connection_details['is_selected']) {
-        var self = this;
-        if(add_new_connection) {
-          Notify.confirm(gettext('Change connection'),
-            gettext('By changing the connection you will lose all your unsaved data for the current connection. <br> Do you want to continue?'),
-            function() {
-              self.change_connection(connection_details, ref, true);
-            },
-            function() {
-              var loadingDiv = $('#main_loader');
-              loadingDiv.addClass('d-none');
-              alertify.newConnectionDialog().destroy();
-              return true;
-            }
-          );
-        } else {
-          self.change_connection(connection_details, ref, false);
-        }
-      }
-    },
-
-    change_connection: function(connection_details, ref, add_new_connection) {
-      var self = this;
-      var loadingDiv = null;
-      var msgDiv = null;
-      if(ref){
-        loadingDiv = $('#show_filter_progress');
-        loadingDiv.removeClass('d-none');
-        msgDiv = loadingDiv.find('.sql-editor-busy-text');
-        msgDiv.text('Connecting to database...');
-      } else{
-        loadingDiv = $('#main_loader');
-        loadingDiv.removeClass('d-none');
-      }
-      self.set_selected_option(connection_details);
-      $.ajax({
-        url: url_for('datagrid.update_query_tool_connection', {
-          'trans_id': self.transId,
-          'sgid': connection_details['server_group'],
-          'sid': connection_details['server'],
-          'did': connection_details['database'],
-        }),
-        method: 'POST',
-        contentType: 'application/json',
-        data: JSON.stringify(connection_details),
-      })
-        .done(function(res) {
-          if(res.success) {
-            delete connection_details.password;
-            self.transId = res.data.tran_id;
-            self.handler.transId = res.data.tran_id;
-            self.handler.url_params = {
-              'did': connection_details['database'],
-              'is_query_tool': self.handler.url_params.is_query_tool,
-              'server_type': self.handler.url_params.server_type,
-              'sgid': connection_details['server_group'],
-              'sid': connection_details['server'],
-              'title': connection_details['title'],
-            };
-            self.set_editor_title(_.unescape(connection_details['conn_title']));
-            self.handler.setTitle(_.unescape(self.handler.url_params.title));
-            let success_msg = connection_details['server_name'] + '/' + connection_details['database_name'] + '- Database connected';
-            Notify.success(success_msg);
-            if(ref){
-              let connection_data = {
-                'server_group': self.handler.url_params.sgid,
-                'server': connection_details['server'],
-                'database': connection_details['database'],
-                'user': connection_details['user'],
-                'title': connection_details['title'],
-                'conn_title': connection_details['conn_title'],
-                'role': connection_details['role'],
-                'is_allow_new_connection': true,
-                'database_name': connection_details['database_name'],
-                'server_name': connection_details['server_name'],
-                'is_selected': true,
-              };
-              delete connection_data.password;
-              if(add_new_connection) {
-                self.connection_list.unshift(connection_data);
-              }
-
-              self.render_connection(self.connection_list);
-              loadingDiv.addClass('d-none');
-              alertify.newConnectionDialog().destroy();
-              alertify.connectServer().destroy();
-            } else {
-              loadingDiv.addClass('d-none');
-              alertify.connectServer().destroy();
-            }
-          }
-          return true;
-        })
-        .fail(function(xhr) {
-          if(xhr.status == 428) {
-            var connection_info = connection_details;
-            if(ref) {
-              connection_info = {};
-            }
-            alertify.connectServer('Connect to server', xhr.responseJSON.result, connection_details['server'], false, connection_info);
-          } else {
-            Notify.error(xhr.responseJSON['errormsg']);
-          }
-        });
-    },
-  });
-
-
-
-  /* Defining controller class for data grid, which actually
-   * perform the operations like executing the sql query, poll the result,
-   * render the data in the grid, Save/Refresh the data etc...
-   */
-  var SqlEditorController = function() {
-    this.initialize.apply(this, arguments);
-  };
-
-  /* This function is used to check whether user have closed
-   * the main window when query tool is opened on new tab
-   */
-  var is_main_window_alive = function() {
-
-    if((pgWindow.default && pgWindow.default.closed) ||
-        pgWindow.default.pgAdmin && pgWindow.default.pgAdmin.Browser
-            && pgWindow.default.pgAdmin.Browser.preference_version() <= 0) {
-
-      Notify.alert(gettext('Connection lost'),
-        gettext('The pgAdmin browser window has been closed and the connection to the server is lost. Please close this window and open a new pgAdmin session.'),
-        function(){
-          //Close the window after connection is lost
-          window.close();
-        },
-        gettext('Close')
-      );
-    }
-  };
-
-  _.extend(
-    SqlEditorController.prototype,
-    Backbone.Events,
-    {
-      initialize: function(container) {
-        var self = this;
-        this.container = container;
-        this.state = {};
-        this.csrf_token = pgAdmin.csrf_token;
-
-        //call to check whether user have closed the parent window and trying to refresh, if yes return error.
-        is_main_window_alive();
-
-        // Disable animation first
-        modifyAnimation.modifyAlertifyAnimation();
-
-        if (!alertify.dlgGetServerPass) {
-          alertify.dialog('dlgGetServerPass', function factory() {
-            return {
-              main: function(
-                title, message
-              ) {
-                this.set('title', title);
-                this.setting('message',message);
-              },
-              setup:function() {
-                return {
-                  buttons:[
-                    {
-                      text: gettext('OK'),
-                      key: 13,
-                      className: 'btn btn-primary',
-                    },
-                    {
-                      text: gettext('Cancel'),
-                      key: 27,
-                      className: 'btn btn-danger',
-                    },
-                  ],
-                  focus: {
-                    element: '#password',
-                    select: true,
-                  },
-                  options: {
-                    modal: 0,
-                    resizable: false,
-                    maximizable: false,
-                    pinnable: false,
-                  },
-                };
-              },
-              build:function() {/* */},
-              settings:{
-                message: null,
-              },
-              prepare:function() {
-                this.setContent(this.setting('message'));
-              },
-              callback: function(closeEvent) {
-                if (closeEvent.button.text == gettext('OK')) {
-                  var passdata = $(this.elements.content).find('#frmPassword').serialize();
-                  self.init_connection(false, passdata);
-                }
-              },
-            };
-          });
-        }
-        this.on('pgadmin-datagrid:transaction:created', function(trans_obj) {
-          self.transId = trans_obj.gridTransId;
-          self.warn_before_continue();
-        });
-        pgBrowser.Events.on('pgadmin:user:logged-in', function() {
-          self.initTransaction();
-        });
-      },
-      saveState: function(fn, args) {
-        if (fn) {
-          this.state = {
-            'fn': fn,
-            'args': args,
-          };
-        } else {
-          this.state = {};
-        }
-      },
-
-      initTransaction: function() {
-        var self = this, url_endpoint;
-        if (self.is_query_tool) {
-          url_endpoint = 'datagrid.initialize_query_tool';
-
-          // If database not present then use Maintenance database
-          // We will handle this at server side
-          if (self.url_params.did) {
-            url_endpoint = 'datagrid.initialize_query_tool_with_did';
-          }
-
-        } else {
-          url_endpoint = 'datagrid.initialize_datagrid';
-        }
-
-        var baseUrl = url_for(url_endpoint, {
-          ...self.url_params,
-          'trans_id': self.transId,
-        });
-
-        $.ajax({
-          url: baseUrl,
-          type: 'POST',
-          data: self.is_query_tool?null:JSON.stringify(self.url_params.sql_filter),
-          contentType: 'application/json',
-        }).done((res)=>{
-          pgBrowser.Events.trigger(
-            'pgadmin:query_tool:connected:' + self.transId, res.data
-          );
-        }).fail((xhr, status, error)=>{
-          if (xhr.status === 410) {
-          //checking for Query tool in new window.
-            var open_new_tab = self.browser_preferences.new_browser_tab_open;
-            if(open_new_tab && open_new_tab.includes('qt')) {
-              pgBrowser.report_error(gettext('Error fetching rows - %s.', xhr.statusText), xhr.responseJSON.errormsg, undefined, window.close);
-            } else {
-              pgBrowser.report_error(gettext('Error fetching rows - %s.', xhr.statusText), xhr.responseJSON.errormsg, undefined, self.close.bind(self));
-            }
-          } else {
-            if (xhr.responseText.search('Ticket expired') !== -1) {
-              let fetchTicket = Kerberos.fetch_ticket();
-              fetchTicket.then(
-                function() {
-                  self.initTransaction();
-                },
-                function() {
-                  pgBrowser.Events.trigger(
-                    'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
-                  );
-                }
-              );
-            } else {
-              pgBrowser.Events.trigger(
-                'pgadmin:query_tool:connected_fail:' + self.transId, xhr, error
-              );
-            }
-          }
-        });
-      },
-
-      handle_connection_lost: function(create_transaction, xhr) {
-        /* If responseJSON is undefined then it could be object of
-         * axios(Promise HTTP) response, so we should check accordingly.
-         */
-        if (xhr.responseJSON !== undefined &&
-            xhr.responseJSON.data && !xhr.responseJSON.data.conn_id) {
-          // if conn_id is null then this is maintenance db.
-          // so attempt connection connect without prompt.
-          this.init_connection(create_transaction);
-        } else if (xhr.data !== undefined &&
-          xhr.data.data && !xhr.data.data.conn_id) {
-          // if conn_id is null then this is maintenance db.
-          // so attempt connection connect without prompt.
-          this.init_connection(create_transaction);
-        } else {
-          this.warn_before_continue();
-        }
-      },
-      handle_cryptkey_missing: function() {
-        pgBrowser.set_master_password('', ()=>{
-          this.warn_before_continue();
-        });
-      },
-      warn_before_continue: function() {
-        var self = this;
-
-        Notify.confirm(
-          gettext('Connection Warning'),
-          '<p style="float:left margin-right:0.5em">'+
-            '<span class="fa fa-exclamation-triangle warn-icon" aria-hidden="true" role="img">'+
-            '</span>'+
-          '</p>'+
-          '<p style="display: inline-block;">'+
-            '<span class="warn-header">'+
-              gettext('The application has lost the database connection:')+
-            '</span>'+
-            '<br>'+
-            '<span class="warn-body">'+
-              gettext('⁃ If the connection was idle it may have been forcibly disconnected.')+
-            '<br>'+
-              gettext('⁃ The application server or database server may have been restarted.')+
-            '<br>'+
-              gettext('⁃ The user session may have timed out.')+
-            '</span>'+
-            '<br>'+
-            '<span class="warn-footer">'+
-            gettext('Do you want to continue and establish a new session?')+
-            '</span>'+
-          '</p>',
-          function() {
-            if ('fn' in self.state) {
-              var fn = self.state['fn'],
-                args = self.state['args'];
-              self.saveState();
-              if (args.indexOf('connect') == -1) {
-                args.push('connect');
-              }
-              if (fn in self) {
-                self[fn].apply(self, args);
-              } else {
-                console.warn('The callback is not valid for this context');
-              }
-
-            }
-          }, function() {
-            self.saveState();
-          },
-          gettext('Continue'),
-          gettext('Cancel')
-        );
-      },
-      init_connection: function(create_transaction, passdata) {
-        var self = this;
-        $.post(url_for('NODE-server.connect_id', {
-          'gid': this.url_params.sgid,
-          'sid': this.url_params.sid,
-        }),passdata)
-          .done(function(res) {
-            if (res.success == 1) {
-              Notify.success(res.info);
-              if (create_transaction) {
-                self.initTransaction();
-              } else if ('fn' in self.state) {
-                var fn = self.state['fn'],
-                  args = self.state['args'];
-                self.saveState();
-                self[fn].apply(self, args);
-              }
-            }
-          })
-          .fail(function(xhr) {
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, xhr, null, [], false
-            );
-            if (msg)
-              alertify.dlgGetServerPass(
-                gettext('Connect to Server'), msg
-              );
-          });
-      },
-      /* This function is used to create instance of SQLEditorView,
-       * call the render method of the grid view to render the slickgrid
-       * header and loading icon and start execution of the sql query.
-       */
-      start: function(transId, url_params, layout, macros) {
-        var self = this;
-
-        self.is_query_tool = url_params.is_query_tool==='true'?true:false;
-        self.rows_affected = 0;
-        self.marked_line_no = 0;
-        self.has_more_rows = false;
-        self.fetching_rows = false;
-        self.close_on_save = false;
-        self.close_on_idle_transaction = false;
-        self.last_transaction_status = -1;
-        self.server_type = url_params.server_type;
-        self.url_params = url_params;
-        self.is_transaction_buttons_disabled = true;
-        self.is_save_results_to_file_disabled = true;
-
-        // We do not allow to call the start multiple times.
-        if (self.gridView)
-          return;
-
-        self.gridView = new SQLEditorView({
-          el: self.container,
-          handler: self,
-          layout: layout,
-        });
-        self.transId = self.gridView.transId = transId;
-        self.macros = self.gridView.macros = macros;
-
-        self.gridView.current_file = undefined;
-
-        // Render the header
-        self.gridView.render();
-
-        // Listen on events to show/hide loading-icon and change messages.
-        // Parallelly, also update the bottom message.
-        let loadingDiv = self.gridView.data_output_panel.$container.find('#fetching_data'),
-          msgDiv = loadingDiv.find('.sql-editor-busy-text'),
-          statusDiv = $('.sql-editor-busy-text-status');
-
-        self.on('pgadmin-sqleditor:loading-icon:message', function(msg) {
-          msgDiv.text(msg);
-          statusDiv.text(msg);
-        }).on('pgadmin-sqleditor:loading-icon:show', function(msg) {
-          loadingDiv.removeClass('d-none');
-          statusDiv.removeClass('d-none');
-
-          msgDiv.text(msg);
-          statusDiv.text(msg);
-        }).on('pgadmin-sqleditor:loading-icon:hide', function() {
-          loadingDiv.addClass('d-none');
-          statusDiv.addClass('d-none');
-        });
-
-        $('#main_loader').addClass('d-none');
-        self.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-        self.gridView.set_editor_title('(' + gettext('Obtaining connection...') + ` ${_.unescape(url_params.title)}`);
-
-        let afterConn = function() {
-          let enableBtns = [];
-
-          if(self.is_query_tool){
-            enableBtns = ['#btn-flash', '#btn-explain', '#btn-explain-analyze'];
-          } else {
-            enableBtns = ['#btn-flash'];
-          }
-
-          enableBtns.forEach((selector)=>{
-            $(selector).prop('disabled', false);
-          });
-
-          $('#btn-conn-status i').removeClass('obtaining-conn');
-
-          var tree_data = pgWindow.default.pgAdmin.Browser.tree.getTreeNodeHierarchy(pgWindow.default.pgAdmin.Browser.tree.selected());
-          let conn_title = ` ${_.unescape(url_params.title)}`;
-          self.gridView.set_editor_title(_.unescape(conn_title));
-          let connection_data = {
-            'server_group': self.gridView.handler.url_params.sgid,
-            'server': self.gridView.handler.url_params.sid,
-            'database': self.gridView.handler.url_params.did,
-            'user': tree_data.server.user.name,
-            'role': null,
-            'title': _.unescape(url_params.title),
-            'conn_title': _.unescape(conn_title),
-            'is_allow_new_connection': false,
-            'database_name': _.unescape(tree_data.database.label),
-            'server_name': _.unescape(tree_data.server.label),
-            'is_selected': true,
-          };
-          delete connection_data.password;
-          self.gridView.connection_list.unshift(connection_data);
-          self.gridView.render_connection(self.gridView.connection_list);
-        };
-
-        pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, afterConn);
-        pgBrowser.Events.on('pgadmin:query_tool:connected_fail:' + transId, afterConn);
-
-        pgBrowser.Events.on('pgadmin:query_tool:connected:' + transId, (res_data)=>{
-          self.gridView.set_server_version(res_data.serverVersion);
-        });
-
-        pgBrowser.Events.on('pgadmin:query_tool:connected_fail:' + transId, (xhr, error)=>{
-          Notify.pgRespErrorNotify(xhr, error);
-        });
-
-        self.initTransaction();
-
-        /* wcDocker focuses on window always, and all our shortcuts are
-         * bind to editor-panel. So when we use wcDocker focus, editor-panel
-         * loses focus and events don't work.
-         */
-        $(window).on('keydown', (e)=>{
-          if(($('.sql-editor').find(e.target).length !== 0 || e.target == $('body.wcDesktop')[0]) && self.gridView.keyAction) {
-            self.gridView.keyAction(e);
-          }
-        });
-
-        self.init_events();
-        if (self.is_query_tool) {
-          // Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT)
-          // Call AJAX only if script type url is present
-          if (url_params.query_url) {
-            $.ajax({
-              url: url_params.query_url,
-              type:'GET',
-            })
-              .done(function(res) {
-                self.gridView.query_tool_obj.refresh();
-                if (res) {
-                  self.gridView.query_tool_obj.setValue(res);
-                }
-              })
-              .fail(function(jqx) {
-                let msg = '';
-
-                msg = httpErrorHandler.handleQueryToolAjaxError(
-                  pgAdmin, self, jqx, null, [], false
-                );
-                if (msg) {
-                  var open_new_tab = self.browser_preferences.new_browser_tab_open;
-                  if(open_new_tab && open_new_tab.includes('qt')) {
-                    pgBrowser.report_error(gettext('Error fetching SQL for script - %s.', jqx.statusText), jqx.responseJSON.errormsg, undefined, window.close);
-                  } else {
-                    pgBrowser.report_error(gettext('Error fetching SQL for script - %s.', jqx.statusText), jqx.responseJSON.errormsg, undefined, self.close.bind(self));
-                  }
-                }
-
-              });
-          } else if(url_params.sql_id) {
-            let sqlValue = localStorage.getItem(url_params.sql_id);
-            localStorage.removeItem(url_params.sql_id);
-            if(sqlValue) {
-              self.gridView.query_tool_obj.setValue(sqlValue);
-            }
-          }
-        }
-        else {
-          // Disable codemirror by setting readOnly option to true, background to dark, and cursor, hidden.
-          self.gridView.query_tool_obj.setOption('readOnly', true);
-          var cm = self.gridView.query_tool_obj.getWrapperElement();
-          if (cm) {
-            cm.className += ' bg-gray-lighter opacity-5 hide-cursor-workaround';
-          }
-          self.disable_tool_buttons(true);
-          pgBrowser.Events.on('pgadmin:query_tool:connected:'+ transId,()=>{
-            self.check_data_changes_to_execute_query();
-          });
-        }
-      },
-
-      set_value_to_editor: function(query) {
-        if (this.gridView && this.gridView.query_tool_obj && !_.isUndefined(query) && query != '') {
-          this.gridView.query_tool_obj.setValue(query);
-        }
-      },
-
-      init_events: function() {
-        var self = this;
-        // Listen to the file manager button events
-        pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:select_file', self._select_file_handler, self);
-        pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', self._save_file_handler, self);
-
-        // Listen to the codemirror on text change event
-        // only in query editor tool
-        if (self.is_query_tool) {
-          self.gridView.query_tool_obj.on('change', self._on_query_change.bind(self));
-        }
-
-        // Listen on events come from SQLEditorView for the button clicked.
-        self.on('pgadmin-sqleditor:button:load_file', self._load_file, self);
-        self.on('pgadmin-sqleditor:button:save_file', self._save_file, self);
-        self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
-        self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
-        self.on('pgadmin-sqleditor:button:show_new_connection', self._show_new_connection, self);
-        self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
-        self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
-        self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
-        self.on('pgadmin-sqleditor:button:copy_row', self._copy_row, self);
-        self.on('pgadmin-sqleditor:button:copy_row_with_header', self._copy_row_with_header, self);
-        self.on('pgadmin-sqleditor:button:paste_row', self._paste_row, self);
-        self.on('pgadmin-sqleditor:button:limit', self._set_limit, self);
-        self.on('pgadmin-sqleditor:button:cancel-query', self._cancel_query, self);
-        self.on('pgadmin-sqleditor:button:auto_rollback', self._auto_rollback, self);
-        self.on('pgadmin-sqleditor:button:auto_commit', self._auto_commit, self);
-        self.on('pgadmin-sqleditor:button:explain-verbose', self._explain_verbose, self);
-        self.on('pgadmin-sqleditor:button:explain-costs', self._explain_costs, self);
-        self.on('pgadmin-sqleditor:button:explain-buffers', self._explain_buffers, self);
-        self.on('pgadmin-sqleditor:button:explain-timing', self._explain_timing, self);
-        self.on('pgadmin-sqleditor:button:explain-summary', self._explain_summary, self);
-        self.on('pgadmin-sqleditor:button:explain-settings', self._explain_settings, self);
-        self.on('pgadmin-sqleditor:button:show_query_tool', self._show_query_tool, self);
-        // Indentation related
-        self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self);
-        self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self);
-        // Format
-        self.on('pgadmin-sqleditor:format_sql', self._format_sql, self);
-        self.on('pgadmin-sqleditor:button:manage_macros', self._manage_macros, self);
-        self.on('pgadmin-sqleditor:button:execute_macro', self._execute_macro, self);
-
-        window.parent.$(window.parent.document).on('pgadmin-sqleditor:rows-copied', self._copied_in_other_session);
-      },
-
-      // Checks if there is any dirty data in the grid before executing a query
-      check_data_changes_to_execute_query: function(explain_prefix=null, shouldReconnect=false, macroId=undefined) {
-        var self = this;
-
-        // Check if the data grid has any changes before running query
-        if (_.has(self, 'data_store') &&
-          (_.size(self.data_store.added) ||
-            _.size(self.data_store.updated) ||
-            _.size(self.data_store.deleted))
-        ) {
-          Notify.confirm(gettext('Unsaved changes'),
-            gettext('The data has been modified, but not saved. Are you sure you wish to discard the changes?'),
-            function() {
-              // The user does not want to save, just continue
-              if (macroId !== undefined) {
-                self._execute_macro_query(explain_prefix, shouldReconnect, macroId);
-              }
-              else if(self.is_query_tool) {
-                self._execute_sql_query(explain_prefix, shouldReconnect);
-              }
-              else {
-                self._execute_view_data_query();
-              }
-            },
-            function() {
-              // Stop, User wants to save
-              $('#btn-flash').prop('disabled', false);
-              return true;
-            }
-          );
-        } else {
-          if (macroId !== undefined) {
-            self._execute_macro_query(explain_prefix, shouldReconnect, macroId);
-          }
-          else if(self.is_query_tool) {
-            self._execute_sql_query(explain_prefix, shouldReconnect);
-          }
-          else {
-            self._execute_view_data_query();
-          }
-        }
-      },
-
-      // Makes the ajax call to execute the sql query in View Data mode
-      _execute_view_data_query: function() {
-        var self = this,
-          url = url_for('sqleditor.view_data_start', {
-            'trans_id': self.transId,
-          });
-
-        self.query_start_time = new Date();
-        self.rows_affected = 0;
-        self._init_polling_flags();
-
-        self.has_more_rows = false;
-        self.fetching_rows = false;
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Running query...')
-        );
-
-        $('#btn-flash').prop('disabled', true);
-        self.enable_disable_download_btn(true);
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:message',
-          gettext('Waiting for the query to complete...')
-        );
-
-        if (arguments.length > 0 &&
-          arguments[arguments.length - 1] == 'connect') {
-          url += '?connect=1';
-        }
-
-        $.ajax({
-          url: url,
-          method: 'GET',
-        })
-          .done(function(res) {
-            if (res.data.status) {
-              self.can_edit = res.data.can_edit;
-              self.can_filter = res.data.can_filter;
-              self.info_notifier_timeout = res.data.info_notifier_timeout;
-
-              // Set the sql query to the SQL panel
-              self.gridView.query_tool_obj.setValue(res.data.sql);
-              self.query = res.data.sql;
-
-
-              /* If filter is applied then remove class 'btn-secondary'
-             * and add 'btn-primary' to change the colour of the button.
-             */
-              if (self.can_filter && res.data.filter_applied) {
-                $('#btn-filter').removeClass('btn-secondary');
-                $('#btn-filter-dropdown').removeClass('btn-secondary');
-                $('#btn-filter').addClass('btn-primary');
-                $('#btn-filter-dropdown').addClass('btn-primary');
-              } else {
-                $('#btn-filter').removeClass('btn-primary');
-                $('#btn-filter-dropdown').removeClass('btn-primary');
-                $('#btn-filter').addClass('btn-secondary');
-                $('#btn-filter-dropdown').addClass('btn-secondary');
-              }
-
-              $('#btn-copy-row').prop('disabled', true);
-              $('#btn-paste-row').prop('disabled', true);
-
-              // Set the combo box value
-              $('.limit').val(res.data.limit);
-
-              // If status is True then poll the result.
-              self._poll();
-            } else {
-              self.trigger('pgadmin-sqleditor:loading-icon:hide');
-              self.update_msg_history(false, res.data.result);
-            }
-          })
-          .fail(function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_execute_view_data_query', [], true
-            );
-            if (msg)
-              self.update_msg_history(false, msg);
-          });
-      },
-
-      // Executes sql query  for macroin the editor in Query Tool mode
-      _execute_macro_query: function(explain_prefix, shouldReconnect, macroId) {
-        var self = this;
-
-        self.has_more_rows = false;
-        self.fetching_rows = false;
-
-        $.ajax({
-          url: url_for('sqleditor.get_macro', {'macro_id': macroId, 'trans_id': self.transId}),
-          method: 'GET',
-          contentType: 'application/json',
-          dataType: 'json',
-        })
-          .done(function(res) {
-            if (res) {
-              // Replace the place holder
-              const regex = /\$SELECTION\$/gi;
-              let query =  res.sql.replace(regex, self.gridView.query_tool_obj.getSelection());
-
-              const executeQuery = new ExecuteQuery.ExecuteQuery(self, pgAdmin.Browser.UserManagement);
-              executeQuery.poll = pgBrowser.override_activity_event_decorator(executeQuery.poll).bind(executeQuery);
-              executeQuery.execute(query, explain_prefix, shouldReconnect);
-            } else {
-              // Let it be for now
-            }
-          })
-          .fail(function() {
-          /* failure should not be ignored */
-          });
-
-      },
-
-      // Executes sql query in the editor in Query Tool mode
-      _execute_sql_query: function(explain_prefix, shouldReconnect) {
-        var self = this, sql = '';
-
-        self.has_more_rows = false;
-        self.fetching_rows = false;
-
-        if (!_.isUndefined(self.special_sql)) {
-          sql = self.special_sql;
-        } else {
-          /* If code is selected in the code mirror then execute
-           * the selected part else execute the complete code.
-           */
-          var selected_code = self.gridView.query_tool_obj.getSelection();
-          if (selected_code.length > 0)
-            sql = selected_code;
-          else
-            sql = self.gridView.query_tool_obj.getValue();
-        }
-
-        const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
-        executeQuery.poll = pgBrowser.override_activity_event_decorator(executeQuery.poll).bind(executeQuery);
-        executeQuery.execute(sql, explain_prefix, shouldReconnect);
-      },
-
-      // This is a wrapper to call_render function
-      // We need this because we have separated columns route & result route
-      // We need to combine both result here in wrapper before rendering grid
-      call_render_after_poll: function(queryResult) {
-        callRenderAfterPoll.callRenderAfterPoll(this, Notify, queryResult);
-      },
-
-
-      /* This function makes the ajax call to poll the result,
-       * if status is Busy then recursively call the poll function
-       * till the status is 'Success' or 'NotConnected'. If status is
-       * 'Success' then call the render method to render the data.
-       */
-      _poll: function() {
-        const executeQuery = new ExecuteQuery.ExecuteQuery(this, pgAdmin.Browser.UserManagement);
-        executeQuery.delayedPoll(this);
-      },
-
-      /* This function is used to create the slickgrid columns
-       * and render the data in the slickgrid.
-       */
-      _render: function(data) {
-        var self = this;
-        self.colinfo = data.colinfo;
-        self.primary_keys = (_.isEmpty(data.primary_keys) && data.has_oids) ? data.oids : data.primary_keys;
-        self.client_primary_key = data.client_primary_key;
-        self.cell_selected = false;
-        self.selected_model = null;
-        self.changedModels = [];
-        self.has_oids = data.has_oids;
-        self.oids = data.oids;
-        $('.sql-editor-explain').html(EMPTY_EXPLAIN_CONTENT);
-        self.explain_plan = false;
-
-        /* If object don't have primary keys then set the
-         * can_edit flag to false.
-         */
-        if ((self.primary_keys === null || self.primary_keys === undefined ||
-            _.size(self.primary_keys) === 0) && self.has_oids == false)
-          self.can_edit = false;
-        else
-          self.can_edit = true;
-
-        /* If user can filter the data then we should enabled
-         * Filter and Limit buttons.
-         */
-        if (self.can_filter) {
-          $('.limit').prop('disabled', false);
-          $('.limit').addClass('limit-enabled');
-          $('#btn-filter').prop('disabled', false);
-          $('#btn-filter-dropdown').prop('disabled', false);
-        }
-
-        // No data to save initially
-        $('#btn-save-data').prop('disabled', true);
-
-        // Initial settings for delete row, copy row and paste row buttons.
-        $('#btn-delete-row').prop('disabled', true);
-
-        if (!self.can_edit) {
-          $('#btn-delete-row').prop('disabled', true);
-          $('#btn-copy-row').prop('disabled', true);
-          $('#btn-paste-row').prop('disabled', true);
-        }
-
-        // Fetch the columns metadata
-        self._fetch_column_metadata.call(
-          self, data,
-          function() {
-            var self_col = this;
-
-            self_col.trigger(
-              'pgadmin-sqleditor:loading-icon:message',
-              gettext('Loading data from the database server and rendering...'),
-              self_col
-            );
-
-            // Show message in message and history tab in case of query tool
-            self_col.total_time = calculateQueryRunTime.calculateQueryRunTime(
-              self_col.query_start_time,
-              self_col.query_end_time
-            );
-            var msg1 = gettext('Successfully run. Total query runtime: %s.',self_col.total_time);
-            var msg2 = gettext('%s rows affected.',self_col.rows_affected);
-
-            // Display the notifier if the timeout is set to >= 0
-            if (self_col.info_notifier_timeout >= 0) {
-              Notify.success(msg1 + ' ' + msg2, self_col.info_notifier_timeout);
-            }
-
-            var _msg = msg1 + '\n' + msg2;
-
-
-            // If there is additional messages from server then add it to message
-            if (!_.isNull(data.additional_messages) &&
-              !_.isUndefined(data.additional_messages)) {
-              _msg = data.additional_messages + '\n' + _msg;
-            }
-
-            self_col.update_msg_history(true, _msg, false);
-
-            /* Add the data to the collection and render the grid.
-             * In case of Explain draw the graph on explain panel
-             * and add json formatted data to collection and render.
-             */
-            var explain_data_array = [],
-              explain_data_json = null;
-
-            if(data.result && !_.isEmpty(self_col.colinfo)
-             && self_col.colinfo[0].name == 'QUERY PLAN' && !_.isEmpty(data.types)
-             && data.types[0] && data.types[0].typname === 'json') {
-              /* json is sent as text, parse it */
-              explain_data_json = JSON.parse(data.result[0][0]);
-            }
-
-            if (explain_data_json && explain_data_json[0] &&
-              explain_data_json[0].hasOwnProperty('Plan') &&
-              _.isObject(explain_data_json[0]['Plan'])
-            ) {
-              var explain_data = [JSON.stringify(explain_data_json, null, 2)];
-              explain_data_array.push(explain_data);
-              // Make sure - the 'Data Output' panel is visible, before - we
-              // start rendering the grid.
-              self_col.gridView.data_output_panel.focus();
-              setTimeout(
-                function() {
-                  self_col.gridView.render_grid(
-                    explain_data_array, self_col.columns, self_col.can_edit,
-                    self_col.client_primary_key, 0
-                  );
-                  // Make sure - the 'Explain' panel is visible, before - we
-                  // start rendering the grid.
-                  self_col.gridView.explain_panel.focus();
-                  pgExplain.DrawJSONPlan(
-                    $('.sql-editor-explain'), explain_data_json
-                  );
-                }, 10
-              );
-            } else {
-              // Make sure - the 'Data Output' panel is visible, before - we
-              // start rendering the grid.
-              self_col.gridView.data_output_panel.focus();
-              setTimeout(
-                function() {
-                  self_col.gridView.render_grid(data.result, self_col.columns,
-                    self_col.can_edit, self_col.client_primary_key, data.rows_affected);
-                }, 10
-              );
-            }
-
-            // Hide the loading icon
-            self_col.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-            // Enable/Disable download button based on query result
-            if (!_.isUndefined(data) && Array.isArray(data.result) && data.result.length > 0) {
-              self.enable_disable_download_btn(false);
-            }
-            else {
-              self.enable_disable_download_btn(true);
-            }
-          }.bind(self)
-        );
-      },
-
-      // This function creates the columns as required by the backgrid
-      _fetch_column_metadata: function(data, cb) {
-        var colinfo = data.colinfo,
-          primary_keys = data.primary_keys,
-          columns = [],
-          self = this;
-        // Store pg_types in an array
-        var pg_types = new Array();
-        _.each(data.types, function(r) {
-          pg_types[r.oid] = [r.typname];
-        });
-
-        // Create columns required by slick grid to render
-        _.each(colinfo, function(c) {
-          var is_primary_key = false,
-            is_editable = self.can_edit && (!self.is_query_tool || c.is_editable);
-
-          // Check whether this column is a primary key
-          if (is_editable && _.size(primary_keys) > 0) {
-            _.each(primary_keys, function(value, key) {
-              if (key === c.name)
-                is_primary_key = true;
-            });
-          }
-
-          // To show column label and data type in multiline,
-          // The elements should be put inside the div.
-          // Create column label and type.
-          var col_type = '',
-            column_label = '',
-            col_cell;
-          var type = 'unknown';
-          if (pg_types[c.type_code]) {
-            type = pg_types[c.type_code][0];
-          } else if (pg_types[pg_types.length - 1][0]) {
-            type = pg_types[pg_types.length - 1][0];
-          }
-
-          if (!is_primary_key)
-            col_type += type;
-          else
-            col_type += '[PK] ' + type;
-
-          if (c.precision && c.precision >= 0 && c.precision != 65535) {
-            col_type += ' (' + c.precision;
-            col_type += c.scale && c.scale != 65535 ?
-              ',' + c.scale + ')' :
-              ')';
-          }
-          // Identify cell type of column.
-          switch (type) {
-          case 'oid':
-            col_cell = 'oid';
-            break;
-          case 'json':
-          case 'json[]':
-          case 'jsonb':
-          case 'jsonb[]':
-            col_cell = 'Json';
-            break;
-          case 'smallint':
-          case 'smallint[]':
-          case 'integer':
-          case 'integer[]':
-          case 'bigint':
-          case 'bigint[]':
-          case 'decimal':
-          case 'decimal[]':
-          case 'numeric':
-          case 'numeric[]':
-          case 'real':
-          case 'real[]':
-          case 'double precision':
-          case 'double precision[]':
-            col_cell = 'number';
-            break;
-          case 'boolean':
-            col_cell = 'boolean';
-            break;
-          case 'character':
-          case 'character[]':
-          case '"char"':
-          case '"char"[]':
-          case 'character varying':
-          case 'character varying[]':
-            if (c.internal_size && c.internal_size >= 0 && c.internal_size != 65535) {
-              // Update column type to display length on column header
-              col_type += ' (' + c.internal_size + ')';
-            }
-            col_cell = 'string';
-            break;
-          case 'bytea':
-          case 'bytea[]':
-            col_cell = 'binary';
-            break;
-          case 'geometry':
-            // PostGIS geometry type
-            col_cell = 'geometry';
-            break;
-          case 'geography':
-            // PostGIS geography type
-            col_cell = 'geography';
-            break;
-          default:
-            col_cell = 'string';
-          }
-
-          column_label = c.display_name + '<br>' + col_type;
-
-          var array_type_bracket_index = type.lastIndexOf('[]'),
-            col = {
-              'name': c.name,
-              'display_name': c.display_name,
-              'column_type': col_type,
-              'column_type_internal': type,
-              'pos': c.pos,
-              'label': column_label,
-              'cell': col_cell,
-              'can_edit': (c.name == 'oid') ? false : is_editable,
-              'type': type,
-              'not_null': c.not_null,
-              'has_default_val': c.has_default_val,
-              'is_array': array_type_bracket_index > -1 && array_type_bracket_index + 2 == type.length,
-            };
-          columns.push(col);
-        });
-
-        self.columns = columns;
-        if (cb && typeof(cb) == 'function') {
-          cb();
-        }
-      },
-
-      resetQueryHistoryObject: function(history) {
-        history.total_time = '-';
-      },
-
-      set_sql_message(msg, append=false) {
-        if(append) {
-          $('.sql-editor-message').append(msg);
-        } else {
-          $('.sql-editor-message').text(msg);
-        }
-
-        // Scroll automatically when msgs appends to element
-        setTimeout(function() {
-          $('.sql-editor-message').scrollTop($('.sql-editor-message')[0].scrollHeight);
-        }, 10);
-      },
-
-      // This function is used to raise appropriate message.
-      update_msg_history: function(status, msg, clear_grid) {
-        var self = this;
-        if (clear_grid === undefined)
-          clear_grid = true;
-
-        self.gridView.messages_panel.focus();
-
-        if (clear_grid) {
-          // Delete grid
-          if (self.gridView.handler.slickgrid) {
-            self.gridView.handler.slickgrid.destroy();
-
-          }
-          // Misc cleaning
-          self.columns = undefined;
-          self.collection = undefined;
-
-          self.set_sql_message(msg);
-        } else {
-          self.set_sql_message(_.escape(msg), true);
-        }
-
-        if (status != 'Busy') {
-          setTimeout(() => {
-            $('#btn-flash').prop('disabled', false);
-          }, 400);
-
-          self.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-          if(!self.total_time) {
-            self.total_time = calculateQueryRunTime.calculateQueryRunTime(
-              self.query_start_time,
-              new Date());
-          }
-
-          if(_.isUndefined(self.history_query_source)) {
-            self.history_query_source = QuerySources.VIEW_DATA;
-          }
-
-          let history_entry = {
-            'status': status,
-            'start_time': self.query_start_time,
-            'query': self.query,
-            'row_affected': self.rows_affected,
-            'total_time': self.total_time,
-            'message': msg,
-            'query_source': self.history_query_source,
-            'is_pgadmin_query': !self.is_query_tool,
-          };
-
-          if(!self.is_query_tool) {
-            var info_msg = gettext('This query was generated by pgAdmin as part of a "View/Edit Data" operation');
-            history_entry.info = info_msg;
-          }
-
-          self.add_to_history(history_entry);
-        }
-      },
-
-      /* Make ajax call to save the history data */
-      add_to_history: function(history_entry) {
-        var self = this;
-
-        $.ajax({
-          url: url_for('sqleditor.add_query_history', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(history_entry),
-        })
-          .done(function() { /* This is intentional */ })
-          .fail(function() { /* This is intentional */ });
-
-        self.gridView.history_collection.add(history_entry);
-      },
-
-      /* This function is used to check whether cell
-       * is editable or not depending on primary keys
-       * and staged_rows flag
-       */
-      is_editable: function(obj) {
-        var self = this;
-        if (obj instanceof Backbone.Collection)
-          return false;
-        return (self.get('can_edit'));
-      },
-
-      enable_disable_download_btn: function(is_save_results_to_file_disabled) {
-        var self = this;
-        self.is_save_results_to_file_disabled = is_save_results_to_file_disabled;
-        $('#btn-save-results-to-file').prop('disabled', is_save_results_to_file_disabled);
-      },
-
-      rows_to_delete: function(data) {
-        let self = this;
-        let tmp_keys = self.primary_keys;
-
-        // re-calculate rows with no primary keys
-        self.temp_new_rows = [];
-        data.forEach(function(d, idx) {
-          let p_keys_list = _.pick(d, _.keys(tmp_keys));
-          let is_primary_key = Object.keys(p_keys_list).length > 0;
-
-          if (!is_primary_key) {
-            self.temp_new_rows.push(idx);
-          }
-        });
-        self.rows_to_disable = _.clone(self.temp_new_rows);
-      },
-
-      // This function will delete selected row.
-      _delete: function() {
-        var self = this,
-          deleted_keys = [],
-          is_added = _.size(self.data_store.added),
-          is_updated = _.size(self.data_store.updated);
-
-        // Remove newly added rows from staged rows as we don't want to send them on server
-        if (is_added) {
-          _.each(self.data_store.added, function(val, key) {
-            if (key in self.data_store.staged_rows) {
-              // Remove the row from data store so that we do not send it on server
-              deleted_keys.push(key);
-            }
-          });
-        }
-        // If only newly rows to delete and no data is there to send on server
-        // then just re-render the grid
-        if (_.size(self.data_store.staged_rows) > 0 && (_.size(self.data_store.staged_rows) === _.size(deleted_keys))) {
-          var grid = self.slickgrid,
-            dataView = grid.getData();
-
-          grid.resetActiveCell();
-
-          dataView.beginUpdate();
-          for (let del_val of deleted_keys) {
-            delete self.data_store.staged_rows[del_val];
-            delete self.data_store.added[del_val];
-            delete self.data_store.added_index[del_val];
-            dataView.deleteItem(del_val);
-          }
-          dataView.endUpdate();
-          self.rows_to_delete.apply(self, [dataView.getItems()]);
-          grid.resetActiveCell();
-          grid.setSelectedRows([]);
-          grid.invalidate();
-
-          // Nothing to copy or delete here
-          $('#btn-delete-row').prop('disabled', true);
-          $('#btn-copy-row').prop('disabled', true);
-          if (_.size(self.data_store.added) || is_updated) {
-            // Do not disable save button if there are
-            // any other changes present in grid data
-            $('#btn-save-data').prop('disabled', false);
-          } else {
-            $('#btn-save-data').prop('disabled', true);
-          }
-          Notify.success(gettext('Row(s) deleted.'));
-        } else {
-
-          let strikeout = true;
-          _.each(_.keys(self.data_store.staged_rows), function(key) {
-            if(key in self.data_store.deleted) {
-              strikeout = false;
-            }
-          });
-
-          if (!strikeout) {
-            $(self.gridView.grid.getCanvasNode()).find('div.selected').removeClass('strikeout');
-            _.each(_.keys(self.data_store.staged_rows), function(key) {
-              if(key in self.data_store.deleted) {
-                delete self.data_store.deleted[key];
-              }
-            });
-          } else {
-            // Strike out the rows to be deleted
-            self.data_store.deleted = Object.assign({}, self.data_store.deleted, self.data_store.staged_rows);
-            $(self.gridView.grid.getCanvasNode()).find('div.selected').addClass('strikeout');
-          }
-
-          if (_.size(self.data_store.added) || is_updated || _.size(self.data_store.deleted)) {
-            // Do not disable save button if there are
-            // any other changes present in grid data
-            $('#btn-save-data').prop('disabled', false);
-          } else {
-            $('#btn-save-data').prop('disabled', true);
-          }
-        }
-      },
-
-      // This function will open save file dialog conditionally.
-
-      _save_file: function(save_as=false) {
-        var self = this;
-
-        var current_file = self.gridView.current_file;
-        if (!_.isUndefined(current_file) && !save_as) {
-          self._save_file_handler(current_file);
-        } else {
-          // provide custom option to save file dialog
-          var params = {
-            'supported_types': ['*', 'sql'],
-            'dialog_type': 'create_file',
-            'dialog_title': 'Save File',
-            'btn_primary': 'Save',
-          };
-          pgAdmin.FileManager.init();
-          pgAdmin.FileManager.show_dialog(params);
-        }
-      },
-
-      /* This function will fetch the list of changed models and make
-       * the ajax call to save the data into the database server.
-       */
-      save_data: function() {
-        var self = this;
-
-        if(!self.can_edit)
-          return;
-
-        var is_added = _.size(self.data_store.added),
-          is_updated = _.size(self.data_store.updated),
-          is_deleted = _.size(self.data_store.deleted);
-
-        if (!is_added && !is_updated && !is_deleted) {
-          return; // Nothing to save here
-        }
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Saving the updated data...')
-        );
-        // Disable query tool buttons and cancel button only if query tool
-        if(self.is_query_tool)
-          self.disable_tool_buttons(true, true);
-
-        // Add the columns to the data so the server can remap the data
-        var req_data = self.data_store, view = self.gridView;
-        req_data.columns = view ? view.handler.columns : self.columns;
-
-        var save_successful = false, save_start_time = new Date();
-
-        // Make ajax call to save the data
-        $.ajax({
-          url: url_for('sqleditor.save', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(req_data),
-        })
-          .done(function(res) {
-            var grid = self.slickgrid,
-              dataView = grid.getData(),
-              data_length = dataView.getLength(),
-              data = [];
-
-            var transaction_status = res.data.transaction_status;
-
-            // Update last transaction status
-            self.last_transaction_status = transaction_status;
-
-            var is_commit_required = transaction_status > 0; // 0 is idle
-
-            // Enable/Disable commit and rollback button.
-            if (is_commit_required) {
-              self.disable_transaction_buttons(false);
-            } else {
-              self.disable_transaction_buttons(true);
-            }
-            // Enable query tool buttons and cancel button only if query tool
-            if(self.is_query_tool)
-              self.disable_tool_buttons(false);
-
-            if (res.data.status) {
-              // Disable Save Data Changes button
-              $('#btn-save-data').prop('disabled', true);
-
-              save_successful = true;
-
-              // Remove highlighted cells styling
-              for (let i = 1; i <= self.numberOfModifiedCells; i++)
-                grid.removeCellCssStyles(i);
-
-              self.numberOfModifiedCells = 0;
-
-              if(is_added) {
-              // Update the rows in a grid after addition
-                dataView.beginUpdate();
-                _.each(res.data.query_results, function(r) {
-                  if (!_.isNull(r.row_added)) {
-                  // Fetch temp_id returned by server after addition
-                    var row_id = Object.keys(r.row_added)[0];
-                    _.each(req_data.added_index, function(v, k) {
-                      if (v == row_id) {
-                      // Fetch item data through row index
-                        var item_fetched = grid.getDataItem(k);
-                        _.extend(item_fetched, r.row_added[row_id]);
-                      }
-                    });
-                  }
-                });
-                dataView.endUpdate();
-              }
-              // Remove flag is_row_copied from copied rows
-              _.each(data, function(row) {
-                if (row.is_row_copied) {
-                  delete row.is_row_copied;
-                }
-              });
-
-              // Remove 2d copied_rows array
-              if (grid.copied_rows) {
-                delete grid.copied_rows;
-              }
-
-              // Remove deleted rows from client as well
-              if (is_deleted) {
-                var rows = _.keys(self.data_store.deleted);
-                if (data_length == rows.length) {
-                // This means all the rows are selected, clear all data
-                  data = [];
-                  dataView.setItems(data, self.client_primary_key);
-                } else {
-                  dataView.beginUpdate();
-                  for (let row_val of rows) {
-                    var item = grid.getData().getItemById(row_val);
-                    data.push(item);
-                    dataView.deleteItem(item[self.client_primary_key]);
-                  }
-                  dataView.endUpdate();
-                }
-                self.rows_to_delete.apply(self, [data]);
-              }
-
-              grid.setSelectedRows([]);
-
-              // Reset data store
-              self.reset_data_store();
-
-              // Reset old primary key data now
-              self.primary_keys_data = {};
-
-              // Clear msgs after successful save
-              self.set_sql_message('');
-
-              Notify.success(gettext('Data saved successfully.'));
-
-              if(is_commit_required)
-                Notify.info(gettext('Auto-commit is off. You still need to commit changes to the database.'));
-
-
-            } else {
-            // Something went wrong while saving data on the db server
-              self.set_sql_message(res.data.result);
-              var err_msg = gettext('%s.', res.data.result);
-              Notify.error(err_msg, 20000);
-              // If the transaction is not idle, notify the user that previous queries are not rolled back,
-              // only the failed save queries.
-              if (transaction_status != 0)
-                Notify.info(gettext('Saving data changes was rolled back but the current transaction is ' +
-                                      'still active; previous queries are unaffected.'));
-              grid.setSelectedRows([]);
-              // To highlight the row at fault
-              if (_.has(res.data, '_rowid') &&
-              (!_.isUndefined(res.data._rowid) || !_.isNull(res.data._rowid))) {
-                var _row_index = self._find_rowindex(res.data._rowid);
-                if (_row_index in self.data_store.added_index) {
-                // Remove new row index from temp_list if save operation
-                // fails
-                  var index = self.handler.temp_new_rows.indexOf(res.data._rowid);
-                  if (index > -1) {
-                    self.handler.temp_new_rows.splice(index, 1);
-                  }
-                  self.data_store.added[self.data_store.added_index[_row_index]].err = true;
-                } else if (_row_index in self.data_store.updated_index) {
-                  self.data_store.updated[self.data_store.updated_index[_row_index]].err = true;
-                }
-              }
-              grid.gotoCell(_row_index, 1);
-            }
-
-            var query_history_info_msg = gettext('This query was generated by pgAdmin as part of a "Save Data" operation');
-
-            _.each(res.data.query_results, function(r) {
-              var history_entry = {
-                'status': r.status,
-                'start_time': save_start_time,
-                'query': r.sql,
-                'row_affected': r.rows_affected,
-                'total_time': null,
-                'message': r.result,
-                'query_source': QuerySources.SAVE_DATA,
-                'is_pgadmin_query': true,
-                'info': query_history_info_msg,
-              };
-              self.add_to_history(history_entry);
-            });
-
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-            grid.invalidate();
-            if (self.close_on_save) {
-              if(save_successful) {
-                // Check for any other needed confirmations before closing
-                self.check_needed_confirmations_before_closing_panel();
-              }
-              else {
-                self.close_on_save = false;
-              }
-            }
-          })
-          .fail(function(e) {
-            let stateParams = [view];
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, 'save_data', stateParams, true
-            );
-            // Enable query tool buttons and cancel button only if query tool
-            if(self.is_query_tool)
-              self.disable_tool_buttons(false);
-            if (msg)
-              self.update_msg_history(false, msg);
-          });
-      },
-
-      reset_data_store: function() {
-        var self = this;
-        // This holds all the inserted/updated/deleted data from grid
-        self.data_store = {
-          updated: {},
-          added: {},
-          staged_rows: {},
-          deleted: {},
-          updated_index: {},
-          added_index: {},
-        };
-      },
-
-      // Find index of row at fault from grid data
-      _find_rowindex: function(rowid) {
-        var self = this,
-          grid = self.slickgrid,
-          dataView = grid.getData(),
-          data = dataView.getItems(),
-          _rowid,
-          count = 0,
-          _idx = -1;
-
-        // If _rowid is object then it's update/delete operation
-        if (_.isObject(rowid)) {
-          _rowid = rowid;
-        } else if (_.isString(rowid)) { // Insert operation
-          rowid = {};
-          rowid[self.client_primary_key] = rowid;
-          _rowid = rowid;
-        } else {
-          // Something is wrong with unique id
-          return _idx;
-        }
-
-        _.find(data, function(d) {
-          // search for unique id in row data if found than its the row
-          // which error out on server side
-          var tmp = []; //_.findWhere needs array of object to work
-          tmp.push(d);
-          if (_.findWhere(tmp, _rowid)) {
-            _idx = count;
-            // Now exit the loop by returning true
-            return true;
-          }
-          count++;
-        });
-
-        // Not able to find in grid Data
-        return _idx;
-      },
-
-      // Save as
-      _save_as: function() {
-        return this._save_file(true);
-      },
-
-      // Set panel title.
-      setTitle: function(title, is_file, is_dirty_editor=false) {
-        var self = this;
-        var open_new_tab = self.browser_preferences.new_browser_tab_open;
-        if(open_new_tab && open_new_tab.includes('qt')) {
-          window.document.title = _.escape(title);
-        } else {
-          _.each(pgWindow.default.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
-            if (p.isVisible()) {
-              if(is_dirty_editor) {
-                p.is_dirty_editor = true;
-              }
-              panelTitleFunc.setQueryToolDockerTitle(p, self.is_query_tool, title, is_file);
-            }
-          });
-        }
-      },
-
-      // load select file dialog
-      _load_file: function() {
-        var self = this;
-
-        /* If is_query_changed flag is set to false then no need to
-         * confirm with the user for unsaved changes.
-         */
-        if (self.is_query_changed) {
-          Notify.confirm(gettext('Unsaved changes'),
-            gettext('Are you sure you wish to discard the current changes?'),
-            function() {
-              // User do not want to save, just continue
-              self._open_select_file_manager();
-            },
-            function() {
-              return true;
-            }
-          );
-        } else {
-          self._open_select_file_manager();
-        }
-
-      },
-
-      // Open FileManager
-      _open_select_file_manager: function() {
-        var params = {
-          'supported_types': ['*', 'sql'], // file types allowed
-          'dialog_type': 'select_file', // open select file dialog
-        };
-        pgAdmin.FileManager.init();
-        pgAdmin.FileManager.show_dialog(params);
-      },
-
-      // read file data and return as response
-      _select_file_handler: function(e) {
-        var self = this,
-          _e = e,
-          data = {
-            'file_name': decodeURI(e),
-          };
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Loading the file...')
-        );
-        // set cursor to progress before file load
-        var $busy_icon_div = $('.sql-editor-busy-fetching');
-        $busy_icon_div.addClass('show_progress');
-
-        // Make ajax call to load the data from file
-        $.ajax({
-          url: url_for('sqleditor.load_file'),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(data),
-        })
-          .done(function(res) {
-            self.gridView.query_tool_obj.setValue(res);
-            self.gridView.current_file = e;
-            self.gridView.query_tool_obj.file_data = res;
-            self.setTitle(self.gridView.current_file.split('\\').pop().split('/').pop(), true);
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            // hide cursor
-            $busy_icon_div.removeClass('show_progress');
-
-            // disable save button on file save
-            $('#btn-save-file').prop('disabled', true);
-            $('#btn-file-menu-save').css('display', 'none');
-
-            // Update the flag as new content is just loaded.
-            self.is_query_changed = false;
-            setTimeout(() => { self.gridView.query_tool_obj.focus(); }, 200);
-          })
-          .fail(function(er) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let stateParams = [_e];
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, er, '_select_file_handler', stateParams, false
-            );
-            if (msg)
-              Notify.error(msg);
-            // hide cursor
-            $busy_icon_div.removeClass('show_progress');
-          });
-      },
-
-      // read data from codemirror and write to file
-      _save_file_handler: function(e) {
-        var self = this,
-          _e = e,
-          data = {
-            'file_name': decodeURI(e),
-            'file_content': self.gridView.query_tool_obj.getValue(),
-          };
-        var file_data = self.gridView.query_tool_obj.getValue();
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Saving the queries in the file...')
-        );
-
-        // Make ajax call to save the data to file
-        $.ajax({
-          url: url_for('sqleditor.save_file'),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(data),
-        })
-          .done(function(res) {
-            if (res.data.status) {
-              Notify.success(gettext('File saved successfully.'));
-              self.gridView.current_file = e;
-              self.setTitle(self.gridView.current_file.replace(/^.*[\\\/]/g, ''), true);
-              self.gridView.query_tool_obj.file_data = file_data;
-              // disable save button on file save
-              $('#btn-save-file').prop('disabled', true);
-              $('#btn-file-menu-save').css('display', 'none');
-
-              // Update the flag as query is already saved.
-              self.is_query_changed = false;
-              setTimeout(() => { self.gridView.query_tool_obj.focus(); }, 200);
-            }
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            if (self.close_on_save) {
-              // Check for any other needed confirmations before closing
-              self.check_needed_confirmations_before_closing_panel();
-            }
-          })
-          .fail(function(er) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let stateParams = [_e];
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, er, '_save_file_handler', stateParams, false
-            );
-            if (msg)
-              Notify.error(msg);
-          });
-      },
-
-      // codemirror text change event
-      _on_query_change: function() {
-        var self = this;
-
-        if (!self.is_query_changed) {
-          // Update the flag as query is going to changed.
-          self.is_query_changed = true;
-
-          if (self.gridView.current_file) {
-            var title = self.gridView.current_file.replace(/^.*[\\\/]/g, '') + ' *';
-            self.setTitle(title, true);
-          } else {
-            var open_new_tab = self.browser_preferences.new_browser_tab_open;
-            var is_dirty_editor = false;
-            if(open_new_tab && open_new_tab.includes('qt')) {
-              title = window.document.title + ' *';
-            } else {
-              // Find the title of the visible panel
-              _.each(pgWindow.default.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
-                if (p.isVisible()) {
-                  self.gridView.panel_title = $(p._title).text();
-                }
-              });
-
-              title = self.gridView.panel_title + ' *';
-              is_dirty_editor = true;
-            }
-            self.setTitle(title, false, is_dirty_editor);
-          }
-
-          $('#btn-save-file').prop('disabled', false);
-          $('#btn-file-menu-save').css('display', 'block');
-          $('#btn-file-menu-dropdown').prop('disabled', false);
-        } else {
-          if(self.gridView.current_file) {
-            if (self.gridView.query_tool_obj.file_data == self.gridView.query_tool_obj.getValue()) {
-              title = self.gridView.current_file.replace(/^.*[\\\/]/g, '');
-              is_dirty_editor = false;
-            } else {
-              title = self.gridView.current_file.replace(/^.*[\\\/]/g, '') + ' *';
-              is_dirty_editor = true;
-            }
-
-            self.setTitle(title, true, is_dirty_editor);
-          }
-        }
-      },
-
-      // This function will set the required flag for polling response data
-      _init_polling_flags: function() {
-        var self = this;
-
-        // To get a timeout for polling fallback timer in seconds in
-        // regards to elapsed time
-        self.POLL_FALLBACK_TIME = function() {
-          var seconds = parseInt((Date.now() - self.query_start_time.getTime()) / 1000);
-          // calculate & return fall back polling timeout
-          if (seconds >= 10 && seconds < 30) {
-            return 500;
-          } else if (seconds >= 30 && seconds < 60) {
-            return 1000;
-          } else if (seconds >= 60 && seconds < 90) {
-            return 2000;
-          } else if (seconds >= 90) {
-            return 5000;
-          } else {
-            return 1;
-          }
-        };
-      },
-      // This function will show the filter in the text area.
-      _show_filter: function() {
-        let self = this,
-          reconnect = false;
-
-        /* When server is disconnected and connected, connection is lost,
-         * To reconnect pass true
-         */
-        if (arguments.length > 0 &&
-          arguments[arguments.length - 1] == 'connect') {
-          reconnect = true;
-        }
-        FilterHandler.dialog(self, reconnect);
-      },
-      // This function will show the new connection.
-      _show_new_connection: function() {
-        let self = this,
-          reconnect = false;
-
-        /* When server is disconnected and connected, connection is lost,
-         * To reconnect pass true
-         */
-        if (arguments.length > 0 && arguments[arguments.length - 1] == 'connect') {
-          reconnect = true;
-        }
-
-        newConnectionHandler.dialog(self, reconnect, self.browser_preferences);
-      },
-      // This function will include the filter by selection.
-      _include_filter: function() {
-        var self = this,
-          data = {},
-          grid, active_column, column_info, _values;
-
-        grid = self.slickgrid;
-        active_column = grid.getActiveCell();
-
-        // If no cell is selected then return from the function
-        if (_.isNull(active_column) || _.isUndefined(active_column))
-          return;
-
-        column_info = grid.getColumns()[active_column.cell];
-
-        // Fetch current row data from grid
-        _values = grid.getDataItem(active_column.row, active_column.cell);
-        if (_.isNull(_values) || _.isUndefined(_values))
-          return;
-
-        // Add column position and it's value to data
-        data[column_info.field] = _.isUndefined(_values[column_info.field]) ? '' : _values[column_info.field];
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Applying the new filter...')
-        );
-
-        // Make ajax call to include the filter by selection
-        $.ajax({
-          url: url_for('sqleditor.inclusive_filter', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(data),
-        })
-          .done(function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            setTimeout(
-              function() {
-                if (res.data.status) {
-                // Refresh the sql grid
-                  queryToolActions.executeQuery(self);
-                } else {
-                  Notify.alert(gettext('Filter By Selection Error'), res.data.result);
-                }
-              }
-            );
-          })
-          .fail(function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_include_filter', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Filter By Selection Error'), msg);
-          });
-      },
-
-      // This function will exclude the filter by selection.
-      _exclude_filter: function() {
-        var self = this,
-          data = {},
-          grid, active_column, column_info, _values;
-
-        grid = self.slickgrid;
-        active_column = grid.getActiveCell();
-
-        // If no cell is selected then return from the function
-        if (_.isNull(active_column) || _.isUndefined(active_column))
-          return;
-
-        column_info = grid.getColumns()[active_column.cell];
-
-        // Fetch current row data from grid
-        _values = grid.getDataItem(active_column.row, active_column.cell);
-        if (_.isNull(_values) || _.isUndefined(_values))
-          return;
-
-        // Add column position and it's value to data
-        data[column_info.field] = _.isUndefined(_values[column_info.field]) ? '' : _values[column_info.field];
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Applying the new filter...')
-        );
-
-        // Make ajax call to exclude the filter by selection.
-        $.ajax({
-          url: url_for('sqleditor.exclusive_filter', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(data),
-        })
-          .done(function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            setTimeout(
-              function() {
-                if (res.data.status) {
-                // Refresh the sql grid
-                  queryToolActions.executeQuery(self);
-                } else {
-                  Notify.alert(gettext('Filter Exclude Selection Error'), res.data.result);
-                }
-              }, 10
-            );
-          })
-          .fail(function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_exclude_filter', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Filter Exclude Selection Error'), msg);
-          });
-      },
-
-      // This function will remove the filter.
-      _remove_filter: function() {
-        var self = this;
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Removing the filter...')
-        );
-
-        // Make ajax call to exclude the filter by selection.
-        $.ajax({
-          url: url_for('sqleditor.remove_filter', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-        })
-          .done(function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            setTimeout(
-              function() {
-                if (res.data.status) {
-                // Refresh the sql grid
-                  queryToolActions.executeQuery(self);
-                } else {
-                  Notify.alert(gettext('Remove Filter Error'), res.data.result);
-                }
-              }
-            );
-          })
-          .fail(function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_remove_filter', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Remove Filter Error'), msg);
-          });
-      },
-
-      // This function will copy the selected row.
-      _copy_row: copyData,
-
-      _copy_row_with_header: function() {
-        $('.copy-with-header').toggleClass('visibility-hidden');
-      },
-
-      // This function will enable Paste button if data is copied in some other active session
-      _copied_in_other_session: function(e, copiedWithHeaders) {
-        pgAdmin.SqlEditor.copiedInOtherSessionWithHeaders = copiedWithHeaders;
-        $('#btn-paste-row').prop('disabled', false);
-      },
-
-      // This function will paste the selected row.
-      _paste_row: function() {
-        var self = this;
-        let rowsText = clipboard.getTextFromClipboard();
-        let copied_rows = pgadminUtils.CSVToArray(rowsText, self.preferences.results_grid_field_separator, self.preferences.results_grid_quote_char);
-        // Do not parse row if rows are copied with headers
-        if(pgAdmin.SqlEditor.copiedInOtherSessionWithHeaders) {
-          copied_rows = copied_rows.slice(1);
-        }
-        var row_index = 0;
-        copied_rows = copied_rows.reduce((partial, values) => {
-          // split each row with field separator character
-          let row = {};
-          for (let col in self.columns) {
-            let v = values[col];
-
-            // set value to default or null depending on column metadata
-            if(v === '') {
-              if(self.columns[col].has_default_val) {
-                v = undefined;
-              } else if (self.copied_rows[row_index][self.columns[col].display_name] === null) {
-                v = null;
-              } else {
-                v = '';
-              }
-            }
-
-            if(self.columns[col].cell === 'boolean') {
-              if(v == 'true') {
-                v = true;
-              } else if(v == 'false') {
-                v = false;
-              } else {
-                v = null;
-              }
-            }
-
-            row[self.columns[col].name] = v;
-          }
-          partial.push(row);
-          row_index ++;
-          return partial;
-        }, []);
-
-        // If there are rows to paste?
-        if (copied_rows.length > 0) {
-          // Enable save button so that user can
-          // save newly pasted rows on server
-          $('#btn-save-data').prop('disabled', false);
-
-          var grid = self.slickgrid,
-            dataView = grid.getData(),
-            count = dataView.getLength(),
-            array_types = [];
-          // for quick look up create list of array data types
-          for (var k in self.columns) {
-            if (self.columns[k].is_array) {
-              array_types.push(self.columns[k].name);
-            }
-          }
-
-          var arr_to_object = function (arr) {
-            var obj = {};
-
-            _.each(arr, function (val, i) {
-              if (arr[i] !== undefined) {
-                // Do not stringify array types.
-                if (_.isObject(arr[i]) && array_types.indexOf(i) == -1) {
-                  obj[String(i)] = JSON.stringify(arr[i]);
-                } else {
-                  obj[String(i)] = arr[i];
-                }
-              }
-            });
-            return obj;
-          };
-
-          // Generate Unique key for each pasted row(s)
-          // Convert array values to object to send to server
-          // Add flag is_row_copied to handle [default] and [null]
-          // for copied rows.
-          // Add index of copied row into temp_new_rows
-          // Trigger grid.onAddNewRow when a row is copied
-          // Reset selection
-
-          dataView.beginUpdate();
-          _.each(copied_rows, function (row) {
-            var new_row = arr_to_object(row),
-              _key = (self.gridView.client_primary_key_counter++).toString();
-            new_row.is_row_copied = true;
-            self.temp_new_rows.push(count);
-            new_row[self.client_primary_key] = _key;
-            if (self.has_oids && new_row.oid) {
-              new_row.oid = null;
-            }
-
-            dataView.addItem(new_row);
-            self.data_store.added[_key] = {
-              'err': false,
-              'data': new_row,
-            };
-            self.data_store.added_index[count] = _key;
-            count++;
-          });
-          dataView.endUpdate();
-          grid.invalidateRow(grid.getSelectedRows());
-          grid.updateRowCount();
-          grid.render();
-          // Pasted row/s always append so bring last row in view port.
-          grid.scrollRowIntoView(dataView.getLength());
-          grid.setSelectedRows([]);
-        }
-      },
-
-      // This function will set the limit for SQL query
-      _set_limit: function() {
-        var self = this,
-          limit = parseInt($('.limit').val());
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Setting the limit on the result...')
-        );
-        // Make ajax call to change the limit
-        $.ajax({
-          url: url_for('sqleditor.set_limit', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(limit),
-        })
-          .done(function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            setTimeout(
-              function() {
-                if (res.data.status) {
-                // Refresh the sql grid
-                  queryToolActions.executeQuery(self);
-                } else {
-                  Notify.alert(gettext('Change limit Error'), res.data.result);
-                }
-              }, 10
-            );
-          })
-          .fail(function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_set_limit', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Change limit Error'), msg);
-          });
-      },
-
-      // This function is used to enable/disable buttons
-      disable_tool_buttons: function(disabled, disable_cancel=null) {
-        let mode_disabled = disabled;
-
-        /* Buttons be always disabled in view/edit mode */
-        if(!this.is_query_tool) {
-          mode_disabled = true;
-        }
-
-        $('#btn-explain').prop('disabled', mode_disabled);
-        $('#btn-explain-analyze').prop('disabled', mode_disabled);
-        $('#btn-explain-options-dropdown').prop('disabled', mode_disabled);
-        $('#btn-edit-dropdown').prop('disabled', mode_disabled);
-        $('#btn-load-file').prop('disabled', mode_disabled);
-        if(this.gridView.current_file) {
-          if(this.gridView.query_tool_obj.file_data != this.gridView.query_tool_obj.getValue()) {
-            $('#btn-save-file').prop('disabled', mode_disabled);
-          }
-        } else {
-          $('#btn-save-file').prop('disabled', mode_disabled);
-        }
-
-        $('#btn-file-menu-dropdown').prop('disabled', mode_disabled);
-        $('#btn-find').prop('disabled', mode_disabled);
-        $('#btn-find-menu-dropdown').prop('disabled', mode_disabled);
-        $('#btn-macro-dropdown').prop('disabled', mode_disabled);
-
-        if (this.is_query_tool) {
-
-          if(disable_cancel !== null)
-            $('#btn-cancel-query').prop('disabled', disable_cancel);
-          // Cancel query tool needs opposite behaviour if not explicitly given
-          else
-            $('#btn-cancel-query').prop('disabled', !disabled);
-
-          if(this.is_transaction_buttons_disabled) {
-            $('#btn-query-dropdown').prop('disabled', disabled);
-          } else {
-            $('#btn-query-dropdown').prop('disabled', true);
-          }
-        } else {
-          $('#btn-query-dropdown').prop('disabled', mode_disabled);
-        }
-      },
-
-      // This function is used to enable/disable commit/rollback buttons
-      disable_transaction_buttons: function(disabled) {
-        this.is_transaction_buttons_disabled = disabled;
-        if (this.is_query_tool) {
-          $('#btn-commit').prop('disabled', disabled);
-          $('#btn-rollback').prop('disabled', disabled);
-        }
-      },
-
-      /* This function is used to highlight the error line and
-       * underlining for the error word.
-       */
-      _highlight_error: function(result) {
-        var self = this,
-          error_line_no = 0,
-          start_marker = 0,
-          end_marker = 0,
-          selected_line_no = 0;
-
-        // Remove already existing marker
-        self.gridView.query_tool_obj.removeLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
-
-        // In case of selection we need to find the actual line no
-        if (self.gridView.query_tool_obj.getSelection().length > 0)
-          selected_line_no = self.gridView.query_tool_obj.getCursor(true).line;
-
-        // Fetch the LINE string using regex from the result
-        var line = /LINE (\d+)/.exec(result),
-          // Fetch the Character string using regex from the result
-          char = /Character: (\d+)/.exec(result);
-
-        // If line and character is null then no need to mark
-        if (line != null && char != null) {
-          error_line_no = self.marked_line_no = (parseInt(line[1]) - 1) + selected_line_no;
-          var error_char_no = (parseInt(char[1]) - 1);
-
-          /* We need to loop through each line till the error line and
-           * count the total no of character to figure out the actual
-           * starting/ending marker point for the individual line. We
-           * have also added 1 per line for the "\n" character.
-           */
-          var prev_line_chars = 0;
-          var loop_index = selected_line_no > 0 ? selected_line_no : 0;
-          for (var i = loop_index; i < error_line_no; i++)
-            prev_line_chars += self.gridView.query_tool_obj.getLine(i).length + 1;
-
-          /* Marker starting point for the individual line is
-           * equal to error character index minus total no of
-           * character till the error line starts.
-           */
-          start_marker = error_char_no - prev_line_chars;
-
-          // Find the next space from the character or end of line
-          var error_line = self.gridView.query_tool_obj.getLine(error_line_no);
-
-          if (_.isUndefined(error_line)) return;
-          end_marker = error_line.indexOf(' ', start_marker);
-          if (end_marker < 0)
-            end_marker = error_line.length;
-
-          // Mark the error text
-          self.gridView.marker = self.gridView.query_tool_obj.markText({
-            line: error_line_no,
-            ch: start_marker,
-          }, {
-            line: error_line_no,
-            ch: end_marker,
-          }, {
-            className: 'sql-editor-mark',
-          });
-
-          self.gridView.query_tool_obj.addLineClass(self.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
-        }
-      },
-
-      // This function will cancel the running query.
-      _cancel_query: function() {
-        var self = this;
-        $.ajax({
-          url: url_for('sqleditor.cancel_transaction', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-        })
-          .done(function(res) {
-            if (!res.data.status) {
-              Notify.alert(gettext('Cancel Query Error'), res.data.result);
-            }
-            self.disable_tool_buttons(false);
-            is_query_running = false;
-            if(!_.isUndefined(self.download_results_obj)) {
-              self.download_results_obj.abort();
-              $('#btn-flash').prop('disabled', false);
-              self.trigger(
-                'pgadmin-sqleditor:loading-icon:hide');
-            }
-            setTimeout(() => { self.gridView.query_tool_obj.focus(); }, 200);
-          })
-          .fail(function(e) {
-            self.disable_tool_buttons(false);
-
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_cancel_query', [], false
-            );
-            if (msg)
-              Notify.alert(gettext('Cancel Query Error'), msg);
-          });
-      },
-
-      // Trigger query result download to csv.
-      trigger_csv_download: function(filename) {
-        var self = this,
-          url = url_for('sqleditor.query_tool_download', {
-            'trans_id': self.transId,
-          }),
-          data = { filename: filename };
-
-        // Disable the Execute button
-        $('#btn-flash').prop('disabled', true);
-        self.enable_disable_download_btn(true);
-        self.disable_tool_buttons(true);
-        self.set_sql_message('');
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Downloading Results...')
-        );
-
-        // Get the CSV file
-        self.download_results_obj = $.ajax({
-          type: 'POST',
-          url: url,
-          data: data,
-          cache: false,
-        }).done(function(response) {
-          // if response.data present, extract the message
-          if(!_.isUndefined(response.data)) {
-            if(!response.status) {
-              self._highlight_error(response.data.result);
-              self.set_sql_message(response.data.result);
-            }
-          } else {
-            let respBlob = new Blob([response], {type : 'text/csv'}),
-              urlCreator = window.URL || window.webkitURL,
-              download_url = urlCreator.createObjectURL(respBlob),
-              current_browser = pgAdmin.Browser.get_browser(),
-              link = document.createElement('a');
-
-            document.body.appendChild(link);
-
-            if (current_browser.name === 'IE' && window.navigator.msSaveBlob) {
-            // IE10+ : (has Blob, but not a[download] or URL)
-              window.navigator.msSaveBlob(respBlob, filename);
-            } else {
-              link.setAttribute('href', download_url);
-              link.setAttribute('download', filename);
-              link.click();
-            }
-
-            document.body.removeChild(link);
-            self.download_results_obj = undefined;
-          }
-
-          // Enable the execute button
-          $('#btn-flash').prop('disabled', false);
-          self.enable_disable_download_btn(false);
-          self.disable_tool_buttons(false);
-          self.trigger('pgadmin-sqleditor:loading-icon:hide');
-        }).fail(function(err) {
-          let msg = '';
-          // Enable the execute button
-          $('#btn-flash').prop('disabled', false);
-          self.enable_disable_download_btn(false);
-          self.disable_tool_buttons(false);
-          self.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-
-          if (err.statusText == 'abort') {
-            msg = gettext('CSV Download cancelled.');
-          } else {
-            msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, err, 'trigger_csv_download', [], true
-            );
-          }
-          // Check if error message is present
-          if (msg)
-            Notify.alert(gettext('Download CSV error'), msg);
-        });
-      },
-
-      call_cache_preferences: function() {
-        let browser = pgWindow.default.pgAdmin.Browser;
-        browser.cache_preferences('sqleditor');
-
-        /* This will make sure to get latest updates only and not older events */
-        pgBrowser.preference_version(pgBrowser.generate_preference_version());
-      },
-
-      _auto_rollback: function() {
-        var self = this,
-          auto_rollback = true;
-
-        if ($('.auto-rollback').hasClass('visibility-hidden') === true)
-          $('.auto-rollback').removeClass('visibility-hidden');
-        else {
-          $('.auto-rollback').addClass('visibility-hidden');
-          auto_rollback = false;
-        }
-
-        // Make ajax call to change the limit
-        $.ajax({
-          url: url_for('sqleditor.auto_rollback', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(auto_rollback),
-        })
-          .done(function(res) {
-            if (!res.data.status)
-              Notify.alert(gettext('Auto Rollback Error'), res.data.result);
-          })
-          .fail(function(e) {
-
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_auto_rollback', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Auto Rollback Error'), msg);
-          });
-      },
-
-      _auto_commit: function() {
-        var self = this,
-          auto_commit = true;
-
-        if ($('.auto-commit').hasClass('visibility-hidden') === true)
-          $('.auto-commit').removeClass('visibility-hidden');
-        else {
-          $('.auto-commit').addClass('visibility-hidden');
-          auto_commit = false;
-        }
-
-        // Make ajax call to toggle auto commit
-        $.ajax({
-          url: url_for('sqleditor.auto_commit', {
-            'trans_id': self.transId,
-          }),
-          method: 'POST',
-          contentType: 'application/json',
-          data: JSON.stringify(auto_commit),
-        })
-          .done(function(res) {
-            if (!res.data.status)
-              Notify.alert(gettext('Auto Commit Error'), res.data.result);
-          })
-          .fail(function(e) {
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_auto_commit', [], true
-            );
-            if (msg)
-              Notify.alert(gettext('Auto Commit Error'), msg);
-          });
-
-      },
-
-      _toggle_explain_option: function(type) {
-        let selector = `.explain-${type}`;
-        $(selector).toggleClass('visibility-hidden');
-      },
-
-      // This function will toggle "verbose" option in explain
-      _explain_verbose: function() {
-        this._toggle_explain_option('verbose');
-      },
-
-      // This function will toggle "costs" option in explain
-      _explain_costs: function() {
-        this._toggle_explain_option('costs');
-      },
-
-      // This function will toggle "buffers" option in explain
-      _explain_buffers: function() {
-        this._toggle_explain_option('buffers');
-      },
-
-      // This function will toggle "timing" option in explain
-      _explain_timing: function() {
-        this._toggle_explain_option('timing');
-      },
-
-      _explain_summary: function() {
-        this._toggle_explain_option('summary');
-      },
-
-      _explain_settings: function() {
-        this._toggle_explain_option('settings');
-      },
-
-      _show_query_tool: function() {
-        var self = this;
-        var open_new_tab = self.browser_preferences.new_browser_tab_open;
-        if (open_new_tab && open_new_tab.includes('qt')) {
-          is_main_window_alive();
-        }
-        this._open_query_tool(self);
-      },
-
-      _open_query_tool: function(that) {
-
-        const transId = pgadminUtils.getRandomInt(1, 9999999);
-
-        let url_endpoint = url_for('datagrid.panel', {
-          'trans_id': transId,
-        });
-
-        url_endpoint += `?is_query_tool=${true}`
-          +`&sgid=${that.url_params.sgid}`
-          +`&sid=${that.url_params.sid}`
-          +`&server_type=${that.url_params.server_type}`;
-
-        if(that.url_params.did) {
-          url_endpoint += `&did=${that.url_params.did}`;
-        }
-
-        let panel_title = that.url_params.title;
-        if(that.url_params.is_query_tool == 'false') {//check whether query tool is hit from View/Edit
-          var split_title = that.url_params.title.split('/');
-          if(split_title.length > 2) {
-            panel_title = split_title[split_title.length-2] + '/' + split_title[split_title.length-1];
-          }
-        }
-
-        launchDataGrid(pgWindow.default.pgAdmin.DataGrid, transId, url_endpoint, panel_title, '', alertify);
-      },
-
-      /*
-       * This function will indent selected code
-       */
-      _indent_selected_code: function() {
-        var self = this,
-          editor = self.gridView.query_tool_obj;
-        editor.execCommand('indentMore');
-      },
-
-      /*
-       * This function will unindent selected code
-       */
-      _unindent_selected_code: function() {
-        var self = this,
-          editor = self.gridView.query_tool_obj;
-        editor.execCommand('indentLess');
-      },
-
-      /*
-       * This function will format the SQL
-       */
-      _format_sql: function() {
-        var self = this,
-          editor = self.gridView.query_tool_obj,
-          selection = true,
-          sql = '';
-
-        sql = editor.getSelection();
-
-        if (sql == '') {
-          sql = editor.getValue();
-          selection = false;
-        }
-
-        $.ajax({
-          url: url_for('sql.format'),
-          data: JSON.stringify({'sql': sql}),
-          method: 'POST',
-          contentType: 'application/json',
-          dataType: 'json',
-        })
-          .done(function(res) {
-            if (selection === true) {
-              editor.replaceSelection(res.data.sql, 'around');
-            } else {
-              editor.setValue(res.data.sql);
-            }
-          })
-          .fail(function() {
-          /* failure should be ignored */
-          });
-      },
-
-      // This function will open the manage macro dialog
-      _manage_macros: function() {
-        let self = this;
-
-        /* When server is disconnected and connected, connection is lost,
-         * To reconnect pass true
-         */
-        MacroHandler.dialog(self);
-      },
-
-      // This function will open the manage macro dialog
-      _execute_macro: function() {
-
-        queryToolActions.executeMacro(this.handler);
-
-      },
-
-
-      isQueryRunning: function() {
-        return is_query_running;
-      },
-
-      setIsQueryRunning: function(value) {
-        is_query_running = value;
-      },
-
-      /* Checks if there is any unsaved data changes, unsaved changes in the query
-      or uncommitted transactions before closing a panel */
-      check_needed_confirmations_before_closing_panel: function(is_close_event_call = false) {
-        var self = this, msg;
-
-        /*
-         is_close_event_call = true only when the function is called when the
-         close panel event is triggered, otherwise (on recursive calls) it is false
-        */
-        if(!self.ignore_on_close || is_close_event_call)
-          self.ignore_on_close = {
-            unsaved_data: false,
-            unsaved_query: false,
-          };
-
-        var ignore_unsaved_data = self.ignore_on_close.unsaved_data,
-          ignore_unsaved_query = self.ignore_on_close.unsaved_query;
-
-        // If there is unsaved data changes in the grid
-        if (!ignore_unsaved_data && self.can_edit
-            && self.preferences.prompt_save_data_changes &&
-            self.data_store &&
-             (_.size(self.data_store.added) ||
-              _.size(self.data_store.updated) ||
-              _.size(self.data_store.deleted))) {
-          msg = gettext('The data has changed. Do you want to save changes?');
-          self.unsaved_changes_user_confirmation(msg, true);
-        } // If there is unsaved query changes in the query editor
-        else if (!ignore_unsaved_query && self.is_query_tool
-                   && self.is_query_changed
-                   && self.preferences.prompt_save_query_changes) {
-          msg = gettext('The query text has changed. Do you want to save changes?');
-          self.unsaved_changes_user_confirmation(msg, false);
-        } // If a transaction is currently ongoing
-        else if (self.preferences.prompt_commit_transaction
-                 && (self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INTRANS
-                    || self.last_transaction_status === queryTxnStatus.TRANSACTION_STATUS_INERROR)) {
-          var is_commit_disabled = self.last_transaction_status == queryTxnStatus.TRANSACTION_STATUS_INERROR;
-          self.uncommitted_transaction_user_confirmation(is_commit_disabled);
-        }
-        else {
-          // No other function should call close() except through this function
-          // in order to perform necessary checks
-          self.ignore_on_close = undefined;
-          self.close();
-        }
-        // Return false so that the panel does not close unless close()
-        // is called explicitly (when all needed prompts are issued).
-        return false;
-      },
-
-      /* To prompt the user for uncommitted transaction */
-      uncommitted_transaction_user_confirmation: function(is_commit_disabled = false) {
-        var self = this;
-
-        alertify.confirmCommit || alertify.dialog('confirmCommit', function() {
-          return {
-            main: function(title, message, is_commit_disabled_arg) {
-              this.is_commit_disabled = is_commit_disabled_arg;
-              this.setHeader(title);
-              this.setContent(message);
-            },
-            setup: function() {
-              return {
-                buttons: [{
-                  text: gettext('Cancel'),
-                  key: 27, // ESC
-                  invokeOnClose: true,
-                  className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button',
-                }, {
-                  text: gettext('Rollback'),
-                  className: 'btn btn-primary fa fa-lg pg-alertify-button',
-                }, {
-                  text: gettext('Commit'),
-                  className: 'btn btn-primary fa fa-lg pg-alertify-button',
-                }],
-                focus: {
-                  element: 0,
-                  select: false,
-                },
-                options: {
-                  maximizable: false,
-                  resizable: false,
-                },
-              };
-            },
-            prepare: function() {
-              // Disable commit button if needed
-              if(this.is_commit_disabled)
-                this.__internal.buttons[2].element.disabled = true;
-              else
-                this.__internal.buttons[2].element.disabled = false;
-            },
-            callback: function(closeEvent) {
-              switch (closeEvent.index) {
-              case 0: // Cancel
-                //Do nothing.
-                break;
-              case 1: // Rollback
-                self.close_on_idle_transaction = true;
-                queryToolActions.executeRollback(self);
-                break;
-              case 2: // Commit
-                self.close_on_idle_transaction = true;
-                queryToolActions.executeCommit(self);
-                break;
-              }
-            },
-          };
-        });
-
-        let msg = gettext('The current transaction is not commited to the database. '
-                           + 'Do you want to commit or rollback the transaction?');
-
-        alertify.confirmCommit(gettext('Commit transaction?'), msg, is_commit_disabled);
-      },
-
-      /* To prompt user for unsaved changes */
-      unsaved_changes_user_confirmation: function(msg, is_unsaved_data) {
-        // If there is anything to save then prompt user
-        var self = this;
-
-        alertify.confirmSave || alertify.dialog('confirmSave', function() {
-          return {
-            main: function(title, message, is_unsaved_data_arg) {
-              this.is_unsaved_data = is_unsaved_data_arg;
-              this.setHeader(title);
-              this.setContent(message);
-            },
-            setup: function() {
-              return {
-                buttons: [{
-                  text: gettext('Cancel'),
-                  key: 27, // ESC
-                  invokeOnClose: true,
-                  className: 'btn btn-secondary fa fa-lg fa-times pg-alertify-button',
-                }, {
-                  text: gettext('Don\'t save'),
-                  className: 'btn btn-secondary fa fa-lg fa-trash-alt pg-alertify-button',
-                }, {
-                  text: gettext('Save'),
-                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
-                }],
-                focus: {
-                  element: 0,
-                  select: false,
-                },
-                options: {
-                  maximizable: false,
-                  resizable: false,
-                },
-              };
-            },
-            callback: function(closeEvent) {
-              switch (closeEvent.index) {
-              case 0: // Cancel
-                //Do nothing.
-                break;
-              case 1: // Don't Save
-                self.close_on_save = false;
-                self.is_unsaved_data = this.is_unsaved_data;
-                $.ajax({
-                  url: url_for('sqleditor._check_server_connection_status', {
-                    'sid': self.url_params.sid,
-                    'sgid': self.url_params.sgid,
-                  }),
-                  headers: {
-                    'Cache-Control' : 'no-cache',
-                  },
-                }).done(function (res) {
-                  let response = res.data.result.server;
-                  if (response) {
-                    closeEvent.cancel = true;
-                    if (self.is_unsaved_data)
-                      self.ignore_on_close.unsaved_data = true;
-                    else
-                      self.ignore_on_close.unsaved_query = true;
-
-                    // Go back to check for any other needed confirmations before closing
-                    if (!self.check_needed_confirmations_before_closing_panel()) {
-                      closeEvent.cancel = true;
-                    }
-                  } else {
-                    Notify.confirm(
-                      gettext('Warning'),
-                      gettext('The current transaction has been rolled back because the server was disconnected.'),
-                      function() {
-                        // Close the query tool if server is disconnected.
-                        setTimeout(() => { self.close(); }, 200);
-                      },
-                      function() {
-                        return true;
-                      },
-                      gettext('OK'),
-                      gettext('Cancel')
-                    );
-                  }
-                }).fail(function() {
-                  /* failure should be ignored */
-                });
-                break;
-              case 2: //Save
-                self.close_on_save = true;
-                if(this.is_unsaved_data) {
-                  self.save_data();
-                }
-                else {
-                  self._save_file();
-                }
-                break;
-              }
-            },
-          };
-        });
-        alertify.confirmSave(gettext('Save changes?'), msg, is_unsaved_data);
-      },
-
-      close: function() {
-        var self = this;
-
-        pgBrowser.Events.off('pgadmin:user:logged-in', this.initTransaction);
-        window.onbeforeunload = null;
-        self.gridView.currentPanel.off(wcDocker.EVENT.CLOSING);
-        // remove col_size object on panel close
-        if (!_.isUndefined(self.col_size)) {
-          delete self.col_size;
-        }
-        pgWindow.default.pgAdmin.Browser.docker.removePanel(self.gridView.currentPanel);
-      },
-      /* This function is used to raise notify messages and update
-       * the notification grid.
-       */
-      update_notifications: function (notifications) {
-        queryToolNotifications.updateNotifications(notifications);
-      },
-
-      /* This function is used to set editor title based on title
-       * & db_name passed as parameters
-       */
-      set_title_and_render_connection: function(title, db_name) {
-        var self = this;
-        self.gridView.set_editor_title(_.unescape(title));
-        self.gridView.handler.setTitle(_.unescape(title));
-
-        self.gridView.connection_list.forEach(option =>{
-          if(db_name == option['database_name']) {
-            option.database_name = db_name;
-            option.title = title;
-            if('is_selected' in option && option['is_selected']) {
-              self.gridView.$el.find('ul#connections-list li.selected-connection > a').text(title);
-            }
-            return true;
-          }
-        });
-      },
-
-      /* This function is used to check the synchronous db name change by users.
-       * if changed, alert will be generated with message prompting user to
-       * click OK for automatic db node refresh.
-       */
-      check_db_name_change: function(data) {
-
-        var self = this;
-
-        var selected_item = pgWindow.default.pgAdmin.Browser.tree.selected(),
-          tree_data = pgWindow.default.pgAdmin.Browser.tree.translateTreeNodeIdFromReactTree(selected_item),
-          server_data = pgWindow.default.pgAdmin.Browser.tree.findNode(tree_data[1]),
-          database_data = pgWindow.default.pgAdmin.Browser.tree.findNode(tree_data[3]),
-          db_name = database_data.data.label,
-          db_did = database_data.data._id;
-
-        if(data.data_obj.db_id == db_did && !_.isEqual(db_name, data.data_obj.db_name)) {
-
-          var message = `Current database has been moved or renamed to ${data.data_obj.db_name}. Click on the OK button to refresh the database name.`,
-            title = '';
-
-          if(self.is_query_tool) {// for query tool
-
-            var qt_title_placeholder = self.gridView.browser_preferences['qt_tab_title_placeholder'];
-            var title_data = {
-              'database': data.data_obj.db_name,
-              'username': server_data.data.user.name,
-              'server': server_data.data.label,
-              'type': 'query_tool'
-            };
-            title = panelTitleFunc.generateTitle(qt_title_placeholder, title_data);
-          }
-          else { // for datagrid
-            title = generateDatagridTitle(pgWindow.default.pgAdmin.Browser, selected_item, null, data.data_obj);
-          }
-
-          panelTitleFunc.refresh_db_node(message, database_data.domNode);
-          self.set_title_and_render_connection(title, database_data.data.label);
-        }
-      },
-    });
-
-  pgAdmin.SqlEditor = {
-    // This function is used to create and return the object of grid controller.
-    create: function(container) {
-      return new SqlEditorController(container);
-    },
-    jquery: $,
-  };
-
-  return pgAdmin.SqlEditor;
-});
diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor_title.js
similarity index 58%
rename from web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js
rename to web/pgadmin/tools/sqleditor/static/js/sqleditor_title.js
index 7f5d6fbe2..93bd0d015 100644
--- a/web/pgadmin/tools/datagrid/static/js/datagrid_panel_title.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor_title.js
@@ -8,8 +8,9 @@
 //////////////////////////////////////////////////////////////
 
 import gettext from 'sources/gettext';
+import Alertify from 'pgadmin.alertifyjs';
 import pgWindow from 'sources/window';
-import Notify from '../../../../static/js/helpers/Notifier';
+import { retrieveNameSpaceName, retrieveNodeName } from './show_view_data';
 
 const pgAdmin = pgWindow.pgAdmin;
 
@@ -22,6 +23,25 @@ function isServerInformationAvailable(parentData) {
   return parentData.server === undefined;
 }
 
+export function getTitle(pgAdmin, browserPref, parentData=null, isConnTitle=false, server=null, database=null, username=null, isQueryTool=true) {
+  let titleTemplate = isQueryTool ? pgAdmin['qt_default_placeholder'] : pgAdmin['vw_edt_default_placeholder'];
+  if (!isConnTitle) {
+    if(!isQueryTool) {
+      titleTemplate = browserPref['vw_edt_tab_title_placeholder'] ?? pgAdmin['qt_default_placeholder'];
+    } else {
+      titleTemplate = browserPref['qt_tab_title_placeholder'] ?? pgAdmin['vw_edt_default_placeholder'];
+    }
+  }
+  return generateTitle(titleTemplate, {
+    'database': database,
+    'username': username,
+    'server': server,
+    'schema': retrieveNameSpaceName(parentData),
+    'table': retrieveNodeName(parentData),
+    'type': isQueryTool ? 'query_tool' : 'view_data',
+  });
+}
+
 export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null, parentData=null, conn_title=false, db_label=null) {
   var preferences = pgBrowser.get_preferences_for_module('browser');
   if(selected_item == null && parentData == null) {
@@ -57,6 +77,7 @@ export function getPanelTitle(pgBrowser, selected_item=null, custom_title=null,
     'server': parentData.server.label,
     'type': 'query_tool',
   };
+
   return generateTitle(qt_title_placeholder, title_data);
 }
 
@@ -69,7 +90,7 @@ export function setQueryToolDockerTitle(panel, is_query_tool, panel_title, is_fi
     panel_tooltip = gettext('File - ') + panel_title;
     panel_icon = 'fa fa-file-alt';
   }
-  else if (is_query_tool == 'false' || is_query_tool == false) {
+  else if (is_query_tool == 'false' || !is_query_tool) {
     // Edit grid titles
     panel_tooltip = gettext('View/Edit Data - ') + panel_title;
     panel_icon = 'pg-font-icon icon-view_data';
@@ -86,33 +107,29 @@ export function setQueryToolDockerTitle(panel, is_query_tool, panel_title, is_fi
 
 export function set_renamable_option(panel, is_file) {
   if(is_file || is_file == 'true') {
-    panel.renamable(false);
+    panel?.renamable(false);
   } else {
-    panel.renamable(true);
+    panel?.renamable(true);
   }
 }
 
 export function generateTitle(title_placeholder, title_data) {
 
-  if(title_data.type == 'query_tool') {
-    title_placeholder = title_placeholder.replace(new RegExp('%DATABASE%'), _.unescape(title_data.database));
-    title_placeholder = title_placeholder.replace(new RegExp('%USERNAME%'), _.unescape(title_data.username));
-    title_placeholder = title_placeholder.replace(new RegExp('%SERVER%'), _.unescape(title_data.server));
-  } else if(title_data.type == 'datagrid') {
-    title_placeholder = title_placeholder.replace(new RegExp('%DATABASE%'), _.unescape(title_data.database));
-    title_placeholder = title_placeholder.replace(new RegExp('%USERNAME%'), _.unescape(title_data.username));
-    title_placeholder = title_placeholder.replace(new RegExp('%SERVER%'), _.unescape(title_data.server));
-    title_placeholder = title_placeholder.replace(new RegExp('%SCHEMA%'), _.unescape(title_data.schema));
-    title_placeholder = title_placeholder.replace(new RegExp('%TABLE%'), _.unescape(title_data.table));
+  if(title_data.type == 'query_tool' || title_data.type == 'psql_tool') {
+    title_placeholder = title_placeholder.replace('%DATABASE%', _.unescape(title_data.database));
+    title_placeholder = title_placeholder.replace('%USERNAME%', _.unescape(title_data.username));
+    title_placeholder = title_placeholder.replace('%SERVER%', _.unescape(title_data.server));
+  } else if(title_data.type == 'view_data') {
+    title_placeholder = title_placeholder.replace('%DATABASE%', _.unescape(title_data.database));
+    title_placeholder = title_placeholder.replace('%USERNAME%', _.unescape(title_data.username));
+    title_placeholder = title_placeholder.replace('%SERVER%', _.unescape(title_data.server));
+    title_placeholder = title_placeholder.replace('%SCHEMA%', _.unescape(title_data.schema));
+    title_placeholder = title_placeholder.replace('%TABLE%', _.unescape(title_data.table));
   } else if(title_data.type == 'debugger') {
-    title_placeholder = title_placeholder.replace(new RegExp('%FUNCTION%'), _.unescape(title_data.function_name));
-    title_placeholder = title_placeholder.replace(new RegExp('%ARGS%'), _.unescape(title_data.args));
-    title_placeholder = title_placeholder.replace(new RegExp('%SCHEMA%'), _.unescape(title_data.schema));
-    title_placeholder = title_placeholder.replace(new RegExp('%DATABASE%'), _.unescape(title_data.database));
-  } else if(title_data.type == 'psql_tool') {
-    title_placeholder = title_placeholder.replace(new RegExp('%DATABASE%'), _.unescape(title_data.database));
-    title_placeholder = title_placeholder.replace(new RegExp('%USERNAME%'), _.unescape(title_data.username));
-    title_placeholder = title_placeholder.replace(new RegExp('%SERVER%'), _.unescape(title_data.server));
+    title_placeholder = title_placeholder.replace('%FUNCTION%', _.unescape(title_data.function_name));
+    title_placeholder = title_placeholder.replace('%ARGS%', _.unescape(title_data.args));
+    title_placeholder = title_placeholder.replace('%SCHEMA%', _.unescape(title_data.schema));
+    title_placeholder = title_placeholder.replace('%DATABASE%', _.unescape(title_data.database));
   }
 
   return _.escape(title_placeholder);
@@ -122,7 +139,7 @@ export function generateTitle(title_placeholder, title_data) {
  * This function is used refresh the db node after showing alert to the user
  */
 export function refresh_db_node(message, dbNode) {
-  Notify.alert()
+  Alertify.alert()
     .setting({
       'title': gettext('Database moved/renamed'),
       'label':gettext('OK'),
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_history.scss b/web/pgadmin/tools/sqleditor/static/scss/_history.scss
deleted file mode 100644
index fbb817824..000000000
--- a/web/pgadmin/tools/sqleditor/static/scss/_history.scss
+++ /dev/null
@@ -1,254 +0,0 @@
-.query-history {
-  overflow: auto;
-
-  .list-item {
-    border-bottom: $panel-border;
-    background-color: $color-bg;
-  }
-
-  .entry {
-    font-family: $font-family-editor;
-    border: $panel-border-width solid transparent;
-    margin-left: 1px;
-    padding: 0 5px;
-
-    .other-info {
-      @extend .text-12;
-      color: $text-muted;
-      font-family: $font-family-editor;
-      display: flex;
-      flex-direction: row;
-      justify-content: space-between;
-
-      .timestamp {
-        align-self: flex-start;
-      }
-    }
-
-    .query {
-      text-overflow: ellipsis;
-      overflow: hidden;
-      white-space: nowrap;
-      user-select: initial;
-
-      .query-history-icon {
-        width: 18px;
-        text-align: center;
-      }
-    }
-  }
-
-  .date-label {
-    font-family: $font-family-editor;
-    background: $color-gray-lighter;
-    padding: 2px 9px;
-    font-size: 11px;
-    font-weight: bold;
-    color:  $color-gray;
-    border-bottom: $panel-border;
-  }
-
-  .entry.error {
-    background: $sql-history-error-bg;
-  }
-
-  .selected {
-    & .entry.error {
-      background-color: $sql-history-error-bg;
-    }
-
-    & .entry {
-      color: $sql-history-success-fg;
-      border: $table-hover-border;
-      background: $sql-history-success-bg;
-      font-weight: bold;
-
-      .other-info {
-        color: $sql-history-success-fg;
-        font-weight: bold;
-      }
-    }
-  }
-}
-
-#history-detail-query .CodeMirror {
-  border: $panel-border;
-  background-color: $sql-history-detail-bg;
-  width: 100%;
-  padding-left: 5px;
-  position: absolute;
-}
-
-.header-label {
-  @extend .text-12;
-  display: block;
-  color: $color-fg;
-}
-
-
-.sql-editor-history-container {
-  height: 100%;
-  overflow: hidden;
-  background-color: $negative-bg;
-}
-
-
-.query-detail {
-  height: 100%;
-  width: 100%;
-  min-height: 19em;
-  overflow: auto;
-  display: flex;
-  flex-direction: column;
-  background-color: $color-bg;
-  .error-message-block {
-    background: $sql-history-error-bg;
-    flex: 0.3;
-    padding-left: 20px;
-
-    .history-error-text {
-      @extend .text-12;
-      padding: 7px 0;
-
-      span {
-        color:$sql-history-error-fg;
-        font-weight: 500;
-        margin-right: 8px;
-      }
-    }
-  }
-
-  .info-message-block {
-    background: $sql-history-detail-bg;
-    flex: 0.3;
-    padding-left: 20px;
-
-    .history-info-text {
-      @extend .text-12;
-      padding: 7px 0;
-    }
-  }
-
-  .metadata-block {
-    flex: 0.4;
-    padding: 10px 20px;
-
-    .metadata {
-      display: flex;
-      flex-wrap: wrap;
-
-      .item {
-        flex: 1;
-        min-width: 130px;
-
-        .value {
-          @extend .text-14;
-          display: block;
-        }
-
-        .description {
-          @extend .header-label;
-        }
-      }
-
-    }
-  }
-
-  .query-statement-block {
-    flex: 5;
-    margin-left: 10px;
-    margin-right: 10px;
-    min-height: 4em;
-    position: relative;
-
-    .copy-all, .was-copied, .copy-to-editor {
-      float: left;
-      position: relative;
-      z-index: 10;
-      border: 1px solid $border-color;
-      color: $color-fg;
-      font-size: 12px;
-      box-shadow: 1px 2px 4px 0px $color-gray-light;
-      padding: 3px 12px 3px 10px;
-      font-weight: 500;
-      min-width: 75px;
-    }
-
-    .copy-all, .copy-to-editor {
-      background-color: $color-bg;
-    }
-
-    .was-copied {
-      background-color: $color-primary-light;
-      border-color: $color-primary-light;
-      color: $btn-copied-color-fg;
-    }
-
-    .CodeMirror-scroll {
-      padding-top: 25px;
-    }
-  }
-
-  .block-divider {
-    margin-top: 11px;
-    margin-bottom: 8px;
-  }
-
-  .message-block {
-    flex: 2;
-    display: flex;
-    padding: 0 20px;
-    min-height: 6em;
-
-    .message {
-      flex: 2 2 0%;
-      flex-direction: column;
-      display: flex;
-
-      .message-header {
-        @extend .header-label;
-        @extend .not-selectable;
-        flex: 0 0 auto;
-      }
-
-      .content {
-        flex: 0 1 auto;
-        overflow: auto;
-        position: relative;
-        height: 100%;
-
-        .content-value {
-          @extend .bg-white;
-          @extend .text-13;
-          font-family: $font-family-editor;
-          color: $color-fg;
-          border: 0;
-          padding-left: 0;
-          position: absolute;
-        }
-      }
-    }
-  }
-}
-
-#history_grid {
-  .gutter.gutter-horizontal {
-    width: $panel-border-width;
-    background: $panel-border-color;
-
-    &:hover {
-      cursor: ew-resize;
-    }
-  }
-
-  .toggle-and-history-container {
-    display: flex;
-    flex-direction: column;
-    height: 100%;
-
-    .query-history-toggle {
-      padding-top: 4px;
-      padding-bottom: 4px;
-    }
-  }
-}
diff --git a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss b/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
deleted file mode 100644
index ee5aabf8d..000000000
--- a/web/pgadmin/tools/sqleditor/static/scss/_sqleditor.scss
+++ /dev/null
@@ -1,431 +0,0 @@
-.filter-title {
-  background-color: $color-primary;
-  padding: 2px;
-  color: $color-primary-fg;
-  font-size: 13px;
-}
-
-.sql-icon-lg {
-  font-size: 0.875rem;
-  line-height: 1.3;
-}
-
-.sql_textarea {
-  height: 100%;
-}
-
-.sql_textarea .CodeMirror-scroll {
-  z-index: 0;
-}
-
-.data-output-container {
-  position: absolute;
-  top: 0;
-  bottom: 0;
-  right: 0;
-  left: 0;
-  overflow: hidden;
-}
-
-.sql-editor-busy-text-status {
-  position: absolute;
-  padding: 1rem 1.5rem;
-  bottom: 0;
-  right: 0;
-  opacity: 0.8;
-  background-color: $color-warning-light;
-  color: $color-warning-fg;
-}
-
-.connection_status {
-  background-color: $sql-title-bg;
-  color: $sql-title-fg;
-  border-right: $border-width solid $border-color;
-}
-
-.editor-title {
-  padding: $sql-title-padding;
-  background: $sql-title-bg;
-  color: $sql-title-fg;
-}
-
-.connection-info {
-  background: $sql-title-bg;
-  color: $sql-title-fg;
-  width:100%;
-  display: inherit;
-}
-
-.conn-info-dd {
-  padding-top: 0.3em;
-  padding-left: 0.2em;
-  cursor: pointer;
-}
-
-.connection-data {
-  display: inherit;
-  cursor: pointer;
-  width: auto;
-}
-
-
-#editor-panel {
-  z-index: 0;
-  position: absolute;
-  top: $sql-editor-panel-top;
-  bottom: 0;
-  left: 0;
-  right: 0;
-}
-
-
-.ajs-body .warn-icon {
-  color: $color-warning;
-  font-size: 2em;
-  margin-right: 20px;
-  padding-top: 10px;
-}
-
-.connection_status_wrapper {
-  width: 100%;
-  border-top: $panel-border;
-  border-bottom: $panel-border;
-}
-
-li.CodeMirror-hint-active {
-  background: $color-primary-light;
-  color: $color-primary-fg;
-}
-
-.sql-editor .CodeMirror-activeline-background {
-  background: $color-editor-activeline-light !important;
-  border: 1px solid $color-editor-activeline-border-color;
-}
-
-.filter-container {
-  position: relative;
-  background-color: $color-bg;
-  border: 1px solid $border-color;
-  padding-bottom: 30px;
-  top: 10px;
-  z-index: 1;
-  margin: auto;
-  width: 60%;
-}
-
-.limit-enabled {
-  background-color: $color-bg;
-}
-
-
-
-.CodeMirror-hint {
-  margin: 0;
-  padding: 0 4px;
-  border-radius: 2px;
-  overflow: hidden;
-  white-space: pre;
-  color: $color-fg;
-  cursor: pointer;
-}
-
-.grid-header .ui-icon.ui-state-hover {
-  background-color: $color-bg;
-}
-
-.slick-cell.selected span[data-cell-type="row-header-selector"] {
-  color: $color-primary-fg;
-}
-
-.slick-cell.cell-move-handle {
-  font-weight: bold;
-  text-align: right;
-  border-right: solid  $border-color;
-  background: $color-gray-lighter;
-  cursor: move;
-}
-
-.cell-move-handle:hover {
-  background: $color-gray-light;
-}
-
-.slick-row.selected .cell-move-handle {
-  background: $color-warning-light;
-}
-
-.slick-row.complete {
-  background-color: $color-success-light;
-  color:  $color-gray-dark;
-}
-
-.cell-selection {
-  border-right-color: $border-color;
-  border-right-style: solid;
-  background: $color-gray-lighter;
-  color:  $color-gray;
-  text-align: right;
-  font-size: 10px;
-}
-
-#datagrid .slick-header .slick-header-columns {
-  background: $sql-grid-title-cell-bg;
-  color: $sql-grid-title-cell-fg;
-  height: 40px;
-  border-bottom: $panel-border;
-}
-
-#datagrid .slick-header .slick-header-column.ui-state-default {
-  padding: 4px 0 3px 6px;
-  border-bottom: $panel-border;
-  border-right: $panel-border;
-}
-
-.slick-row:hover .slick-cell{
-  border-top: $table-hover-border;
-  border-bottom: $table-hover-border;
-  background-color: $table-hover-bg-color;
-}
-
-#datagrid .slick-header .slick-header-column.selected {
-  background-color: $color-primary;
-}
-.slick-row .slick-cell {
-  border-bottom: $panel-border;
-  border-right: $panel-border;
-  z-index: 0;
-}
-
-#datagrid {
-  background: none;
-  background-color: $datagrid-bg;
-}
-
-.ui-widget-content.slick-row {
-  &.even,  &.odd {
-    background: none;
-    background-color: $table-bg;
-  }
-}
-
-/* Remove active cell border */
-.slick-cell.active {
-  border: 1px solid transparent;
-  border-right: 1px solid $color-gray-light;
-  border-bottom-color: $color-gray-light;
-}
-
-/* To highlight all newly inserted rows */
-.grid-canvas .new_row {
-  background: $color-success-light !important;
-}
-
-/* To highlight all the updated rows */
-.grid-canvas .updated_row {
-  background: $color-gray-lighter;
-}
-
-/* To highlight row at fault */
-.grid-canvas .new_row.error, .grid-canvas .updated_row.error {
-  background: $color-danger-light !important;
-}
-
-/* Disabled row */
-.grid-canvas .disabled_row {
-  background: $color-gray-lighter;
-}
-
-/* Disabled cell */
-.grid-canvas .disabled_cell {
-  color: $text-muted;
-}
-
-/* Highlighted (modified or new) cell */
-.grid-canvas .highlighted_grid_cells {
-  background: $color-gray-lighter;
-  font-weight: bold;
-}
-
-
-/* Override selected row color */
-#datagrid .slick-row .slick-cell.selected {
-    background-color: $table-bg-selected;
-    color: $datagrid-selected-color;
-}
-
-/* color the first column */
-
-#datagrid .slick-row {
-    .slick-cell {
-        background-color: $sql-grid-data-cell-bg;
-        color: $sql-grid-data-cell-fg;
-    }
-
-    .slick-cell.l0.r0 {
-        background-color: $sql-grid-title-cell-bg;
-        color: $sql-grid-title-cell-fg;
-    }
-}
-
-#datagrid div.slick-header.ui-state-default {
-  background-color: $sql-grid-title-cell-bg;
-  color: $sql-grid-title-cell-fg;
-  border-bottom: none;
-  border-right: none;
-  border-top: none;
-}
-
-#datagrid .slick-row .slick-cell.l0.r0.selected {
-  background-color: $color-primary;
-  color: $color-primary-fg;
-}
-
-#datagrid .slick-row > .slick-cell:not(.l0):not(.r0).selected {
-  background-color: $table-hover-bg-color;
-  border-top: $table-hover-border;
-  border-bottom: $table-hover-border;
-}
-
-.pg-text-editor {
-  z-index:10000;
-  position:absolute;
-  background: $color-bg;
-  padding: 0.25rem;
-  border: $panel-border;
-  box-shadow: $dropdown-box-shadow;
-
-
-  & .pg-textarea {
-    width:250px;
-    height:80px;
-    border:0;
-    outline:0;
-    resize: both;
-  }
-
-  & .pg-text-invalid {
-    background: $color-danger-lighter;
-  }
-
-  & #pg-json-editor {
-    min-width:525px;
-    min-height:300px;
-    height:295px;
-    width:550px;
-    border: $panel-border;
-    outline:0;
-    resize: both;
-    overflow:auto
-  }
-}
-
-.pg_buttons {
-  padding-top: 3px;
-}
-.sql-editor-message {
-  white-space:pre-wrap;
-  font-family: $font-family-editor;
-  padding-top: 5px;
-  padding-left: 10px;
-  overflow: auto;
-  height: 100%;
-  font-size: 0.925em;
-  -webkit-user-select: text;
-  -moz-user-select: text;
-  -ms-user-select: text;
-  user-select: text;
-}
-
-.view-geometry-property-table .td-disabled{
-  color: $color-gray-lighter;
-}
-
-/* For leaflet map background */
-.geometry-viewer-container-plain-background {
-  background: $color-bg;
-}
-
-
-div.strikeout:before {
-  content: " ";
-  position: absolute;
-  top: 50%;
-  left: 0;
-  border-top: 1px solid $color-danger;
-  width: 100%;
-  font-weight: 900;
-}
-
-div.strikeout:after {
-  content: "\00B7";
-  font-size: 1px;
-  font-weight: 900;
-}
-
-.sql-scratch {
-  width: 100%;
-  height: 100%;
-  box-sizing: border-box;
-  overflow-y: hidden;
-
-  textarea {
-    width: 100%;
-    height: 100%;
-    box-sizing: border-box;
-    border: none;
-    resize: none;
-  }
-}
-
-.connection_status .obtaining-conn {
-  background-image: $loader-icon-small !important;
-  background-position: center center;
-  background-repeat: no-repeat;
-  &:before {
-    content:'';
-  }
-  min-width: 50%;
-  min-height: 100%;
-}
-
-.sql-editor-grid-container {
-  height: 100%;
-  overflow: auto;
-
-  .ui-widget-content {
-    background-color: $input-bg;
-    color: $input-color;
-  }
-
-  .ui-state-default {
-    color: $color-fg;
-  }
-}
-
-.sql-editor-grid-container.has-no-footer {
-  height: 100%;
-}
-
-.selected-connection {
-  background-color: $color-primary-light;
-}
-
-/* Setting it to hardcoded white as the SVG generated is having white bg
- * Need to check what can be done.
- */
-
-/* Css for psql */
-.psql_terminal .terminal {
-  padding-top: 1%;
-  padding-left: 0.5%;
-  height: 100%;
-}
-
-.psql-icon-style {
-  font-size: inherit;
-  padding-left: 0em;
-}
-
-.psql-tab-style {
-  font-size: small;
-  padding-left: 0em;
-}
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/filter.html b/web/pgadmin/tools/sqleditor/templates/sqleditor/filter.html
similarity index 100%
rename from web/pgadmin/tools/datagrid/templates/datagrid/filter.html
rename to web/pgadmin/tools/sqleditor/templates/sqleditor/filter.html
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/index.html b/web/pgadmin/tools/sqleditor/templates/sqleditor/index.html
new file mode 100644
index 000000000..bcb6a04ea
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/index.html
@@ -0,0 +1,70 @@
+{% extends "base.html" %}
+{% block title %}{{title}}{% endblock %}
+
+{% block css_link %}
+<link type="text/css" rel="stylesheet" href="{{ url_for('browser.browser_css')}}"/>
+{% endblock %}
+{% block body %}
+<style>
+    body {padding: 0px;}
+    #sqleditor-container {
+        display: flex;
+        flex-direction: column;
+        height: 100%;
+    }
+    #sqleditor-container:not(:empty) + .pg-sp-container {
+        display: none;
+    }
+    {% if is_desktop_mode and is_linux %}
+    .alertify .ajs-dimmer,.alertify .ajs-modal{-webkit-transform: none;}
+    .alertify-notifier{-webkit-transform: none;}
+    .alertify-notifier .ajs-message{-webkit-transform: none;}
+    .alertify .ajs-dialog.ajs-shake{-webkit-animation-name: none;}
+    .sql-editor-busy-icon.fa-pulse{-webkit-animation: none;}
+    {% endif %}
+</style>
+<div id="sqleditor-container">
+<div class="pg-sp-container">
+    <div class="pg-sp-content">
+        <div class="row">
+            <div class="col-12 pg-sp-icon"></div>
+        </div>
+    </div>
+</div>
+</div>
+{% endblock %}
+{% block init_script %}
+    try {
+        require(
+            ['sources/generated/browser_nodes', 'sources/generated/codemirror'],
+            function() {
+                require(['sources/generated/sqleditor'], function(module) {
+                window.pgAdmin.Tools.SQLEditor.loadComponent(
+                    document.getElementById('sqleditor-container'), {{ params|safe }});
+
+                if(window.opener) {
+                    $(window).on('unload', function(ev) {
+                        $.ajax({
+                            method: 'DELETE',
+                            url: '{{close_url}}'
+                        });
+                    });
+                } else {
+                    $(window).on('beforeunload', function(ev) {
+                        $.ajax({
+                            method: 'DELETE',
+                            url: '{{close_url}}'
+                        });
+                    });
+                }
+            }, function() {
+                console.log(arguments);
+            });
+        },
+        function() {
+            console.log(arguments);
+        });
+    } catch (err) {
+        console.log(err);
+    }
+{% endblock %}
diff --git a/web/pgadmin/tools/sqleditor/tests/test_download_csv_query_tool.py b/web/pgadmin/tools/sqleditor/tests/test_download_csv_query_tool.py
index f609193ce..bae8fb7e2 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_download_csv_query_tool.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_download_csv_query_tool.py
@@ -27,7 +27,7 @@ class TestDownloadCSV(BaseTestGenerator):
             'Download csv URL with valid query',
             dict(
                 sql='SELECT 1 as "A",2 as "B",3 as "C"',
-                init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
+                init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
                 donwload_url="/sqleditor/query_tool/download/{0}",
                 output_columns='"A","B","C"',
                 output_values='1,2,3',
@@ -41,7 +41,7 @@ class TestDownloadCSV(BaseTestGenerator):
             'Download csv URL with wrong TX id',
             dict(
                 sql='SELECT 1 as "A",2 as "B",3 as "C"',
-                init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
+                init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
                 donwload_url="/sqleditor/query_tool/download/{0}",
                 output_columns=None,
                 output_values=None,
@@ -55,7 +55,7 @@ class TestDownloadCSV(BaseTestGenerator):
             'Download csv URL with wrong query',
             dict(
                 sql='SELECT * FROM this_table_does_not_exist',
-                init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
+                init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
                 donwload_url="/sqleditor/query_tool/download/{0}",
                 output_columns=None,
                 output_values=None,
@@ -69,7 +69,7 @@ class TestDownloadCSV(BaseTestGenerator):
             'Download as txt without filename parameter',
             dict(
                 sql='SELECT 1 as "A",2 as "B",3 as "C"',
-                init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
+                init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
                 donwload_url="/sqleditor/query_tool/download/{0}",
                 output_columns='"A";"B";"C"',
                 output_values='1;2;3',
@@ -83,7 +83,7 @@ class TestDownloadCSV(BaseTestGenerator):
             'Download as csv without filename parameter',
             dict(
                 sql='SELECT 1 as "A",2 as "B",3 as "C"',
-                init_url='/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}',
+                init_url='/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}',
                 donwload_url="/sqleditor/query_tool/download/{0}",
                 output_columns='"A","B","C"',
                 output_values='1,2,3',
@@ -211,7 +211,7 @@ class TestDownloadCSV(BaseTestGenerator):
                 self.assertEqual(response.status_code, 500)
 
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_editor_history.py b/web/pgadmin/tools/sqleditor/tests/test_editor_history.py
index 86b5d935b..c7dcbdad0 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_editor_history.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_editor_history.py
@@ -70,7 +70,7 @@ class TestEditorHistory(BaseTestGenerator):
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -101,7 +101,7 @@ class TestEditorHistory(BaseTestGenerator):
 
     def tearDown(self):
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_encoding_charset.py b/web/pgadmin/tools/sqleditor/tests/test_encoding_charset.py
index 99ec32a49..653ddac10 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_encoding_charset.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_encoding_charset.py
@@ -262,7 +262,7 @@ class TestEncodingCharset(BaseTestGenerator):
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'\
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'\
             .format(self.trans_id, test_utils.SERVER_GROUP, self.encode_sid,
                     self.encode_did)
         response = self.tester.post(url)
@@ -283,7 +283,7 @@ class TestEncodingCharset(BaseTestGenerator):
         self.assertEqual(result, self.test_str)
 
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_explain_plan.py b/web/pgadmin/tools/sqleditor/tests/test_explain_plan.py
index 734125399..e2bfc259a 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_explain_plan.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_explain_plan.py
@@ -34,7 +34,7 @@ class TestExplainPlan(BaseTestGenerator):
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -68,7 +68,7 @@ class TestExplainPlan(BaseTestGenerator):
         self.assertEqual(len(response_data['data']['result'][0]), 1)
 
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_macros.py b/web/pgadmin/tools/sqleditor/tests/test_macros.py
index b66678668..95e41ccb8 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_macros.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_macros.py
@@ -106,7 +106,7 @@ class TestMacros(BaseTestGenerator):
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -150,7 +150,7 @@ class TestMacros(BaseTestGenerator):
 
     def tearDown(self):
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_poll_query_tool.py b/web/pgadmin/tools/sqleditor/tests/test_poll_query_tool.py
index 920839e56..d35a2bb73 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_poll_query_tool.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_poll_query_tool.py
@@ -77,7 +77,7 @@ NOTICE:  Hello, world!
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -110,7 +110,7 @@ NOTICE:  Hello, world!
             cnt += 1
 
         # Close query tool
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/tests/test_transaction_status.py b/web/pgadmin/tools/sqleditor/tests/test_transaction_status.py
index 5fbe83734..353820bf0 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_transaction_status.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_transaction_status.py
@@ -304,7 +304,7 @@ class TestTransactionControl(BaseTestGenerator):
 
     def _initialize_query_tool(self):
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -339,6 +339,6 @@ class TestTransactionControl(BaseTestGenerator):
         utils.create_table_with_query(self.server, self.db_name, create_sql)
 
     def _close_query_tool(self):
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
diff --git a/web/pgadmin/tools/sqleditor/tests/test_view_data.py b/web/pgadmin/tools/sqleditor/tests/test_view_data.py
index b87b3604a..5ad75d28f 100644
--- a/web/pgadmin/tools/sqleditor/tests/test_view_data.py
+++ b/web/pgadmin/tools/sqleditor/tests/test_view_data.py
@@ -97,7 +97,7 @@ class TestViewData(BaseTestGenerator):
 
         # Initialize query tool
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/datagrid/{0}/3/table/{1}/{2}/{3}/{4}' \
+        url = '/sqleditor/initialize/viewdata/{0}/3/table/{1}/{2}/{3}/{4}' \
             .format(self.trans_id, test_utils.SERVER_GROUP, self.server_id,
                     self.db_id, table_id)
 
diff --git a/web/pgadmin/tools/sqleditor/utils/macros.py b/web/pgadmin/tools/sqleditor/utils/macros.py
index bdf3ec9bd..7e2f8731e 100644
--- a/web/pgadmin/tools/sqleditor/utils/macros.py
+++ b/web/pgadmin/tools/sqleditor/utils/macros.py
@@ -76,7 +76,8 @@ def get_user_macros():
     macros = db.session.query(UserMacros.name,
                               Macros.id,
                               Macros.alt, Macros.control,
-                              Macros.key, Macros.key_code
+                              Macros.key, Macros.key_code,
+                              UserMacros.sql
                               ).outerjoin(
         Macros, UserMacros.mid == Macros.id).filter(
         UserMacros.uid == current_user.id).order_by(UserMacros.name).all()
@@ -87,7 +88,8 @@ def get_user_macros():
         key_label = 'Ctrl + ' + m[4] if m[3] is True else 'Alt + ' + m[4]
         data.append({'name': m[0], 'id': m[1], 'key': m[4],
                      'key_label': key_label, 'alt': 1 if m[2] else 0,
-                     'control': 1 if m[3] else 0, 'key_code': m[5]})
+                     'control': 1 if m[3] else 0, 'key_code': m[5],
+                     'sql': m[6]})
 
     return data
 
@@ -121,7 +123,8 @@ def set_macros():
                 return make_json_response(
                     status=410, success=0, errormsg=msg
                 )
-    return ajax_response(status=200)
+
+    return get_macros(None, True)
 
 
 def create_macro(macro):
diff --git a/web/pgadmin/tools/sqleditor/utils/query_history.py b/web/pgadmin/tools/sqleditor/utils/query_history.py
index cf5e5f0b5..47e35e79a 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_history.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_history.py
@@ -1,6 +1,7 @@
 from pgadmin.utils.ajax import make_json_response
 from pgadmin.model import db, QueryHistoryModel
 from config import MAX_QUERY_HIST_STORED
+import json
 
 
 class QueryHistory:
@@ -107,30 +108,34 @@ class QueryHistory:
         )
 
     @staticmethod
-    def clear_history(uid, sid, dbname=None):
+    def clear_history(uid, sid, dbname=None, filter=None):
         try:
+            filters = [
+                QueryHistoryModel.uid == uid,
+                QueryHistoryModel.sid == sid
+            ]
             if dbname is not None:
-                db.session.query(QueryHistoryModel) \
-                    .filter(QueryHistoryModel.uid == uid,
-                            QueryHistoryModel.sid == sid,
-                            QueryHistoryModel.dbname == dbname) \
-                    .delete()
-
-                db.session.commit()
+                filters.append(QueryHistoryModel.dbname == dbname)
+
+            history = db.session.query(QueryHistoryModel) \
+                .filter(*filters)
+            if filter is not None:
+                for row in history:
+                    query_info = json.loads(row.query_info.decode())
+                    if query_info['query'] == filter['query'] and \
+                            query_info['start_time'] == filter['start_time']:
+                        db.session.delete(row)
             else:
-                db.session.query(QueryHistoryModel) \
-                    .filter(QueryHistoryModel.uid == uid,
-                            QueryHistoryModel.sid == sid)\
-                    .delete()
+                history.delete()
 
-                db.session.commit()
+            db.session.commit()
         except Exception:
             db.session.rollback()
             # do not affect query execution if history clear fails
 
     @staticmethod
-    def clear(uid, sid, dbname=None):
-        QueryHistory.clear_history(uid, sid, dbname)
+    def clear(uid, sid, dbname=None, filter=None):
+        QueryHistory.clear_history(uid, sid, dbname, filter)
         return make_json_response(
             data={
                 'status': True,
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index b18145e0f..ace48392f 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -18,22 +18,6 @@ from pgadmin.utils import SHORTCUT_FIELDS as shortcut_fields, \
 
 
 def register_query_tool_preferences(self):
-
-    self.info_notifier_timeout = self.preference.register(
-        'display', 'info_notifier_timeout',
-        gettext("Query info notifier timeout"), 'integer', 5,
-        category_label=PREF_LABEL_DISPLAY,
-        min_val=-1,
-        max_val=999999,
-        help_str=gettext(
-            'The length of time to display the query info notifier after '
-            'execution has completed. A value of -1 disables the notifier'
-            ' and a value of 0 displays it until clicked. Values greater'
-            ' than 0 display the notifier for the number of seconds'
-            ' specified.'
-        )
-    )
-
     self.explain_verbose = self.preference.register(
         'Explain', 'explain_verbose',
         gettext("Verbose output?"), 'boolean', False,
@@ -128,7 +112,7 @@ def register_query_tool_preferences(self):
         )
     )
 
-    self.show_prompt_commit_transaction = self.preference.register(
+    self.copy_sql_to_query_tool = self.preference.register(
         'Options', 'copy_sql_to_query_tool',
         gettext("Copy SQL from main window to query tool?"), 'boolean',
         False,
@@ -411,6 +395,24 @@ def register_query_tool_preferences(self):
         fields=shortcut_fields
     )
 
+    self.preference.register(
+        'keyboard_shortcuts',
+        'clear_query',
+        gettext('Clear query'),
+        'keyboardshortcut',
+        {
+            'alt': True,
+            'shift': False,
+            'control': True,
+            'key': {
+                'key_code': 76,
+                'char': 'L'
+            }
+        },
+        category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
+        fields=shortcut_fields
+    )
+
     self.preference.register(
         'keyboard_shortcuts',
         'download_results',
@@ -614,19 +616,6 @@ def register_query_tool_preferences(self):
         fields=accesskey_fields
     )
 
-    self.preference.register(
-        'keyboard_shortcuts', 'btn_clear_options',
-        gettext('Accesskey (Clear editor options)'), 'keyboardshortcut',
-        {
-            'key': {
-                'key_code': 76,
-                'char': 'l'
-            }
-        },
-        category_label=PREF_LABEL_KEYBOARD_SHORTCUTS,
-        fields=accesskey_fields
-    )
-
     self.preference.register(
         'keyboard_shortcuts', 'btn_conn_status',
         gettext('Accesskey (Connection status)'), 'keyboardshortcut',
diff --git a/web/pgadmin/tools/sqleditor/utils/start_running_query.py b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
index 876c1e8d5..b2a001236 100644
--- a/web/pgadmin/tools/sqleditor/utils/start_running_query.py
+++ b/web/pgadmin/tools/sqleditor/utils/start_running_query.py
@@ -107,8 +107,6 @@ class StartRunningQuery:
             data={
                 'status': status, 'result': result,
                 'can_edit': can_edit, 'can_filter': can_filter,
-                'info_notifier_timeout':
-                    self.blueprint_object.info_notifier_timeout.get() * 1000,
                 'notifies': notifies,
                 'transaction_status': trans_status,
             }
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py b/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py
index c6840df63..9839472ec 100644
--- a/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_is_query_resultset_updatable.py
@@ -203,7 +203,7 @@ class TestQueryUpdatableResultset(BaseTestGenerator):
 
     def _initialize_query_tool(self):
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -235,7 +235,7 @@ class TestQueryUpdatableResultset(BaseTestGenerator):
         utils.create_table_with_query(self.server, self.db_name, create_sql)
 
     def _close_query_tool(self):
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
 
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
index 0bef9b947..1b888d285 100644
--- a/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
@@ -921,7 +921,7 @@ class TestSaveChangedData(BaseTestGenerator):
 
     def _initialize_query_tool(self):
         self.trans_id = str(random.randint(1, 9999999))
-        url = '/datagrid/initialize/query_tool/{0}/{1}/{2}/{3}'.format(
+        url = '/sqleditor/initialize/sqleditor/{0}/{1}/{2}/{3}'.format(
             self.trans_id, utils.SERVER_GROUP, self.server_id, self.db_id)
         response = self.tester.post(url)
         self.assertEqual(response.status_code, 200)
@@ -955,6 +955,6 @@ class TestSaveChangedData(BaseTestGenerator):
         utils.create_table_with_query(self.server, self.db_name, create_sql)
 
     def _close_query_tool(self):
-        url = '/datagrid/close/{0}'.format(self.trans_id)
+        url = '/sqleditor/close/{0}'.format(self.trans_id)
         response = self.tester.delete(url)
         self.assertEqual(response.status_code, 200)
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
index 9c8e0e470..c884f3f10 100644
--- a/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_start_running_query.py
@@ -112,7 +112,6 @@ class StartRunningQueryTest(BaseTestGenerator):
                             'not found.',
                      can_edit=False,
                      can_filter=False,
-                     info_notifier_timeout=5000,
                      notifies=None,
                      transaction_status=None
                  )
@@ -273,7 +272,6 @@ class StartRunningQueryTest(BaseTestGenerator):
                      result='async function result output',
                      can_edit=True,
                      can_filter=True,
-                     info_notifier_timeout=5000,
                      notifies=None,
                      transaction_status=None
                  )
@@ -318,7 +316,6 @@ class StartRunningQueryTest(BaseTestGenerator):
                      result='async function result output',
                      can_edit=True,
                      can_filter=True,
-                     info_notifier_timeout=5000,
                      notifies=None,
                      transaction_status=None
                  )
@@ -363,7 +360,6 @@ class StartRunningQueryTest(BaseTestGenerator):
                      result='async function result output',
                      can_edit=True,
                      can_filter=True,
-                     info_notifier_timeout=5000,
                      notifies=None,
                      transaction_status=None
                  )
@@ -409,7 +405,6 @@ class StartRunningQueryTest(BaseTestGenerator):
                      result='async function result output',
                      can_edit=True,
                      can_filter=True,
-                     info_notifier_timeout=5000,
                      notifies=None,
                      transaction_status=None
                  )
@@ -444,8 +439,7 @@ class StartRunningQueryTest(BaseTestGenerator):
         if self.expect_internal_server_error_called_with is not None:
             internal_server_error_mock.return_value = expected_response
         pickle_mock.loads.return_value = self.pickle_load_return
-        blueprint_mock = MagicMock(
-            info_notifier_timeout=MagicMock(get=lambda: 5))
+        blueprint_mock = MagicMock()
 
         # Save value for the later use
         self.is_begin_required_for_sql_query = \
diff --git a/web/pgadmin/utils/constants.py b/web/pgadmin/utils/constants.py
index 9873371d2..35a5ed51d 100644
--- a/web/pgadmin/utils/constants.py
+++ b/web/pgadmin/utils/constants.py
@@ -34,6 +34,7 @@ SERVER_CONNECTION_CLOSED = gettext(
 
 # Query tool placeholder
 QT_DEFAULT_PLACEHOLDER = '%DATABASE%/%USERNAME%@%SERVER%'
+VW_EDT_DEFAULT_PLACEHOLDER = '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%'
 
 # Data Types
 DATATYPE_TIME_WITH_TIMEZONE = 'time with time zone'
diff --git a/web/pgadmin/utils/csrf.py b/web/pgadmin/utils/csrf.py
index f88cbf58e..7ad14eebe 100644
--- a/web/pgadmin/utils/csrf.py
+++ b/web/pgadmin/utils/csrf.py
@@ -31,8 +31,9 @@ class _PGCSRFProtect(CSRFProtect):
             'pgadmin.tools.translations',
             app.blueprints['redirects'],
             'pgadmin.browser.server_groups.servers.supported_servers-js',
-            'pgadmin.tools.datagrid.initialize_query_tool',
+            'pgadmin.tools.sqleditor.initialize_sqleditor',
             'pgadmin.tools.datagrid.panel',
+            'pgadmin.tools.sqleditor.panel',
             'pgadmin.tools.debugger.initialize_target',
             'pgadmin.tools.debugger.direct_new',
             'pgadmin.tools.schema_diff.panel',
diff --git a/web/regression/javascript/components/CodeMirror.spec.js b/web/regression/javascript/components/CodeMirror.spec.js
index 7e8d8f248..6cec715d4 100644
--- a/web/regression/javascript/components/CodeMirror.spec.js
+++ b/web/regression/javascript/components/CodeMirror.spec.js
@@ -52,9 +52,33 @@ describe('CodeMirror', ()=>{
       'setSelection': ()=>{/*This is intentional (SonarQube)*/},
       'scrollIntoView': ()=>{/*This is intentional (SonarQube)*/},
       'getWrapperElement': document.createElement('div'),
+      'on': ()=>{/*This is intentional (SonarQube)*/},
     });
   beforeEach(()=>{
     jasmineEnzyme();
+    window.pgAdmin = {
+      Browser: {
+        Events: {
+          on: jasmine.createSpy('on'),
+        },
+        get_preferences_for_module: ()=>({}),
+        docker: {
+          findPanels: function() {
+            return [
+              {
+                isVisible: function() {
+                  return true;
+                },
+              },
+            ];
+          },
+        },
+        onPreferencesChange: ()=>{/*This is intentional (SonarQube)*/},
+        utils: {
+          app_version_int: 1234,
+        },
+      },
+    };
     spyOn(OrigCodeMirror, 'fromTextArea').and.returnValue(cmObj);
     const ThemedCM = withTheme(CodeMirror);
     cmInstance = mount(
@@ -65,6 +89,10 @@ describe('CodeMirror', ()=>{
       />);
   });
 
+  afterEach(()=>{
+    window.pgAdmin = undefined;
+  });
+
   it('init', ()=>{
     /* textarea ref passed to fromTextArea */
     expect(OrigCodeMirror.fromTextArea).toHaveBeenCalledWith(cmInstance.find('textarea').getDOMNode(), jasmine.objectContaining(options));
diff --git a/web/regression/javascript/components/ShortcutTitle.spec.js b/web/regression/javascript/components/ShortcutTitle.spec.js
index a88f9d7ad..1508bba07 100644
--- a/web/regression/javascript/components/ShortcutTitle.spec.js
+++ b/web/regression/javascript/components/ShortcutTitle.spec.js
@@ -57,7 +57,6 @@ describe('ShortcutTitle', ()=>{
   });
 
   describe('shortcutToString', ()=>{
-
     it('shortcut', ()=>{
       spyOn(keyShort, 'isMac').and.returnValue(false);
       expect(shortcutToString(shortcut)).toBe('Ctrl + Shift + K');
@@ -69,6 +68,7 @@ describe('ShortcutTitle', ()=>{
     });
 
     it('accesskey', ()=>{
+      spyOnProperty(window.navigator, 'userAgent').and.returnValue('Unknown');
       expect(shortcutToString(null, 'A')).toEqual('Accesskey + A');
     });
 
diff --git a/web/regression/javascript/datagrid/get_panel_title_spec.js b/web/regression/javascript/datagrid/get_panel_title_spec.js
deleted file mode 100644
index 1230eb394..000000000
--- a/web/regression/javascript/datagrid/get_panel_title_spec.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {getPanelTitle} from '../../../pgadmin/tools/datagrid/static/js/datagrid_panel_title';
-import {TreeFake} from '../tree/tree_fake';
-import {TreeNode} from '../../../pgadmin/static/js/tree/tree_nodes';
-import {pgBrowser} from 'pgadmin.browser.preferences';
-
-const context = describe;
-
-var dummy_cache = [
-  {
-    id: 1,
-    mid: 1,
-    module:'browser',
-    name:'qt_tab_title_placeholder',
-    value: '%DATABASE%/%USERNAME%@%SERVER%',
-  },
-];
-
-describe('#getPanelTitle', () => {
-  let tree;
-  beforeEach(() => {
-    pgBrowser.preferences_cache = dummy_cache;
-    pgBrowser.Nodes = {
-      server: {
-        hasId: true,
-        _type: 'server',
-      },
-      database: {
-        hasId: true,
-        _type: 'database',
-      },
-    };
-    pgBrowser.preferences = {
-      'qt_tab_title_placeholder': '',
-    };
-    tree = new TreeFake(pgBrowser);
-    pgBrowser.tree = tree;
-  });
-
-  context('selected node does not belong to a server', () => {
-    it('returns undefined', () => {
-      const root = tree.addNewNode('level1', {_type: 'server_groups'});
-      tree.addChild(root, new TreeNode('level1.1', {_type: 'other'}));
-      tree.selectNode([{id: 'level1'}]);
-      expect(getPanelTitle(pgBrowser)).toBeUndefined();
-    });
-  });
-
-  context('selected node belong to a server', () => {
-    context('selected node does not belong to a database', () => {
-      it('returns the server label and the username', () => {
-        tree.addNewNode('level1', {
-          _type: 'server',
-          db: 'other db label',
-          user: {name: 'some user name'},
-          label: 'server label',
-        }, []);
-
-        tree.selectNode([{id: 'level1'}]);
-        expect(getPanelTitle(pgBrowser))
-          .toEqual('other db label/some user name@server label');
-      });
-    });
-
-    context('selected node belongs to a database', () => {
-      it('returns the database label and the username', () => {
-        const root = tree.addNewNode('level1', {
-          _type: 'server',
-          db: 'other db label',
-          user: {name: 'some user name'},
-          label: 'server label',
-        });
-        const level1 = new TreeNode('level1.1', {
-          _type: 'database',
-          label: 'db label',
-        });
-        tree.addChild(root, level1);
-        tree.addChild(level1,
-          new TreeNode('level1.1.1', {_type: 'table'}));
-        tree.selectNode([{id: 'level1.1.1'}]);
-        expect(getPanelTitle(pgBrowser))
-          .toEqual('db label/some user name@server label');
-      });
-    });
-  });
-});
diff --git a/web/regression/javascript/datagrid/show_data_spec.js b/web/regression/javascript/datagrid/show_data_spec.js
deleted file mode 100644
index 79f5696b3..000000000
--- a/web/regression/javascript/datagrid/show_data_spec.js
+++ /dev/null
@@ -1,167 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {showDataGrid} from '../../../pgadmin/tools/datagrid/static/js/show_data';
-import {TreeFake} from '../tree/tree_fake';
-import {TreeNode} from '../../../pgadmin/static/js/tree/tree_nodes';
-import {pgBrowser} from 'pgadmin.browser.preferences';
-import Notify from '../../../pgadmin/static/js/helpers/Notifier';
-
-const context = describe;
-
-var dummy_cache = [
-  {
-    id: 1,
-    mid: 1,
-    module:'browser',
-    name:'vw_edt_tab_title_placeholder',
-    value: '%SCHEMA%.%TABLE%/%DATABASE%/%USERNAME%@%SERVER%',
-  },
-];
-
-describe('#show_data', () => {
-  let datagrid;
-  let alertify;
-  let transId = 98765432;
-  beforeEach(() => {
-    pgBrowser.preferences_cache = dummy_cache;
-    alertify = jasmine.createSpyObj('alertify', ['alert', 'error']);
-    spyOn(Notify, 'error');
-    spyOn(Notify, 'alert');
-    datagrid = {
-      launch_grid: jasmine.createSpy('launch_grid'),
-    };
-    pgBrowser.Nodes = {
-      server_group: {
-        _type: 'server_group',
-        hasId: true,
-      },
-      server: {
-        _type: 'server',
-        hasId: true,
-      },
-      database: {
-        _type: 'database',
-        hasId: true,
-      },
-      schema: {
-        _type: 'schema',
-        hasId: true,
-      },
-      view: {
-        _type: 'view',
-        hasId: true,
-      },
-      catalog: {
-        _type: 'catalog',
-        hasId: true,
-      },
-    };
-    pgBrowser.tree = new TreeFake(pgBrowser);
-    const parent = pgBrowser.tree.addNewNode('parent', {_type: 'parent'}, []);
-    const serverGroup1 = new TreeNode('server_group1', {
-      _type: 'server_group',
-      _id: 1,
-    });
-    pgBrowser.tree.addChild(parent, serverGroup1);
-
-    const server1 = new TreeNode('server1', {
-      _type: 'server',
-      label: 'server1',
-      server_type: 'pg',
-      _id: 2,
-      user: {name: 'someuser'},
-    }, ['parent', 'server_group1']);
-    pgBrowser.tree.addChild(serverGroup1, server1);
-
-    const database1 = new TreeNode('database1', {
-      _type: 'database',
-      label: 'database1',
-      _id: 3,
-    }, ['parent', 'server_group1', 'server1']);
-    pgBrowser.tree.addChild(server1, database1);
-
-    const schema1 = new TreeNode('schema1', {
-      _type: 'schema',
-      label: 'schema1',
-      _id: 4,
-    });
-    pgBrowser.tree.addChild(database1, schema1);
-
-    const view1 = new TreeNode('view1', {
-      _type: 'view',
-      label: 'view1',
-      _id: 5,
-    }, ['parent', 'server_group1', 'server1', 'database1']);
-    pgBrowser.tree.addChild(database1, view1);
-
-    const catalog1 = new TreeNode('catalog1', {
-      _type: 'catalog',
-      label: 'catalog1',
-      _id: 6,
-    }, ['parent', 'server_group1', 'server1', 'database1']);
-    pgBrowser.tree.addChild(database1, catalog1);
-  });
-
-  context('cannot find the tree node', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}], transId);
-      expect(datagrid.launch_grid).not.toHaveBeenCalled();
-    });
-
-    it('display alert', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: '10'}], transId);
-      expect(Notify.alert).toHaveBeenCalledWith(
-        'Data Grid Error',
-        'No object selected.'
-      );
-    });
-  });
-
-  context('current node is not underneath a server', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'parent'}], transId);
-      expect(datagrid.launch_grid).not.toHaveBeenCalled();
-    });
-  });
-
-  context('current node is not underneath a schema or view or catalog', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {}, [{id: 'database1'}], transId);
-      expect(datagrid.launch_grid).not.toHaveBeenCalled();
-    });
-  });
-
-  context('current node is underneath a schema', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'schema1'}], transId);
-      expect(datagrid.launch_grid).not.toHaveBeenCalled();
-    });
-  });
-
-  context('current node is underneath a view', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'view1'}], transId);
-
-      expect(datagrid.launch_grid).toHaveBeenCalledWith(
-        98765432,
-        '/panel/98765432?is_query_tool=false&cmd_type=11&obj_type=view&obj_id=5&sgid=1&sid=2&did=3&server_type=pg',
-        false,
-        'view1.view1/database1/someuser@server1'
-      );
-    });
-  });
-
-  context('current node is underneath a catalog', () => {
-    it('does not create a transaction', () => {
-      showDataGrid(datagrid, pgBrowser, alertify, {mnuid: 11}, [{id: 'catalog1'}], transId);
-      expect(datagrid.launch_grid).not.toHaveBeenCalled();
-    });
-  });
-});
diff --git a/web/regression/javascript/datagrid/show_query_tool_spec.js b/web/regression/javascript/datagrid/show_query_tool_spec.js
deleted file mode 100644
index 5cb677d1f..000000000
--- a/web/regression/javascript/datagrid/show_query_tool_spec.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {TreeFake} from '../tree/tree_fake';
-import {showQueryTool} from '../../../pgadmin/tools/datagrid/static/js/show_query_tool';
-import {TreeNode} from '../../../pgadmin/static/js/tree/tree_nodes';
-import {pgBrowser} from 'pgadmin.browser.preferences';
-import Notify from '../../../pgadmin/static/js/helpers/Notifier';
-
-const context = describe;
-
-var dummy_cache = [
-  {
-    id: 1,
-    mid: 1,
-    module:'browser',
-    name:'qt_tab_title_placeholder',
-    value: '%DATABASE%/%USERNAME%@%SERVER%',
-  },
-];
-
-describe('#showQueryTool', () => {
-  let queryTool;
-  let transId = 98765432;
-  beforeEach(() => {
-    pgBrowser.preferences_cache = dummy_cache;
-    spyOn(Notify, 'alert');
-    queryTool = {
-      launch_grid: jasmine.createSpy('launch_grid'),
-    };
-    pgBrowser.Nodes = {
-      server_group: {
-        _type: 'server_group',
-        hasId: true,
-      },
-      server: {
-        _type: 'server',
-        hasId: true,
-      },
-      database: {
-        _type: 'database',
-        hasId: true,
-      },
-    };
-    pgBrowser.tree = new TreeFake(pgBrowser);
-
-    const parent = pgBrowser.tree.addNewNode('parent', {_type: 'parent'});
-    const serverGroup1 =  new TreeNode('server_group1', {
-      _type: 'server_group',
-      _id: 1,
-    }, ['parent']);
-    pgBrowser.tree.addChild(parent, serverGroup1);
-
-    const server1 = new TreeNode('server1', {
-      _type: 'server',
-      label: 'server1',
-      server_type: 'pg',
-      _id: 2,
-      user: {name: 'someuser'},
-      db: 'otherdblabel',
-    });
-    pgBrowser.tree.addChild(serverGroup1, server1);
-
-    const database1 = new  TreeNode('database1', {
-      _type: 'database',
-      label: 'database1',
-      _id: 3,
-    });
-    pgBrowser.tree.addChild(server1, database1);
-  });
-
-  context('cannot find the tree node', () => {
-    beforeEach(() => {
-      showQueryTool(queryTool, pgBrowser, '', [{id: '10'}], transId);
-    });
-    it('does not create a transaction', () => {
-      expect(queryTool.launch_grid).not.toHaveBeenCalled();
-    });
-
-    it('display alert', () => {
-      expect(Notify.alert).toHaveBeenCalledWith(
-        'Query Tool Error',
-        'No object selected.'
-      );
-    });
-  });
-
-  context('current node is not underneath a server', () => {
-    it('does not create a transaction', () => {
-      showQueryTool(queryTool, pgBrowser, '', [{id: 'parent'}], transId);
-      expect(queryTool.launch_grid).not.toHaveBeenCalled();
-    });
-
-    it('no alert is displayed', () => {
-      expect(Notify.alert).not.toHaveBeenCalled();
-    });
-  });
-
-  context('current node is underneath a server', () => {
-    context('current node is not underneath a database', () => {
-      it('creates a transaction', () => {
-        showQueryTool(queryTool, pgBrowser, 'http://someurl', [{id: 'server1'}], transId);
-        expect(queryTool.launch_grid).toHaveBeenCalledWith(
-          98765432,
-          '/panel/98765432?is_query_tool=true&sgid=1&sid=2&server_type=pg',
-          true,
-          'otherdblabel/someuser@server1',
-          'http://someurl'
-        );
-      });
-    });
-
-    context('current node is underneath a database', () => {
-      it('creates a transaction', () => {
-        showQueryTool(queryTool, pgBrowser, 'http://someurl', [{id: 'database1'}], transId);
-        expect(queryTool.launch_grid).toHaveBeenCalledWith(
-          98765432,
-          '/panel/98765432?is_query_tool=true&sgid=1&sid=2&server_type=pg&did=3',
-          true,
-          'database1/someuser@server1',
-          'http://someurl'
-        );
-      });
-    });
-  });
-});
diff --git a/web/regression/javascript/erd/ui_components/body_widget_spec.js b/web/regression/javascript/erd/ui_components/body_widget_spec.js
index 19eb0d34f..74afced32 100644
--- a/web/regression/javascript/erd/ui_components/body_widget_spec.js
+++ b/web/regression/javascript/erd/ui_components/body_widget_spec.js
@@ -9,7 +9,7 @@ import ERDCore from 'pgadmin.tools.erd/erd_tool/ERDCore';
 import * as erdModule from 'pgadmin.tools.erd/erd_module';
 import erdPref from './erd_preferences';
 import BodyWidget from 'pgadmin.tools.erd/erd_tool/ui_components/BodyWidget';
-import * as ERDSqlTool from 'tools/datagrid/static/js/show_query_tool';
+import * as ERDSqlTool from 'tools/sqleditor/static/js/show_query_tool';
 import { FakeLink, FakeNode, FakePort } from '../fake_item';
 import Notify from '../../../../pgadmin/static/js/helpers/Notifier';
 
@@ -42,6 +42,9 @@ let pgAdmin = {
     init: jasmine.createSpy(),
     show_dialog: jasmine.createSpy(),
   },
+  Tools: {
+    SQLEditor: {},
+  }
 };
 
 let pgWindow = {
diff --git a/web/regression/javascript/fake_endpoints.js b/web/regression/javascript/fake_endpoints.js
index ddd50fb8b..fd5ec175f 100644
--- a/web/regression/javascript/fake_endpoints.js
+++ b/web/regression/javascript/fake_endpoints.js
@@ -14,11 +14,11 @@ define(function () {
     'sqleditor.query_tool_start': '/sqleditor/query_tool/start/<path:trans_id>',
     'backup.create_server_job':  '/backup/job/<int:sid>',
     'backup.create_object_job':  '/backup/job/<int:sid>/object',
-    'datagrid.initialize_datagrid': '/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
-    'datagrid.initialize_query_tool': '/initialize/query_tool/<int:sgid>/<int:sid>',
-    'datagrid.initialize_query_tool_with_did': '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>',
+    'sqleditor.initialize_viewdata': '/initialize/sqleditor/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/<int:did>/<int:obj_id>',
+    'sqleditor.initialize_sqleditor': '/initialize/sqleditor/<int:sgid>/<int:sid>',
+    'sqleditor.initialize_sqleditor_with_did': '/initialize/sqleditor/<int:sgid>/<int:sid>/<int:did>',
     'restore.create_job': '/restore/job/<int:sid>',
-    'datagrid.panel': '/panel/<int:trans_id>',
+    'sqleditor.panel': '/panel/<int:trans_id>',
     'search_objects.types': '/search_objects/types/<int:sid>/<int:did>',
     'search_objects.search': '/search_objects/search/<int:sid>/<int:did>',
     'dashboard.dashboard_stats': '/dashboard/dashboard_stats',
diff --git a/web/regression/javascript/geometry_viewer/geometry_viewer_spec.js b/web/regression/javascript/geometry_viewer/geometry_viewer_spec.js
deleted file mode 100644
index 2710eb1e0..000000000
--- a/web/regression/javascript/geometry_viewer/geometry_viewer_spec.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import GeometryViewer from 'sources/sqleditor/geometry_viewer';
-import {Geometry} from 'wkx';
-
-describe('geometry viewer test', function () {
-
-  describe('geometry viewer add header button test', function () {
-    let add_button = GeometryViewer.add_header_button;
-    it('should add button for geography type', function () {
-      let columnDef = {
-        column_type_internal: 'geography',
-      };
-      add_button(columnDef);
-      expect(columnDef.header).toBeDefined();
-    });
-
-    it('should add button for geometry type', function () {
-      let columnDef = {
-        column_type_internal: 'geometry',
-      };
-      add_button(columnDef);
-      expect(columnDef.header).toBeDefined();
-    });
-  });
-
-  describe('geometry viewer rener geometry test', function () {
-
-    it('should group geometry by srid', function () {
-      // POINT(0 0)
-      let ewkb = '010100000000000000000000000000000000000000';
-      // SRID=32632;POINT(0 0)
-      let ewkb1 = '0101000020787F000000000000000000000000000000000000';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }, {
-        id: 1,
-        geom: ewkb,
-      }, {
-        id: 1,
-        geom: ewkb1,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(2);
-    });
-
-
-    it('should support geometry collection', function () {
-      // GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3,3 4))
-      let ewkb = '01070000000200000001010000000000000000000040000000000000084001' +
-        '02000000020000000000000000000040000000000000084000000000000008400000000' +
-        '000001040';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(1);
-    });
-
-    it('should support geometry M', function () {
-      // SRID=4326;MULTIPOINTM(0 0 0,1 2 1)
-      let ewkb = '0104000060E610000002000000010100004000000000000000000000000000' +
-        '00000000000000000000000101000040000000000000F03F00000000000000400000000' +
-        '00000F03F';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(1);
-    });
-
-    it('should support empty geometry', function () {
-      // GEOMETRYCOLLECTION EMPTY
-      let ewkb = '010700000000000000';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(1);
-    });
-
-
-    it('should support mixed geometry type', function () {
-      // GEOMETRYCOLLECTION EMPTY
-      let ewkb = '010700000000000000';
-      // POINT(0 0)
-      let ewkb1 = '010100000000000000000000000000000000000000';
-      // SRID=4326;MULTIPOINTM(0 0 0,1 2 1)
-      let ewkb2 = '0104000060E610000002000000010100004000000000000000000000000000' +
-        '00000000000000000000000101000040000000000000F03F00000000000000400000000' +
-        '00000F03F';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }, {
-        id: 1,
-        geom: ewkb1,
-      }, {
-        id: 1,
-        geom: ewkb2,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(2);
-    });
-
-
-    it('should not support 3D geometry', function () {
-      // POINT(0 0 0)
-      let ewkb = '0101000080000000000000F03F000000000000F03F000000000000F03F';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(0);
-    });
-
-    it('should not support 3DM geometry', function () {
-      // POINT(0 0 0 0)
-      let ewkb = '01010000C00000000000000000000000000000000000000000000000000000' +
-        '000000000000';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(0);
-    });
-
-    it('should not support TRIANGLE geometry', function () {
-      // TRIANGLE ((0 0, 0 9, 9 0, 0 0))
-      let ewkb = '01110000000100000004000000000000000000000000000000000000000000' +
-        '00000000000000000000000022400000000000002240000000000000000000000000000' +
-        '000000000000000000000';
-      let items = [{
-        id: 1,
-        geom: ewkb,
-      }];
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toEqual(0);
-    });
-
-    it('should limit data size', function () {
-      // POINT(0 0)
-      let ewkb = '010100000000000000000000000000000000000000';
-      let items = [];
-      for (let i = 0; i < 600000; i++) {
-        items.push({
-          id: i,
-          geom: ewkb,
-        });
-      }
-
-      let columns = [
-        {
-          column_type_internal: 'geometry',
-          field: 'geom',
-        },
-      ];
-      let columnIndex = 0;
-      let result = GeometryViewer.parse_data(items, columns, columnIndex, Geometry);
-      expect(result.geoJSONs.length).toBeLessThan(600000);
-    });
-  });
-});
diff --git a/web/regression/javascript/history/history_collection_spec.js b/web/regression/javascript/history/history_collection_spec.js
deleted file mode 100644
index fef3e674b..000000000
--- a/web/regression/javascript/history/history_collection_spec.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import HistoryCollection from 'sources/sqleditor/history/history_collection';
-
-describe('historyCollection', function () {
-  let historyCollection, historyModel, onAddSpy, onResetSpy;
-  beforeEach(() => {
-    historyModel = [{some: 'thing', someOther: ['array element']}];
-    historyCollection = new HistoryCollection(historyModel);
-    onAddSpy = jasmine.createSpy('onAddHandler');
-    onResetSpy = jasmine.createSpy('onResetHandler');
-
-    historyCollection.onAdd(onAddSpy);
-    historyCollection.onReset(onResetSpy);
-  });
-
-  describe('length', function () {
-    it('returns 0 when underlying history model has no elements', function () {
-      historyCollection = new HistoryCollection([]);
-
-      expect(historyCollection.length()).toEqual(0);
-    });
-
-    it('returns the length of the underlying history model', function () {
-      expect(historyCollection.length()).toEqual(1);
-    });
-  });
-
-  describe('add', function () {
-    let expectedHistory;
-    let newEntry = {some: 'new thing', someOther: ['value1', 'value2']};
-    beforeEach(() => {
-      historyCollection.add(newEntry);
-
-      expectedHistory = [
-        {some: 'new thing', someOther: ['value1', 'value2']},
-        {some: 'thing', someOther: ['array element']},
-      ];
-    });
-
-    it('adds a passed entry', function () {
-      expect(historyCollection.historyList).toEqual(expectedHistory);
-    });
-
-    it('calls the onChange function', function () {
-      expect(onAddSpy).toHaveBeenCalledWith(newEntry);
-    });
-  });
-
-  describe('reset', function () {
-    beforeEach(() => {
-      historyCollection.reset();
-    });
-
-    it('drops the history', function () {
-      expect(historyCollection.historyList).toEqual([]);
-      expect(historyCollection.length()).toEqual(0);
-    });
-
-    it('calls the onReset function', function () {
-      expect(onResetSpy).toHaveBeenCalledWith([]);
-    });
-  });
-
-  describe('when instantiated', function () {
-    describe('from a history model', function () {
-      it('has the historyModel', () => {
-        let content = historyCollection.historyList;
-
-        expect(content).toEqual(historyModel);
-      });
-
-    });
-  });
-});
diff --git a/web/regression/javascript/history/query_history_spec.js b/web/regression/javascript/history/query_history_spec.js
deleted file mode 100644
index 34a0a1247..000000000
--- a/web/regression/javascript/history/query_history_spec.js
+++ /dev/null
@@ -1,405 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-/* eslint-disable react/no-find-dom-node */
-
-import QueryHistory from 'sources/sqleditor/history/query_history';
-import HistoryCollection from 'sources/sqleditor/history/history_collection';
-import clipboard from 'sources/selection/clipboard';
-import $ from 'jquery';
-import moment from 'moment';
-
-describe('QueryHistory', () => {
-  let historyCollection;
-  let historyWrapper;
-  let historyComponent;
-
-  beforeEach(() => {
-    historyWrapper = $('<div id="history_grid"></div>').appendTo('body');
-  });
-
-  describe('when there is no history', () => {
-    beforeEach(() => {
-      historyCollection = new HistoryCollection([]);
-
-      historyComponent = new QueryHistory(historyWrapper, historyCollection);
-      historyComponent.render();
-    });
-
-    it('has no entries', (done) => {
-      let foundChildren = historyWrapper.find('#query_list');
-      expect(foundChildren.length).toBe(0);
-      done();
-    });
-
-    it('No history found is displayed', (done) => {
-      expect(historyWrapper.find('.pg-panel-message').html()).toBe('No history found');
-      done();
-    });
-  });
-
-  describe('when there is history', () => {
-    let queryEntries;
-    let queryDetail;
-
-    describe('when two SQL queries were executed', () => {
-
-      beforeEach(() => {
-        const historyObjects = [{
-          query: 'first sql statement',
-          start_time: new Date(2017, 5, 3, 14, 3, 15, 150),
-          status: true,
-          row_affected: 12345,
-          total_time: '14 msec',
-          message: 'something important ERROR:  message from first sql query',
-        }, {
-          query: 'second sql statement',
-          start_time: new Date(2016, 11, 11, 1, 33, 5, 99),
-          status: false,
-          row_affected: 1,
-          total_time: '234 msec',
-          message: 'something important ERROR:  message from second sql query',
-        }];
-
-        historyCollection = new HistoryCollection(historyObjects);
-        historyComponent = new QueryHistory(historyWrapper, historyCollection);
-        historyComponent.onCopyToEditorClick(()=>{/*This is intentional (SonarQube)*/});
-        historyComponent.render();
-
-        queryEntries = historyWrapper.find('#query_list .list-item');
-        queryDetail = historyWrapper.find('#query_detail');
-      });
-
-      describe('the history entries panel', () => {
-        it('has two query history entries', (done) => {
-          expect(queryEntries.length).toBe(2);
-          done();
-        });
-
-        it('displays the query history entries in order', () => {
-          expect($(queryEntries[0]).text()).toContain('first sql statement');
-          expect($(queryEntries[1]).text()).toContain('second sql statement');
-        });
-
-        it('displays the formatted timestamp of the queries in chronological order by most recent first', () => {
-          expect($(queryEntries[0]).find('.timestamp').text()).toBe('14:03:15');
-          expect($(queryEntries[1]).find('.timestamp').text()).toBe('01:33:05');
-        });
-
-        it('renders the most recent query as selected', (done) => {
-          expect($(queryEntries[0]).hasClass('selected')).toBeTruthy();
-          done();
-        });
-
-        it('renders the older query as not selected', () => {
-          expect($(queryEntries[1]).hasClass('selected')).toBeFalsy();
-          expect($(queryEntries[1]).find('.entry').hasClass('error')).toBeTruthy();
-        });
-      });
-
-      describe('the historydetails panel', () => {
-        let copyAllButton, copyEditorButton;
-
-        beforeEach(() => {
-          copyAllButton = () => queryDetail.find('#history-detail-query .btn-copy');
-          copyEditorButton = () => queryDetail.find('#history-detail-query .btn-copy-editor');
-        });
-
-        it('should change preference font size', ()=>{
-          historyComponent.setEditorPref({sql_font_size: '1.5em'});
-          expect(queryDetail.find('#history-detail-query .CodeMirror').attr('style')).toBe('font-size: 1.5em;');
-        });
-
-        it('should change preference copy to editor false', ()=>{
-          historyComponent.setEditorPref({copy_to_editor: false});
-          expect($(queryDetail.find('#history-detail-query .btn-copy-editor')).hasClass('d-none')).toBe(true);
-        });
-
-        it('displays the formatted timestamp', () => {
-          let firstDate = new Date(2017, 5, 3, 14, 3, 15, 150);
-          expect(queryDetail.text()).toContain(firstDate.toLocaleDateString() + ' ' + firstDate.toLocaleTimeString());
-        });
-
-        it('displays the number of rows affected', () => {
-          if (/PhantomJS/.test(window.navigator.userAgent)) {
-            expect(queryDetail.text()).toContain('12345');
-          } else {
-            expect(queryDetail.text()).toContain('12,345');
-          }
-        });
-
-        it('displays the total time', () => {
-          expect(queryDetail.text()).toContain('14 msec');
-        });
-
-        it('displays the full message', () => {
-          expect(queryDetail.text()).toContain('message from first sql query');
-        });
-
-        it('displays first query SQL', (done) => {
-          setTimeout(() => {
-            expect(queryDetail.text()).toContain('first sql statement');
-            done();
-          }, 1000);
-        });
-
-        describe('when the "Copy" button is clicked', () => {
-          beforeEach(() => {
-            spyOn(clipboard, 'copyTextToClipboard');
-            copyAllButton().trigger('click');
-          });
-
-          it('copies the query to the clipboard', () => {
-            expect(clipboard.copyTextToClipboard).toHaveBeenCalledWith('first sql statement');
-          });
-        });
-
-        describe('Copy button', () => {
-          beforeEach(() => {
-            jasmine.clock().install();
-          });
-
-          afterEach(() => {
-            jasmine.clock().uninstall();
-          });
-
-          it('should have text \'Copy\'', () => {
-            expect(copyAllButton().text()).toBe('Copy');
-          });
-
-          it('should not have the class \'was-copied\'', () => {
-            expect(copyAllButton().hasClass('was-copied')).toBe(false);
-          });
-
-          describe('when the copy button is clicked', () => {
-            beforeEach(() => {
-              copyAllButton().trigger('click');
-            });
-
-            describe('before 1.5 seconds', () => {
-              beforeEach(() => {
-                jasmine.clock().tick(1499);
-              });
-
-              it('1.5 seconds should change the button text to \'Copied!\'', () => {
-                expect(copyAllButton().text()).toBe('Copied!');
-              });
-
-              it('should have the class \'was-copied\'', () => {
-                expect(copyAllButton().hasClass('was-copied')).toBe(true);
-              });
-            });
-
-            describe('after 1.5 seconds', () => {
-              beforeEach(() => {
-                jasmine.clock().tick(1501);
-              });
-
-              it('1.5 seconds should change the button text back to \'Copy\'', () => {
-                expect(copyAllButton().text()).toBe('Copy');
-              });
-            });
-
-            describe('when is clicked again after 1s', () => {
-              beforeEach(() => {
-                jasmine.clock().tick(1000);
-                copyAllButton().trigger('click');
-
-              });
-
-              describe('before 2.5 seconds', () => {
-                beforeEach(() => {
-                  jasmine.clock().tick(1499);
-                });
-
-                it('should change the button text to \'Copied!\'', () => {
-                  expect(copyAllButton().text()).toBe('Copied!');
-                });
-
-                it('should have the class \'was-copied\'', () => {
-                  expect(copyAllButton().hasClass('was-copied')).toBe(true);
-                });
-              });
-
-              describe('after 2.5 seconds', () => {
-                beforeEach(() => {
-                  jasmine.clock().tick(1501);
-                });
-
-                it('should change the button text back to \'Copy\'', () => {
-                  expect(copyAllButton().text()).toBe('Copy');
-                });
-              });
-            });
-          });
-        });
-
-        describe('when the "Copy to query editor" button is clicked', () => {
-          beforeEach(() => {
-            spyOn(historyComponent.queryHistDetails, 'onCopyToEditorHandler').and.callThrough();
-            copyEditorButton().trigger('click');
-          });
-
-          it('sends the query to the onCopyToEditorHandler', () => {
-            expect(historyComponent.queryHistDetails.onCopyToEditorHandler).toHaveBeenCalledWith('first sql statement');
-          });
-        });
-
-        describe('when the query failed', () => {
-          let failedEntry;
-
-          beforeEach(() => {
-            failedEntry = $(queryEntries[1]);
-            failedEntry.trigger('click');
-          });
-
-          it('displays the error message on top of the details pane', () => {
-            expect(queryDetail.text()).toContain('Error Message message from second sql query');
-          });
-        });
-      });
-
-
-      describe('when the older query is clicked on', () => {
-        let firstEntry, secondEntry;
-
-        beforeEach(() => {
-          firstEntry = $(queryEntries[0]);
-          secondEntry = $(queryEntries[1]);
-          secondEntry.trigger('click');
-        });
-
-        it('displays the query in the right pane', () => {
-          expect(queryDetail.text()).toContain('second sql statement');
-        });
-
-        it('deselects the first history entry', () => {
-          expect(firstEntry.hasClass('selected')).toBeFalsy();
-        });
-
-        it('selects the second history entry', () => {
-          expect(secondEntry.hasClass('selected')).toBeTruthy();
-        });
-      });
-
-      describe('when a third SQL query is executed', () => {
-        beforeEach(() => {
-          historyCollection.add({
-            query: 'third sql statement',
-            start_time: new Date(2017, 11, 11, 1, 33, 5, 99),
-            status: false,
-            row_affected: 5,
-            total_time: '26 msec',
-            message: 'pretext ERROR:  third sql message',
-          });
-
-          queryEntries = historyWrapper.find('#query_list .list-item');
-        });
-
-        it('displays third query SQL in the right pane', () => {
-          expect(queryDetail.text()).toContain('third sql statement');
-        });
-      });
-
-      describe('when a fourth SQL query is executed', () => {
-        beforeEach(() => {
-          historyCollection.add({
-            query: 'fourth sql statement',
-            start_time: new Date(2017, 12, 12, 1, 33, 5, 99),
-            status: false,
-            row_affected: 0,
-            total_time: '26 msec',
-            message: 'ERROR: unexpected error from fourth sql message',
-          });
-
-          queryEntries = historyWrapper.find('#query_list .list-item');
-        });
-
-        it('displays fourth query SQL in the right pane', () => {
-          expect(queryDetail.text()).toContain('Error Message unexpected error from fourth sql message');
-        });
-      });
-    });
-
-    describe('when several days of queries were executed', () => {
-      let queryEntryDateGroups;
-      let dateToday, dateYest, dateBeforeYest;
-
-      beforeEach(() => {
-        jasmine.clock().install();
-        const mockedCurrentDate = moment('2017-07-01 13:30:00');
-        jasmine.clock().mockDate(mockedCurrentDate.toDate());
-
-        dateToday = mockedCurrentDate.toDate();
-        dateYest = mockedCurrentDate.clone().subtract(1, 'days').toDate();
-        dateBeforeYest = mockedCurrentDate.clone().subtract(3, 'days').toDate();
-
-        const historyObjects = [{
-          query: 'first today sql statement',
-          start_time: mockedCurrentDate.toDate(),
-          status: true,
-          row_affected: 12345,
-          total_time: '14 msec',
-          message: 'message from first today sql query',
-        }, {
-          query: 'second today sql statement',
-          start_time: mockedCurrentDate.clone().subtract(1, 'hours').toDate(),
-          status: false,
-          row_affected: 1,
-          total_time: '234 msec',
-          message: 'message from second today sql query',
-        }, {
-          query: 'first yesterday sql statement',
-          start_time: mockedCurrentDate.clone().subtract(1, 'days').toDate(),
-          status: true,
-          row_affected: 12345,
-          total_time: '14 msec',
-          message: 'message from first yesterday sql query',
-        }, {
-          query: 'second yesterday sql statement',
-          start_time: mockedCurrentDate.clone().subtract(1, 'days').subtract(1, 'hours').toDate(),
-          status: false,
-          row_affected: 1,
-          total_time: '234 msec',
-          message: 'message from second yesterday sql query',
-        }, {
-          query: 'older than yesterday sql statement',
-          start_time: mockedCurrentDate.clone().subtract(3, 'days').toDate(),
-          status: true,
-          row_affected: 12345,
-          total_time: '14 msec',
-          message: 'message from older than yesterday sql query',
-        }];
-        historyCollection = new HistoryCollection(historyObjects);
-
-        historyComponent = new QueryHistory(historyWrapper, historyCollection);
-        historyComponent.render();
-
-        queryEntries = historyWrapper.find('#query_list .list-item');
-        queryEntryDateGroups = historyWrapper.find('#query_list .query-group');
-      });
-
-      afterEach(() => {
-        jasmine.clock().uninstall();
-      });
-
-      describe('the history entries panel', () => {
-        it('has three query history entry data groups', () => {
-          expect(queryEntryDateGroups.length).toBe(3);
-        });
-
-        it('has title above', () => {
-          expect($(queryEntryDateGroups[0]).find('.date-label').text()).toBe('Today - ' + dateToday.toLocaleDateString());
-          expect($(queryEntryDateGroups[1]).find('.date-label').text()).toBe('Yesterday - ' + dateYest.toLocaleDateString());
-          expect($(queryEntryDateGroups[2]).find('.date-label').text()).toBe(dateBeforeYest.toLocaleDateString());
-        });
-      });
-    });
-  });
-});
diff --git a/web/regression/javascript/sqleditor/calculate_query_run_time_spec.js b/web/regression/javascript/sqleditor/calculate_query_run_time_spec.js
deleted file mode 100644
index e0e81d0d2..000000000
--- a/web/regression/javascript/sqleditor/calculate_query_run_time_spec.js
+++ /dev/null
@@ -1,105 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import moment from 'moment';
-import {calculateQueryRunTime} from '../../../pgadmin/static/js/sqleditor/calculate_query_run_time';
-
-describe('#calculateQueryRunTime', () => {
-  describe('time difference is smaller then 1 second', () => {
-    it('displays milliseconds', () => {
-      let startDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:30,
-        seconds:20,
-        milliseconds:123}).toDate();
-      let endDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:30,
-        seconds:21,
-        milliseconds:70}).toDate();
-      expect(calculateQueryRunTime(startDate, endDate))
-        .toEqual('947 msec');
-    });
-  });
-
-  describe('time difference is smaller then 1 minute', () => {
-    it('displays milliseconds', () => {
-      let startDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:30,
-        seconds:20,
-        milliseconds:123}).toDate();
-      let endDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:31,
-        seconds:15,
-        milliseconds:70}).toDate();
-      expect(calculateQueryRunTime(startDate, endDate))
-        .toEqual('54 secs 947 msec');
-    });
-  });
-
-  describe('time difference is bigger then 1 minute', () => {
-    it('displays milliseconds', () => {
-      let startDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:30,
-        seconds:20,
-        milliseconds:123}).toDate();
-      let endDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:40,
-        seconds:15,
-        milliseconds:70}).toDate();
-      expect(calculateQueryRunTime(startDate, endDate))
-        .toEqual('9 min 54 secs');
-    });
-  });
-
-  describe('time difference is bigger then 1 hour', () => {
-    it('displays seconds, milliseconds', () => {
-      let startDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:10,
-        minutes:30,
-        seconds:20,
-        milliseconds:123}).toDate();
-      let endDate = moment({
-        years:2018,
-        months:4,
-        date:2,
-        hours:11,
-        minutes:40,
-        seconds:15,
-        milliseconds:70}).toDate();
-      expect(calculateQueryRunTime(startDate, endDate))
-        .toEqual('1 hr 9 min');
-    });
-  });
-});
diff --git a/web/regression/javascript/sqleditor/call_render_after_poll_spec.js b/web/regression/javascript/sqleditor/call_render_after_poll_spec.js
deleted file mode 100644
index 9035f1612..000000000
--- a/web/regression/javascript/sqleditor/call_render_after_poll_spec.js
+++ /dev/null
@@ -1,202 +0,0 @@
-/////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////
-
-import {callRenderAfterPoll} from '../../../pgadmin/static/js/sqleditor/call_render_after_poll';
-import moment from 'moment';
-
-describe('#callRenderAfterPoll', () => {
-  let sqlEditorSpy, queryResult, Notify;
-  beforeEach(() => {
-    let today = moment('2018-01-01 10:12:31').toDate();
-    jasmine.clock().install();
-    jasmine.clock().mockDate(today);
-    sqlEditorSpy = {
-      _render: jasmine.createSpy('SQLEditor._render'),
-      setIsQueryRunning: jasmine.createSpy('SQLEditor.setIsQueryRunning'),
-      trigger: jasmine.createSpy('SQLEditor.trigger'),
-      update_msg_history: jasmine.createSpy('SQLEditor.update_msg_history'),
-      disable_tool_buttons: jasmine.createSpy('SQLEditor.disable_tool_buttons'),
-      disable_transaction_buttons: jasmine.createSpy('SQLEditor.disable_transaction_buttons'),
-      reset_data_store: jasmine.createSpy('SQLEditor.reset_data_store'),
-      enable_disable_download_btn: jasmine.createSpy('SQLEditor.enable_disable_download_btn'),
-      check_db_name_change: jasmine.createSpy('SQLEditor.check_db_name_change'),
-      query_start_time: new Date(),
-    };
-    Notify = jasmine.createSpyObj('Notify', ['success']);
-  });
-
-  afterEach(function () {
-    jasmine.clock().uninstall();
-  });
-
-  let expectAction = (expectObj, callWithValue=null)=> {
-    callRenderAfterPoll(sqlEditorSpy, Notify, queryResult);
-    if (callWithValue !== null) {
-      expect(expectObj).toHaveBeenCalledWith(callWithValue);
-    } else {
-      expect(expectObj).toHaveBeenCalledWith();
-    }
-  };
-
-  let queryResult1 = ()=>{
-    queryResult = {
-      rows_affected: 10,
-      has_more_rows: false,
-      colinfo: {},
-    };
-  };
-
-  let queryResult2 = ()=>{
-    queryResult = {
-      rows_affected: 10,
-      has_more_rows: false,
-      colinfo: undefined,
-      result: 'Some result',
-    };
-  };
-
-  let displayNotificationAction = ()=> {
-    sqlEditorSpy.info_notifier_timeout = 10;
-    callRenderAfterPoll(sqlEditorSpy, Notify, queryResult);
-
-    expect(Notify.success).toHaveBeenCalledWith(
-      'Query returned successfully in 0 msec.',
-      10
-    );
-  };
-
-  let saveAction = ()=> {
-    callRenderAfterPoll(sqlEditorSpy, Notify, queryResult);
-
-    expect(sqlEditorSpy.update_msg_history).toHaveBeenCalledWith(
-      true,
-      'Some result\n\nQuery returned successfully in 0 msec.',
-      false
-    );
-  };
-
-  describe('it is not a query tool', () => {
-    beforeEach(() => {
-      sqlEditorSpy.is_query_tool = false;
-    });
-
-    describe('query was successful and have results', () => {
-      beforeEach(queryResult1);
-
-      it('renders the editor', () => {
-        expectAction(sqlEditorSpy._render, queryResult);
-      });
-
-      it('inform sqleditor that the query stopped running', () => {
-        expectAction(sqlEditorSpy.setIsQueryRunning, false);
-      });
-
-      it('hides the loading icon', () => {
-        expectAction(sqlEditorSpy.trigger, 'pgadmin-sqleditor:loading-icon:hide');
-      });
-    });
-
-    describe('query was successful but had no result to display', () => {
-      beforeEach(queryResult2);
-
-      it('saves execution information in the history', () => {
-        saveAction();
-      });
-
-      it('resets the changed data store', () => {
-        expectAction(sqlEditorSpy.reset_data_store);
-      });
-
-      it('inform sqleditor that the query stopped running', () => {
-        expectAction(sqlEditorSpy.setIsQueryRunning, false);
-      });
-
-      it('hides the loading icon', () => {
-        expectAction(sqlEditorSpy.trigger, 'pgadmin-sqleditor:loading-icon:hide');
-      });
-
-      describe('notifications are enabled', () => {
-        it('display notification', displayNotificationAction);
-      });
-
-      it('disables the save results button', () => {
-        expectAction(sqlEditorSpy.enable_disable_download_btn, true);
-        expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
-      });
-    });
-  });
-
-  describe('it is a query tool', () => {
-    beforeEach(() => {
-      sqlEditorSpy.is_query_tool = true;
-    });
-
-    describe('query was successful and have results', () => {
-      beforeEach(queryResult1);
-
-      it('renders the editor', () => {
-        expectAction(sqlEditorSpy._render, queryResult);
-      });
-
-      it('inform sqleditor that the query stopped running', () => {
-        expectAction(sqlEditorSpy.setIsQueryRunning, false);
-      });
-
-      it('hides the loading icon', () => {
-        expectAction(sqlEditorSpy.trigger, 'pgadmin-sqleditor:loading-icon:hide');
-      });
-
-      it('enables sqleditor tools buttons', () => {
-        expectAction(sqlEditorSpy.disable_tool_buttons, false);
-      });
-    });
-
-    describe('query was successful but had no result to display', () => {
-      beforeEach(queryResult2);
-
-      it('saves execution information in the history', () => {
-        saveAction();
-      });
-
-      it('resets the changed data store', () => {
-        expectAction(sqlEditorSpy.reset_data_store);
-      });
-
-      it('inform sqleditor that the query stopped running', () => {
-        expectAction(sqlEditorSpy.setIsQueryRunning, false);
-      });
-
-      it('hides the loading icon', () => {
-        expectAction(sqlEditorSpy.trigger, 'pgadmin-sqleditor:loading-icon:hide');
-      });
-
-      it('enables sqleditor tools buttons', () => {
-        expectAction(sqlEditorSpy.disable_tool_buttons, false);
-      });
-
-      describe('notifications are enabled', () => {
-        it('display notification', displayNotificationAction);
-      });
-
-      it('disables the save results button', () => {
-        expectAction(sqlEditorSpy.enable_disable_download_btn, true);
-
-        expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
-      });
-
-      it('check whether database has been moved/renamed', () => {
-        callRenderAfterPoll(sqlEditorSpy, Notify, queryResult);
-
-        expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:check_synchronous_db_name_change', queryResult);
-
-        expect(sqlEditorSpy.trigger).toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
-      });
-    });
-  });
-});
diff --git a/web/regression/javascript/sqleditor/execute_query_spec.js b/web/regression/javascript/sqleditor/execute_query_spec.js
deleted file mode 100644
index 9a13401e2..000000000
--- a/web/regression/javascript/sqleditor/execute_query_spec.js
+++ /dev/null
@@ -1,1044 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-import * as subject from 'sources/sqleditor/execute_query';
-import * as httpErrorHandler from 'sources/sqleditor/query_tool_http_error_handler';
-import axios from 'axios';
-import MockAdapter from 'axios-mock-adapter';
-import $ from 'jquery';
-
-const context = describe;
-
-describe('ExecuteQuery', () => {
-  let sqlEditorMock;
-  let networkMock;
-  let executeQuery;
-  let userManagementMock;
-  let isNewTransactionRequiredMock;
-
-  const startTime = new Date(2018, 1, 29, 12, 15, 52);
-  beforeEach(() => {
-    networkMock = new MockAdapter(axios);
-    userManagementMock = jasmine.createSpyObj('UserManagement', [
-      'isPgaLoginRequired',
-      'pgaLogin',
-    ]);
-
-    sqlEditorMock = jasmine.createSpyObj('SqlEditor', [
-      'call_render_after_poll',
-      'disable_tool_buttons',
-      'resetQueryHistoryObject',
-      'setIsQueryRunning',
-      'trigger',
-      'update_msg_history',
-      '_highlight_error',
-      '_init_polling_flags',
-      'saveState',
-      'initTransaction',
-      'handle_connection_lost',
-      'update_notifications',
-      'disable_transaction_buttons',
-      'enable_disable_download_btn',
-    ]);
-    sqlEditorMock.transId = 123;
-    sqlEditorMock.rows_affected = 1000;
-    executeQuery = new subject.ExecuteQuery(sqlEditorMock, userManagementMock);
-    isNewTransactionRequiredMock = spyOn(httpErrorHandler, 'httpResponseRequiresNewTransaction');
-  });
-
-  afterEach(() => {
-    networkMock.restore();
-  });
-
-  let updateHistoryNotCalled = (done)=> {
-    setTimeout(() => {
-      expect(sqlEditorMock.update_msg_history).not.toHaveBeenCalled();
-      done();
-    }, 0);
-  };
-
-  let pgaLoginNotCalled = (done)=> {
-    setTimeout(() => {
-      expect(userManagementMock.pgaLogin).not.toHaveBeenCalled();
-      done();
-    }, 0);
-  };
-
-  let disableToolButtonsNotCalled = (done)=> {
-    setTimeout(() => {
-      expect(sqlEditorMock.disable_tool_buttons).not.toHaveBeenCalled();
-      done();
-    }, 0);
-  };
-
-  let cancelButtonNotCalled = (done)=> {
-    let cancelButtonSpy = spyOn($.fn, 'prop');
-    setTimeout(() => {
-      expect(cancelButtonSpy).not.toHaveBeenCalled();
-      done();
-    }, 0);
-  };
-
-  let beforeEachNotConnected = ()=> {
-    let response = {
-      data: {
-        status: 'NotConnected',
-        result: 'Some interesting result',
-      },
-    };
-    networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-    executeQuery.poll();
-  };
-
-  let beforeEachPythonServer = ()=> {
-    networkMock.onGet('/sqleditor/query_tool/poll/123').reply(404, undefined);
-    executeQuery.poll();
-  };
-
-  let setFlagQueryIsRunningTestCase = ()=> {
-    it('should set the flag to inform SQLEditor a query is running', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.setIsQueryRunning)
-          .toHaveBeenCalledWith(true);
-        done();
-      }, 0);
-    });
-  };
-
-  let updateHistoryMessageTestCase = ()=> {
-    it('should update history message', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.update_msg_history)
-          .toHaveBeenCalledWith('Busy', 'Some important result', false);
-        done();
-      }, 0);
-    });
-  };
-
-  let callPoolingTestCase = ()=> {
-    it('should recursively call polling', (done) => {
-      setTimeout(() => {
-        expect(executeQuery.delayedPoll)
-          .toHaveBeenCalled();
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldNotUpdateHistoryMessageTestCase = ()=> {
-    it('should does not update history', (done) => {
-      updateHistoryNotCalled(done);
-    });
-  };
-
-  let shouldHideLoadingIconTestCase = ()=> {
-    it('should hide the loading icon', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.trigger)
-          .toHaveBeenCalledWith('pgadmin-sqleditor:loading-icon:hide');
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldUpdateLoadingIconMessageTestCase = ()=>{
-    it('should update the loading icon message', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.trigger)
-          .toHaveBeenCalledWith(
-            'pgadmin-sqleditor:loading-icon:message',
-            'Loading data from the database server and rendering...'
-          );
-        done();
-      }, 0);
-    });
-  };
-
-  let addNewEntryToHistoryTestCase = ()=>{
-    it('should add new entry to history and update the Messages tab and clear the result grid', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.update_msg_history)
-          .toHaveBeenCalledWith(
-            false,
-            'Some interesting result',
-            true
-          );
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldEnableToolsButtonTestCase = ()=>{
-    it('should enable the tool buttons', (done) => {
-      setTimeout(
-        () => {
-          expect(sqlEditorMock.disable_tool_buttons).toHaveBeenCalledWith(false);
-          done();
-        }, 0);
-    });
-  };
-
-  let addNewEntryToHistoryExecutionCancelTestCase = ()=> {
-    it('should add new entry to history, add cancellation message to Messages tab and clear the result grid', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.update_msg_history)
-          .toHaveBeenCalledWith(
-            false,
-            'Execution Cancelled!',
-            true
-          );
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldResetLastQueryInformationTestCase = ()=> {
-    it('should reset last query information', (done) => {
-      setTimeout(
-        () => {
-          expect(sqlEditorMock.resetQueryHistoryObject)
-            .toHaveBeenCalledWith(sqlEditorMock);
-          done();
-        }, 0);
-    });
-  };
-
-  let shouldNotAddNewEntryTestCase = ()=> {
-    it('should not add new entry to history and update the Messages tab', (done) => {
-      updateHistoryNotCalled(done);
-    });
-  };
-
-  let shouldNotHighlightErrorTestCase = ()=>{
-    it('should not highlight the error in the SQL panel', (done) => {
-      setTimeout(
-        () => {
-          expect(sqlEditorMock._highlight_error).not
-            .toHaveBeenCalled();
-          done();
-        }, 0);
-    });
-  };
-
-  let shouldHighlightErrorTestCase = (msg)=>{
-    it('should highlight the error in the SQL panel', (done) => {
-      setTimeout(
-        () => {
-          expect(sqlEditorMock._highlight_error)
-            .toHaveBeenCalledWith(msg);
-          done();
-        }, 0);
-    });
-  };
-
-  let shouldLoginNotDisplayedTestCase = ()=>{
-    it('should login is not displayed', (done) => {
-      pgaLoginNotCalled(done);
-    });
-  };
-
-  let shouldNotDisplayPGALoginTestCase = ()=>{
-    it('should not display pga login', (done) => {
-      pgaLoginNotCalled(done);
-    });
-  };
-
-  let shouldLoginDisplayedTestCase = ()=>{
-    it('should login is displayed', (done) => {
-      setTimeout(
-        () => {
-          expect(userManagementMock.pgaLogin)
-            .toHaveBeenCalled();
-          done();
-        }, 0);
-    });
-  };
-
-  let addNewEntryToHistoryUpdateMsgTestCase = (msg)=> {
-    it('should add new entry to history and update the Messages tab', (done) => {
-      setTimeout(
-        () => {
-          expect(sqlEditorMock.update_msg_history)
-            .toHaveBeenCalledWith(false, msg);
-          done();
-        }, 0);
-    });
-  };
-
-  let shouldNotSaveStateTestCase = ()=>{
-    it('should not save the state', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.saveState).not.toHaveBeenCalled();
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldSaveStateTestCase = ()=> {
-    it('should save the state', (done) => {
-      setTimeout(() => {
-        expect(sqlEditorMock.saveState).toHaveBeenCalledWith(
-          'check_data_changes_to_execute_query',
-          ['']
-        );
-        done();
-      }, 0);
-    });
-  };
-
-  let shouldDisableCancelButtonTestCase = ()=> {
-    it('should disable the cancel button', (done) => {
-      cancelButtonNotCalled(done);
-    });
-  };
-
-  let whenQueryIsStillRunningTestCases = ()=> {
-    describe('when query is still running', () => {
-      context('when no additional information is returned', () => {
-        beforeEach(() => {
-          let response = {
-            data: {status: 'Busy'},
-          };
-
-          networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-          executeQuery.poll();
-        });
-
-        setFlagQueryIsRunningTestCase();
-        shouldNotUpdateHistoryMessageTestCase();
-        callPoolingTestCase();
-      });
-
-      context('when additional information is returned', () => {
-        beforeEach(() => {
-          let response = {
-            data: {
-              status: 'Busy',
-              result: 'Some important result',
-            },
-          };
-
-          networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-          executeQuery.poll();
-        });
-
-        setFlagQueryIsRunningTestCase();
-        updateHistoryMessageTestCase();
-        callPoolingTestCase();
-      });
-    });
-  };
-
-  let whenQueryWasCancelledTestCases = ()=> {
-    describe('when query was cancelled', () => {
-      beforeEach(() => {
-        let response = {
-          data: {status: 'Cancel'},
-        };
-        networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-        executeQuery.poll();
-      });
-
-      shouldHideLoadingIconTestCase();
-      addNewEntryToHistoryExecutionCancelTestCase();
-    });
-  };
-
-  describe('#poll', () => {
-    let response;
-
-    beforeEach(() => {
-      sqlEditorMock.POLL_FALLBACK_TIME = () => {
-        return 0;
-      };
-
-      executeQuery.delayedPoll = jasmine.createSpy('ExecuteQuery.delayedPoll');
-    });
-
-    context('when SQLEditor is the query tool', () => {
-      beforeEach(() => {
-        sqlEditorMock.is_query_tool = true;
-      });
-
-      describe('when server answer with success', () => {
-        describe('when query was successful', () => {
-          beforeEach(() => {
-            response = {
-              data: {status: 'Success', notifies: [{'pid': 100}]},
-            };
-            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-            executeQuery.poll();
-          });
-
-          shouldUpdateLoadingIconMessageTestCase();
-
-          it('should render the results', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.call_render_after_poll)
-                .toHaveBeenCalledWith({status: 'Success', notifies: [{'pid': 100}]});
-              done();
-            }, 0);
-          });
-
-          it('should update the notification panel', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.update_notifications)
-                .toHaveBeenCalledWith([{'pid': 100}]);
-              done();
-            }, 0);
-          });
-        });
-
-        describe('when query was successful but in transaction block', () => {
-          beforeEach(() => {
-            response = {
-              data: {status: 'Success', notifies: [{'pid': 100}], transaction_status: 2},
-            };
-            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-            executeQuery.poll();
-          });
-
-          it('enable the transaction buttons', (done) => {
-            setTimeout(
-              () => {
-                expect(sqlEditorMock.disable_transaction_buttons)
-                  .toHaveBeenCalledWith(false);
-                done();
-              }, 0);
-          });
-        });
-
-        describe('when query was successful but not in transaction block', () => {
-          beforeEach(() => {
-            response = {
-              data: {status: 'Success', notifies: [{'pid': 100}], transaction_status: 0},
-            };
-            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-            executeQuery.poll();
-          });
-
-          it('disable the transaction buttons', (done) => {
-            setTimeout(
-              () => {
-                expect(sqlEditorMock.disable_transaction_buttons)
-                  .toHaveBeenCalledWith(true);
-                done();
-              }, 0);
-          });
-        });
-
-        whenQueryIsStillRunningTestCases();
-
-        describe('when the application lost connection with the database', () => {
-          beforeEach(beforeEachNotConnected);
-
-          shouldHideLoadingIconTestCase();
-          addNewEntryToHistoryTestCase();
-          shouldEnableToolsButtonTestCase();
-        });
-
-        whenQueryWasCancelledTestCases();
-      });
-
-      describe('when an error occur', () => {
-        let errorMessageJson = {
-          errormsg: 'Some error in JSON',
-        };
-        let errorMessageText = 'Some plain text error';
-
-        describe('when the connection to the server was lost', () => {
-          describe('when JSON response is available', () => {
-            describe('when login is not required', () => {
-              beforeEach(() => {
-                userManagementMock.isPgaLoginRequired.and.returnValue(false);
-                response = {responseJSON: errorMessageJson};
-                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
-
-                executeQuery.poll();
-              });
-
-              shouldHideLoadingIconTestCase();
-              shouldResetLastQueryInformationTestCase();
-              shouldHighlightErrorTestCase('Some error in JSON');
-              addNewEntryToHistoryUpdateMsgTestCase('Some error in JSON');
-              shouldEnableToolsButtonTestCase();
-              shouldLoginNotDisplayedTestCase();
-            });
-
-            describe('when login is required', () => {
-              beforeEach(() => {
-                userManagementMock.isPgaLoginRequired.and.returnValue(true);
-                response = {responseJSON: errorMessageJson};
-                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
-
-                executeQuery.poll();
-              });
-
-              shouldHideLoadingIconTestCase();
-              shouldResetLastQueryInformationTestCase();
-              shouldNotHighlightErrorTestCase();
-              shouldNotAddNewEntryTestCase();
-              shouldEnableToolsButtonTestCase();
-              shouldLoginDisplayedTestCase();
-            });
-          });
-
-          describe('when no JSON response is available', () => {
-            describe('when login is not required', () => {
-              beforeEach(() => {
-                userManagementMock.isPgaLoginRequired.and.returnValue(false);
-                response = {
-                  errormsg: errorMessageText,
-                };
-                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
-
-                executeQuery.poll();
-              });
-
-              shouldHideLoadingIconTestCase();
-              shouldResetLastQueryInformationTestCase();
-              shouldHighlightErrorTestCase('Some plain text error');
-              addNewEntryToHistoryUpdateMsgTestCase('Some plain text error');
-              shouldEnableToolsButtonTestCase();
-              shouldLoginNotDisplayedTestCase();
-            });
-
-            describe('when login is required', () => {
-              beforeEach(() => {
-                userManagementMock.isPgaLoginRequired.and.returnValue(true);
-                response = {
-                  errormsg: errorMessageText,
-                };
-                networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, response);
-
-                executeQuery.poll();
-              });
-
-              shouldHideLoadingIconTestCase();
-              shouldResetLastQueryInformationTestCase();
-              shouldNotHighlightErrorTestCase();
-              shouldNotAddNewEntryTestCase();
-              shouldEnableToolsButtonTestCase();
-              shouldLoginDisplayedTestCase();
-
-            });
-          });
-
-          describe('when cannot reach the Python Server', () => {
-            beforeEach(beforeEachPythonServer);
-
-            shouldHideLoadingIconTestCase();
-            shouldResetLastQueryInformationTestCase();
-            shouldNotHighlightErrorTestCase();
-            addNewEntryToHistoryUpdateMsgTestCase('Not connected to the server or the connection to the server has been closed.');
-            shouldEnableToolsButtonTestCase();
-            shouldLoginNotDisplayedTestCase();
-          });
-        });
-      });
-    });
-
-    context('when SQLEditor is NOT the query tool', () => {
-      beforeEach(() => {
-        sqlEditorMock.is_query_tool = false;
-      });
-
-      describe('when server answer with success', () => {
-        describe('when query was successful', () => {
-          beforeEach(() => {
-            response = {
-              data: {status: 'Success'},
-            };
-            networkMock.onGet('/sqleditor/query_tool/poll/123').reply(200, response);
-
-            executeQuery.poll();
-          });
-
-          shouldUpdateLoadingIconMessageTestCase();
-
-          it('should render the results', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.call_render_after_poll)
-                .toHaveBeenCalledWith({status: 'Success'});
-              done();
-            }, 0);
-          });
-        });
-
-        whenQueryIsStillRunningTestCases();
-
-        describe('when the application lost connection with the database', () => {
-          beforeEach(beforeEachNotConnected);
-
-          shouldHideLoadingIconTestCase();
-          addNewEntryToHistoryTestCase();
-
-          it('should NOT enable the tool buttons', (done) => {
-            disableToolButtonsNotCalled(done);
-          });
-
-          it('should NOT disable the cancel button', (done) => {
-            cancelButtonNotCalled(done);
-          });
-        });
-
-        whenQueryWasCancelledTestCases();
-      });
-
-      describe('when an error occur', () => {
-        let errorMessageJson = {
-          errormsg: 'Some error in JSON',
-        };
-        let errorMessageText = 'Some plain text error';
-        let res;
-
-        describe('when the connection to the server was lost', () => {
-          describe('when JSON response is available', () => {
-            beforeEach(() => {
-              res = {responseJSON: errorMessageJson};
-              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, res);
-
-              executeQuery.poll();
-            });
-
-            shouldHideLoadingIconTestCase();
-            shouldResetLastQueryInformationTestCase();
-            shouldHighlightErrorTestCase('Some error in JSON');
-            addNewEntryToHistoryUpdateMsgTestCase('Some error in JSON');
-
-            it('should enable the tool buttons', (done) => {
-              disableToolButtonsNotCalled(done);
-            });
-
-            shouldDisableCancelButtonTestCase();
-          });
-          describe('when no JSON response is available', () => {
-            beforeEach(() => {
-              res = {errormsg: errorMessageText};
-              networkMock.onGet('/sqleditor/query_tool/poll/123').reply(401, res);
-
-              executeQuery.poll();
-            });
-
-            shouldHideLoadingIconTestCase();
-            shouldResetLastQueryInformationTestCase();
-            shouldHighlightErrorTestCase('Some plain text error');
-            addNewEntryToHistoryUpdateMsgTestCase('Some plain text error');
-
-            it('should not enable the tool buttons', (done) => {
-              disableToolButtonsNotCalled(done);
-            });
-
-          });
-
-          describe('when cannot reach the Python Server', () => {
-            beforeEach(beforeEachPythonServer);
-
-            shouldHideLoadingIconTestCase();
-            shouldResetLastQueryInformationTestCase();
-            shouldNotHighlightErrorTestCase();
-            addNewEntryToHistoryUpdateMsgTestCase('Not connected to the server or the connection to the server has been closed.');
-
-            it('should enable the tool buttons', (done) => {
-              disableToolButtonsNotCalled(done);
-            });
-
-            shouldDisableCancelButtonTestCase();
-          });
-        });
-      });
-    });
-  });
-
-  describe('#execute', () => {
-    let response;
-
-    beforeEach(() => {
-      response = {
-        'info': '',
-        'errormsg': '',
-        'data': {
-          'status': true,
-          'can_edit': false,
-          'info_notifier_timeout': 5,
-          'result': '2',
-          'can_filter': false,
-        },
-        'result': null,
-        'success': 1,
-      };
-    });
-
-    context('when the SQL statement is empty', () => {
-      it('should return without executing', (done) => {
-        let wasNetworkCalled = false;
-        networkMock.onAny('/sqleditor/query_tool/start/123').reply(() => {
-          wasNetworkCalled = true;
-        });
-
-        executeQuery.execute('', {});
-
-        setTimeout(() => {
-          expect(wasNetworkCalled).toEqual(false);
-          done();
-        }, 0);
-      });
-    });
-
-    context('when the SQL statement is not empty', () => {
-      let pollSpy;
-
-      beforeEach(() => {
-        sqlEditorMock.gridView = {};
-        sqlEditorMock.gridView.query_tool_obj = jasmine.createSpyObj(
-          'QueryToolObject',
-          ['removeLineClass']
-        );
-
-        $('body').append(
-          '<div id="test-id">' +
-          '<button id="btn-flash" disabled></button>' +
-          '<button id="btn-cancel-query"></button>' +
-          '</div>'
-        );
-      });
-
-      afterEach(function () {
-        $('body').remove('#test-id');
-      });
-
-      describe('before the backend request', () => {
-        beforeEach(() => {
-          jasmine.clock().install();
-          jasmine.clock().mockDate(startTime);
-          jasmine.clock().tick(50);
-          networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
-          pollSpy = spyOn(executeQuery, 'poll');
-          executeQuery.execute('some sql query', '');
-        });
-
-        afterEach(function () {
-          jasmine.clock().uninstall();
-        });
-
-        it('should update loading text to "Running query"', () => {
-          expect(sqlEditorMock.trigger)
-            .toHaveBeenCalledWith(
-              'pgadmin-sqleditor:loading-icon:show',
-              'Running query...'
-            );
-        });
-
-        it('disables the run query button', () => {
-          let buttonFlash = $('#btn-flash');
-
-          expect(buttonFlash.prop('disabled')).toEqual(true);
-        });
-
-        it('enable the cancel query button', () => {
-          let buttonFlash = $('#btn-cancel-query');
-
-          expect(buttonFlash.prop('disabled')).toEqual(false);
-        });
-
-        it('disable the query tool buttons', () => {
-          expect(sqlEditorMock.disable_tool_buttons).toHaveBeenCalledWith(true);
-        });
-
-        it('initializes the polling flags', () => {
-          expect(sqlEditorMock._init_polling_flags).toHaveBeenCalled();
-        });
-
-        it('save the query', () => {
-          expect(sqlEditorMock.query).toEqual('some sql query');
-        });
-
-        it('reset the number of rows that were affected', () => {
-          expect(sqlEditorMock.rows_affected).toEqual(0);
-        });
-
-        it('reset query start time', () => {
-          expect(sqlEditorMock.query_start_time.getTime()).toEqual(startTime.getTime() + 50);
-        });
-      });
-
-      describe('when HTTP return 200', () => {
-        describe('when backend informs that query started successfully', () => {
-          beforeEach(() => {
-            networkMock.onPost('/sqleditor/query_tool/start/123?connect=1').reply(200, response);
-            pollSpy = spyOn(executeQuery, 'delayedPoll');
-            executeQuery.execute('some sql query', '', true);
-          });
-
-          it('should changes the loading message to "Waiting for the query to complete"', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.trigger).toHaveBeenCalledWith(
-                'pgadmin-sqleditor:loading-icon:message',
-                'Waiting for the query to complete...'
-              );
-              done();
-            }, 0);
-          });
-
-          it('should update the can edit flag', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.can_edit).toEqual(false);
-              done();
-            }, 0);
-          });
-
-          it('should update the can filter flag', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.can_filter).toEqual(false);
-              done();
-            }, 0);
-          });
-
-          it('should update information notifier timeout', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.info_notifier_timeout).toEqual(5);
-              done();
-            }, 0);
-          });
-
-          it('should start polling', (done) => {
-            setTimeout(() => {
-              expect(pollSpy).toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-        });
-
-        describe('when explain plan is not empty', () => {
-          it('should send the explain plan informatioon through the wire', (done) => {
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply((config) => {
-              setTimeout(() => {
-                expect(config.data).toEqual(JSON.stringify({
-                  sql: 'some sql query',
-                  explain_plan: {
-                    buffers: true,
-                    analyze: false,
-                    timing: true,
-                    summary: false,
-                  },
-                }));
-
-                done();
-              }, 0);
-              return [200, ''];
-            });
-            pollSpy = spyOn(executeQuery, 'delayedPoll');
-            executeQuery.execute('some sql query', {
-              buffers: true,
-              analyze: false,
-              timing: true,
-              summary: false,
-            });
-          });
-
-
-        });
-
-        describe('when backend informs that there was a problem with the query', () => {
-          beforeEach(() => {
-            response.data.status = false;
-            response.data.result = 'something went wrong';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
-            pollSpy = spyOn(executeQuery, 'poll');
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldHideLoadingIconTestCase();
-          shouldEnableToolsButtonTestCase();
-
-          it('update the history tab with the result message', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.update_msg_history).toHaveBeenCalledWith(false, 'something went wrong');
-              done();
-            }, 0);
-          });
-
-          it('highlight the error in the editor', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock._highlight_error).toHaveBeenCalledWith('something went wrong');
-              done();
-            }, 0);
-          });
-
-          it('should not start polling', (done) => {
-            setTimeout(() => {
-              expect(pollSpy).not.toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-        });
-
-        describe('when there is a marker set in the grid', () => {
-          let markerClearSpy;
-          beforeEach(() => {
-            sqlEditorMock.gridView.marker = jasmine.createSpyObj(
-              'GridViewMarker',
-              ['clear']
-            );
-            markerClearSpy = sqlEditorMock.gridView.marker.clear;
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(200, response);
-            pollSpy = spyOn(executeQuery, 'poll');
-            executeQuery.execute('some sql query', '');
-          });
-
-          it('should call clear function on marker', (done) => {
-            setTimeout(() => {
-              expect(markerClearSpy).toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-
-          it('should removes the marker', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.gridView.marker).toEqual(null);
-              done();
-            }, 0);
-          });
-
-          it('should remove CSS classes from the editor', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.gridView.query_tool_obj.removeLineClass)
-                .toHaveBeenCalledWith(sqlEditorMock.marked_line_no, 'wrap', 'CodeMirror-activeline-background');
-              done();
-            }, 0);
-          });
-        });
-      });
-
-      describe('when cannot reach the Python Server', () => {
-        beforeEach(() => {
-          networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, undefined);
-
-
-          executeQuery.execute('some sql query', '');
-        });
-
-        shouldHideLoadingIconTestCase();
-        shouldNotHighlightErrorTestCase();
-        addNewEntryToHistoryUpdateMsgTestCase('Not connected to the server or the connection to the server has been closed.');
-        shouldEnableToolsButtonTestCase();
-      });
-
-      describe('when error is returned by the server', () => {
-        describe('when login is not required', () => {
-          beforeEach(() => {
-            userManagementMock.isPgaLoginRequired.and.returnValue(false);
-            response.errormsg = 'some error message';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
-
-
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldHideLoadingIconTestCase();
-          shouldNotHighlightErrorTestCase();
-          addNewEntryToHistoryUpdateMsgTestCase('some error message');
-          shouldEnableToolsButtonTestCase();
-          shouldNotSaveStateTestCase();
-          shouldNotDisplayPGALoginTestCase();
-        });
-        describe('when login is required', () => {
-          beforeEach(() => {
-            userManagementMock.isPgaLoginRequired.and.returnValue(true);
-            response.errormsg = 'some error message';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
-
-
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldHideLoadingIconTestCase();
-          shouldNotHighlightErrorTestCase();
-          addNewEntryToHistoryUpdateMsgTestCase('some error message');
-          shouldEnableToolsButtonTestCase();
-          shouldSaveStateTestCase();
-          shouldLoginDisplayedTestCase();
-        });
-        describe('when a new transaction is not required', () => {
-          beforeEach(() => {
-            isNewTransactionRequiredMock.and.returnValue(false);
-            response.errormsg = 'some error message';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
-
-
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldHideLoadingIconTestCase();
-          shouldNotHighlightErrorTestCase();
-          addNewEntryToHistoryUpdateMsgTestCase('some error message');
-          shouldEnableToolsButtonTestCase();
-          shouldNotSaveStateTestCase();
-          shouldNotDisplayPGALoginTestCase();
-
-          it('should not initialize a new transaction', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.initTransaction).not.toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-        });
-
-        describe('when a new transaction is required', () => {
-          beforeEach(() => {
-            isNewTransactionRequiredMock.and.returnValue(true);
-            response.errormsg = 'some error message';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(500, response);
-
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldHideLoadingIconTestCase();
-          shouldNotHighlightErrorTestCase();
-          addNewEntryToHistoryUpdateMsgTestCase('some error message');
-          shouldEnableToolsButtonTestCase();
-          shouldSaveStateTestCase();
-          shouldNotDisplayPGALoginTestCase();
-
-          it('should initialize a new transaction', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.initTransaction).toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-        });
-        describe('when connection to database is lost', () => {
-          beforeEach(() => {
-            isNewTransactionRequiredMock.and.returnValue(false);
-            response.info = 'CONNECTION_LOST';
-            networkMock.onAny('/sqleditor/query_tool/start/123').reply(503, response);
-
-            executeQuery.execute('some sql query', '');
-          });
-
-          shouldSaveStateTestCase();
-
-          it('calls handle_connection_lost', (done) => {
-            setTimeout(() => {
-              expect(sqlEditorMock.handle_connection_lost).toHaveBeenCalled();
-              done();
-            }, 0);
-          });
-        });
-      });
-
-    });
-  });
-});
-
diff --git a/web/regression/javascript/sqleditor/filter_dialog_specs.js b/web/regression/javascript/sqleditor/filter_dialog_specs.js
deleted file mode 100644
index 95abd8837..000000000
--- a/web/regression/javascript/sqleditor/filter_dialog_specs.js
+++ /dev/null
@@ -1,28 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-import filterDialog from 'sources/sqleditor/filter_dialog';
-
-describe('filterDialog', () => {
-  describe('filterDialog', () => {
-    describe('when using filter dialog', () => {
-      beforeEach(() => {
-        spyOn(filterDialog, 'dialog');
-      });
-
-      it('it should be defined as function', function() {
-        expect(filterDialog.dialog).toBeDefined();
-      });
-
-      it('it should call without proper handler', () => {
-        expect(filterDialog.dialog).not.toHaveBeenCalledWith({});
-      });
-
-    });
-  });
-});
diff --git a/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js b/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
deleted file mode 100644
index 67595f215..000000000
--- a/web/regression/javascript/sqleditor/keyboard_shortcuts_spec.js
+++ /dev/null
@@ -1,733 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-import * as keyboardShortcuts from 'sources/keyboard_shortcuts';
-import {queryToolActions} from 'sources/sqleditor/query_tool_actions';
-import gettext from 'sources/gettext';
-
-describe('the keyboard shortcuts', () => {
-  const F1_KEY = 112,
-    F5_KEY = 116,
-    F6_KEY = 117,
-    F7_KEY = 118,
-    F8_KEY = 119,
-    PERIOD_KEY = 190,
-    FWD_SLASH_KEY = 191,
-    C1_KEY = 49;
-
-  let sqlEditorControllerSpy, event, queryToolActionsSpy;
-  beforeEach(() => {
-    event = {
-      shift: false,
-      which: undefined,
-      preventDefault: jasmine.createSpy('preventDefault'),
-      cancelBubble: false,
-      stopPropagation: jasmine.createSpy('stopPropagation'),
-      stopImmediatePropagation: jasmine.createSpy('stopImmediatePropagation'),
-    };
-
-    let gridView = {
-      query_tool_obj: {
-        getSelection: jasmine.createSpy('getSelection'),
-        getValue: jasmine.createSpy('getValue'),
-      },
-    };
-
-    sqlEditorControllerSpy = jasmine.createSpyObj('SqlEditorController', [
-      'isQueryRunning',
-      'execute',
-    ]);
-
-    sqlEditorControllerSpy.gridView = gridView;
-
-    sqlEditorControllerSpy.preferences = {
-      execute_query: {
-        alt: false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: F5_KEY,
-        },
-      },
-      explain_query: {
-        alt: false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: F7_KEY,
-        },
-      },
-      explain_analyze_query: {
-        alt: false,
-        shift: true,
-        control: false,
-        key: {
-          key_code: F7_KEY,
-        },
-      },
-      download_results: {
-        alt: false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: F8_KEY,
-        },
-      },
-      move_next: {
-        alt: false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: null,
-        },
-      },
-      move_previous: {
-        alt: false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: null,
-        },
-      },
-      commit_transaction: {
-        alt: false,
-        shift: true,
-        control: true,
-        key: {
-          key_code: 'm',
-        },
-      },
-      rollback_transaction: {
-        alt: false,
-        shift: true,
-        control: true,
-        key: {
-          key_code: 'r',
-        },
-      },
-      save_data: {
-        alt : false,
-        shift: false,
-        control: false,
-        key: {
-          key_code: F6_KEY,
-        },
-      },
-    };
-
-    sqlEditorControllerSpy.macros = [
-      {
-        alt: false,
-        control: true,
-        id: 1,
-        key: '1',
-        key_code: C1_KEY,
-        key_label: 'Ctrl + 1',
-        name: 'C1',
-        sql: 'Select 1;',
-      },
-    ];
-
-    queryToolActionsSpy = jasmine.createSpyObj(queryToolActions, [
-      'explainAnalyze',
-      'explain',
-      'download',
-      'commentBlockCode',
-      'commentLineCode',
-      'uncommentLineCode',
-      'executeQuery',
-      'executeCommit',
-      'executeRollback',
-      'saveDataChanges',
-      'executeMacro',
-    ]);
-  });
-
-  let doesNothingTestCase = ()=> {
-    it('does nothing', () => {
-      keyboardShortcuts.processEventQueryTool(
-        sqlEditorControllerSpy, queryToolActionsSpy, event
-      );
-
-      expect(queryToolActionsSpy.commentLineCode).not.toHaveBeenCalled();
-    });
-  };
-
-  let beforeEachWindows = ()=> {
-    windowsKeysSetup();
-    event.which = FWD_SLASH_KEY;
-    event.shiftKey = true;
-    keyboardShortcuts.processEventQueryTool(
-      sqlEditorControllerSpy, queryToolActionsSpy, event
-    );
-  };
-
-  let beforeEachMAC = ()=> {
-    macKeysSetup();
-    event.which = FWD_SLASH_KEY;
-    event.shiftKey = true;
-    keyboardShortcuts.processEventQueryTool(
-      sqlEditorControllerSpy, queryToolActionsSpy, event
-    );
-  };
-
-
-  describe('when the key is not handled by the function', function () {
-
-    beforeEach(() => {
-      event.which = F1_KEY;
-      keyboardShortcuts.processEventQueryTool(
-        sqlEditorControllerSpy, queryToolActionsSpy, event
-      );
-    });
-
-    it('should allow event to propagate', () => {
-      expect(event.preventDefault).not.toHaveBeenCalled();
-    });
-  });
-
-  describe('F5', () => {
-    describe('when there is no query already running', () => {
-      beforeEach(() => {
-        event.keyCode = F5_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should execute the query', () => {
-        expect(queryToolActionsSpy.executeQuery).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      it('should stop event propagation', () => {
-        expect(event.preventDefault).toHaveBeenCalled();
-      });
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.keyCode = F5_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.executeQuery).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('F6', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.which = F6_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should save the changed data', () => {
-        expect(queryToolActionsSpy.saveDataChanges).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      expectEventPropagationToStop();
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.keyCode = F6_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.saveDataChanges).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('F7', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.which = F7_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should explain the query plan', () => {
-        expect(queryToolActionsSpy.explain).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      expectEventPropagationToStop();
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.keyCode = F7_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.explain).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('Shift+F7', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.shiftKey = true;
-        event.which = F7_KEY;
-        event.altKey = false;
-        event.ctrlKey = false;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should analyze explain the query plan', () => {
-        expect(queryToolActionsSpy.explainAnalyze).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      expectEventPropagationToStop();
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.shiftKey = true;
-        event.which = F7_KEY;
-        event.altKey = false;
-        event.ctrlKey = false;
-
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.explainAnalyze).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('F8', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.which = F8_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should download the query results as a CSV', () => {
-        expect(queryToolActionsSpy.download).toHaveBeenCalled();
-      });
-
-      it('should stop event propagation', () => {
-        expect(event.preventDefault).toHaveBeenCalled();
-      });
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.keyCode = F8_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = false;
-
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.download).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('inlineComment', () => {
-    describe('when there is not a query already running', () => {
-      describe('and the system is a Mac', () => {
-        beforeEach(() => {
-          macKeysSetup();
-          event.which = FWD_SLASH_KEY;
-          keyboardShortcuts.processEventQueryTool(
-            sqlEditorControllerSpy, queryToolActionsSpy, event
-          );
-        });
-
-        it('should comment the line', () => {
-          expect(queryToolActionsSpy.commentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-
-      describe('and the system is Windows', () => {
-        beforeEach(() => {
-          windowsKeysSetup();
-          event.which = FWD_SLASH_KEY;
-          keyboardShortcuts.processEventQueryTool(
-            sqlEditorControllerSpy, queryToolActionsSpy, event
-          );
-        });
-
-        it('should comment the line', () => {
-          expect(queryToolActionsSpy.commentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-    });
-
-    describe('when the query is already running', () => {
-      beforeEach(() => {
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-      });
-      describe('and the system is a Mac', () => {
-        beforeEach(() => {
-          macKeysSetup();
-          event.which = FWD_SLASH_KEY;
-        });
-
-        doesNothingTestCase();
-      });
-
-      describe('and the system is a Windows', () => {
-        beforeEach(() => {
-          windowsKeysSetup();
-          event.which = FWD_SLASH_KEY;
-        });
-
-        doesNothingTestCase();
-      });
-    });
-  });
-
-  describe('inlineUncomment', () => {
-    describe('when there is not a query already running', () => {
-      describe('and the system is a mac', () => {
-        beforeEach(() => {
-          macKeysSetup();
-          event.which = PERIOD_KEY;
-          keyboardShortcuts.processEventQueryTool(
-            sqlEditorControllerSpy, queryToolActionsSpy, event
-          );
-        });
-
-        it('should uncomment the line', () => {
-          expect(queryToolActionsSpy.uncommentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-      describe('and the system is a windows', () => {
-        beforeEach(() => {
-          windowsKeysSetup();
-          event.which = PERIOD_KEY;
-          keyboardShortcuts.processEventQueryTool(
-            sqlEditorControllerSpy, queryToolActionsSpy, event
-          );
-        });
-
-        it('should uncomment the line', () => {
-          expect(queryToolActionsSpy.uncommentLineCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-    });
-
-    describe('when the query is already running', () => {
-      beforeEach(() => {
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-      });
-      describe('and the system is a Mac', () => {
-        beforeEach(() => {
-          macKeysSetup();
-          event.which = PERIOD_KEY;
-        });
-
-        doesNothingTestCase();
-      });
-      describe('and the system is a Windows', () => {
-        beforeEach(() => {
-          windowsKeysSetup();
-          event.which = PERIOD_KEY;
-        });
-
-        doesNothingTestCase();
-      });
-    });
-  });
-
-  describe('blockComment', () => {
-    describe('when there is not a query already running', () => {
-      describe('and the system is a Mac', () => {
-        beforeEach(beforeEachMAC);
-
-        it('should comment out the block selection', () => {
-          expect(queryToolActionsSpy.commentBlockCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-    });
-
-    describe('when there is not a query already running', () => {
-      describe('and the system is a Windows', () => {
-        beforeEach(beforeEachWindows);
-
-        it('should comment out the block selection', () => {
-          expect(queryToolActionsSpy.commentBlockCode).toHaveBeenCalledWith(sqlEditorControllerSpy);
-        });
-
-        expectEventPropagationToStop();
-      });
-    });
-
-    describe('when there is a query already running', () => {
-      beforeEach(() => {
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-      });
-      describe('and the system is a Mac', () => {
-        beforeEach(beforeEachMAC);
-        it('does nothing', () => {
-          expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();
-        });
-      });
-      describe('and the system is a Windows', () => {
-        beforeEach(beforeEachWindows);
-        it('does nothing', () => {
-          expect(queryToolActionsSpy.commentBlockCode).not.toHaveBeenCalled();
-        });
-      });
-    });
-  });
-
-  describe('shortcut to text converters', ()=> {
-    var shortcut = {
-      alt: false,
-      shift: false,
-      control: false,
-      key: {
-        char: 'a',
-        key_code: 65,
-      },
-    };
-
-    it('shortcut_key',()=>{
-      expect(keyboardShortcuts.shortcut_key(shortcut)).toEqual('A');
-    });
-
-    it('shortcut_accesskey_title',()=>{
-      expect(keyboardShortcuts.shortcut_accesskey_title(
-        'Title', shortcut)).toEqual(gettext('Title (accesskey + A)'));
-    });
-
-    it('shortcut_title',()=>{
-      shortcut.alt = true;
-      shortcut.shift = false;
-      shortcut.control = false;
-      expect(keyboardShortcuts.shortcut_title(
-        'Title', shortcut)).toEqual(gettext('Title (Alt+A)'));
-
-      shortcut.alt = false;
-      shortcut.shift = true;
-      shortcut.control = false;
-      expect(keyboardShortcuts.shortcut_title(
-        'Title', shortcut)).toEqual(gettext('Title (Shift+A)'));
-
-      shortcut.alt = false;
-      shortcut.shift = false;
-      shortcut.control = true;
-      expect(keyboardShortcuts.shortcut_title(
-        'Title', shortcut)).toEqual(gettext('Title (Ctrl+A)'));
-
-      shortcut.alt = true;
-      shortcut.shift = true;
-      shortcut.control = true;
-      expect(keyboardShortcuts.shortcut_title(
-        'Title', shortcut)).toEqual(gettext('Title (Alt+Shift+Ctrl+A)'));
-    });
-  });
-
-  describe('Shift+Ctrl+C', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.shiftKey = true;
-        event.which = 'm';
-        event.altKey = false;
-        event.ctrlKey = true;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should commit the transaction', () => {
-        expect(queryToolActionsSpy.executeCommit).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      expectEventPropagationToStop();
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.shiftKey = true;
-        event.which = 'm';
-        event.altKey = false;
-        event.ctrlKey = true;
-
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.executeCommit).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('Shift+Ctrl+R', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.shiftKey = true;
-        event.which = 'r';
-        event.altKey = false;
-        event.ctrlKey = true;
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should rollback the transaction', () => {
-        expect(queryToolActionsSpy.executeRollback).toHaveBeenCalledWith(sqlEditorControllerSpy);
-      });
-
-      expectEventPropagationToStop();
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.shiftKey = true;
-        event.which = 'r';
-        event.altKey = false;
-        event.ctrlKey = true;
-
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.executeRollback).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  describe('Macro Ctrl + 1', () => {
-    describe('when there is not a query already running', () => {
-      beforeEach(() => {
-        event.which = C1_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = true;
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-      });
-
-      it('should execute the macro', () => {
-        expect(queryToolActionsSpy.executeMacro).toHaveBeenCalledWith(sqlEditorControllerSpy,
-          sqlEditorControllerSpy.macros[0].id);
-      });
-
-      it('should stop event propagation', () => {
-        expect(event.preventDefault).toHaveBeenCalled();
-      });
-    });
-
-    describe('when the query is already running', () => {
-      it('does nothing', () => {
-        event.keyCode = C1_KEY;
-        event.altKey = false;
-        event.shiftKey = false;
-        event.ctrlKey = true;
-
-        sqlEditorControllerSpy.isQueryRunning.and.returnValue(true);
-
-        keyboardShortcuts.processEventQueryTool(
-          sqlEditorControllerSpy, queryToolActionsSpy, event
-        );
-
-        expect(queryToolActionsSpy.executeMacro).not.toHaveBeenCalled();
-      });
-    });
-  });
-
-  function expectEventPropagationToStop() {
-    describe('stops all event propogation', () => {
-
-      it('should cancel the bubble', () => {
-        expect(event.cancelBubble).toEqual(true);
-      });
-
-      it('should prevent the default behavior', () => {
-        expect(event.preventDefault).toHaveBeenCalled();
-      });
-
-      it('should stop event propagation', () => {
-        expect(event.stopPropagation).toHaveBeenCalled();
-        expect(event.stopImmediatePropagation).toHaveBeenCalled();
-      });
-    });
-  }
-
-  function windowsKeysSetup() {
-    spyOn(keyboardShortcuts, 'isMac').and.returnValue(false);
-    event.ctrlKey = true;
-    event.metaKey = false;
-  }
-
-  function macKeysSetup() {
-    spyOn(keyboardShortcuts, 'isMac').and.returnValue(true);
-    event.ctrlKey = false;
-    event.metaKey = true;
-  }
-});
diff --git a/web/regression/javascript/sqleditor/query_tool_actions_spec.js b/web/regression/javascript/sqleditor/query_tool_actions_spec.js
deleted file mode 100644
index 74b82d05d..000000000
--- a/web/regression/javascript/sqleditor/query_tool_actions_spec.js
+++ /dev/null
@@ -1,608 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-import queryToolActions from 'sources/sqleditor/query_tool_actions';
-
-describe('queryToolActions', () => {
-  let sqlEditorController,
-    getSelectionSpy, getValueSpy,
-    selectedQueryString, entireQueryString,
-    replaceSelectionSpy;
-
-  let executeFunctionCall = ()=>{
-    queryToolActions.executeQuery(sqlEditorController);
-    expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalled();
-  };
-
-  let toggleSelectionStringUpperCase = ()=> {
-    it('toggle the selection and string should be in upper case', () => {
-      queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
-      expect(replaceSelectionSpy
-      ).toHaveBeenCalledWith('STRING');
-    });
-  };
-
-  let beforeEachEmptySelection = ()=> {
-    setUpSpies('', 'a string\nddd\nsss');
-
-    sqlEditorController.gridView.query_tool_obj.getCursor = () => {
-      return 3;
-    };
-  };
-
-  describe('executeQuery', () => {
-    describe('when the command is being run from the query tool', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_clearMessageTab');
-      });
-
-      it('clears the html in the message tab', () => {
-        queryToolActions.executeQuery(sqlEditorController);
-
-        expect(queryToolActions._clearMessageTab).toHaveBeenCalled();
-      });
-
-      it('calls the execute function on the sqlEditorController', executeFunctionCall);
-    });
-    describe('when the command is being run from the view data view', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        sqlEditorController.is_query_tool = false;
-      });
-
-      it('it calls the check_data_changes_to_execute_query function on the sqlEditorController', executeFunctionCall);
-    });
-  });
-
-  describe('explainAnalyze', () => {
-    describe('when verbose and costs are not selected and buffers and timing are not selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(false);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
-        spyOn(queryToolActions, '_buffers').and.returnValue(false);
-        spyOn(queryToolActions, '_timing').and.returnValue(false);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-
-      it('calls the execute function', () => {
-        queryToolActions.explainAnalyze(sqlEditorController);
-
-        const explainObject = {
-          format: 'json',
-          analyze: true,
-          verbose: false,
-          costs: false,
-          buffers: false,
-          timing: false,
-          summary: false,
-          settings: false,
-        };
-
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when all options are selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(true);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
-        spyOn(queryToolActions, '_buffers').and.returnValue(true);
-        spyOn(queryToolActions, '_timing').and.returnValue(true);
-        spyOn(queryToolActions, '_summary').and.returnValue(true);
-        spyOn(queryToolActions, '_settings').and.returnValue(true);
-      });
-      it('calls the execute function', () => {
-        queryToolActions.explainAnalyze(sqlEditorController);
-        const explainObject = {
-          format: 'json',
-          analyze: true,
-          verbose: true,
-          costs: true,
-          buffers: true,
-          timing: true,
-          summary: true,
-          settings: true,
-        };
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when verbose is selected and costs is not selected and buffer is selected and timing is not selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(true);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
-        spyOn(queryToolActions, '_buffers').and.returnValue(true);
-        spyOn(queryToolActions, '_timing').and.returnValue(false);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-      it('calls the execute function', () => {
-        queryToolActions.explainAnalyze(sqlEditorController);
-
-        const explainObject = {
-          format: 'json',
-          analyze: true,
-          verbose: true,
-          costs: false,
-          buffers: true,
-          timing: false,
-          summary: false,
-          settings: false,
-        };
-
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when verbose is  not selected and costs is selected and buffer is not selected and timing is  selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(false);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
-        spyOn(queryToolActions, '_buffers').and.returnValue(false);
-        spyOn(queryToolActions, '_timing').and.returnValue(true);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-      it('calls the execute function', () => {
-        queryToolActions.explainAnalyze(sqlEditorController);
-
-        const explainObject = {
-          format: 'json',
-          analyze: true,
-          verbose: false,
-          costs: true,
-          buffers: false,
-          timing: true,
-          summary: false,
-          settings: false,
-        };
-
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when all are not selected except summary and settings', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(false);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
-        spyOn(queryToolActions, '_buffers').and.returnValue(false);
-        spyOn(queryToolActions, '_timing').and.returnValue(false);
-        spyOn(queryToolActions, '_summary').and.returnValue(true);
-        spyOn(queryToolActions, '_settings').and.returnValue(true);
-      });
-      it('calls the execute function', () => {
-        queryToolActions.explainAnalyze(sqlEditorController);
-
-        const explainObject = {
-          format: 'json',
-          analyze: true,
-          verbose: false,
-          costs: false,
-          buffers: false,
-          timing: false,
-          summary: true,
-          settings: true,
-        };
-
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-  });
-
-  describe('explain', () => {
-    describe('when verbose and costs are selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(true);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(true);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-
-      it('calls the execute function', () => {
-        queryToolActions.explain(sqlEditorController);
-        const explainObject = {
-          format: 'json',
-          analyze: false,
-          verbose: true,
-          costs: true,
-          buffers: false,
-          timing: false,
-          summary: false,
-          settings: false,
-        };
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when verbose and costs are not selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(false);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-
-      it('calls the execute function', () => {
-        queryToolActions.explain(sqlEditorController);
-        const explainObject = {
-          format: 'json',
-          analyze: false,
-          verbose: false,
-          costs: false,
-          buffers: false,
-          timing: false,
-          summary: false,
-          settings: false,
-        };
-
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-
-    describe('when verbose is selected and costs is not selected', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-        spyOn(queryToolActions, '_verbose').and.returnValue(true);
-        spyOn(queryToolActions, '_costsEnabled').and.returnValue(false);
-        spyOn(queryToolActions, '_summary').and.returnValue(false);
-        spyOn(queryToolActions, '_settings').and.returnValue(false);
-      });
-
-      it('calls the execute function', () => {
-        queryToolActions.explain(sqlEditorController);
-        const explainObject = {
-          format: 'json',
-          analyze: false,
-          verbose: true,
-          costs: false,
-          buffers: false,
-          timing: false,
-          summary: false,
-          settings: false,
-        };
-        expect(sqlEditorController.check_data_changes_to_execute_query).toHaveBeenCalledWith(explainObject);
-      });
-    });
-  });
-
-  describe('download', () => {
-    describe('when the query is empty', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-      it('does nothing', () => {
-        queryToolActions.download(sqlEditorController);
-
-        expect(sqlEditorController.trigger_csv_download).toHaveBeenCalled();
-      });
-    });
-
-    describe('when the table was opened through the queryTool', () => {
-      let time;
-      let spyCall = ()=> {
-        time = 'rightNow';
-        spyOn(window, 'Date').and.callFake(() => ({
-          getTime: () => {
-            return time;
-          },
-        }));
-      };
-
-      let callCSVDownload = ()=> {
-        let filename = 'data-' + time + '.csv';
-
-        queryToolActions.download(sqlEditorController);
-
-        expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(filename);
-      };
-
-      describe('when the query tool object has a selection', () => {
-        beforeEach(() => {
-          entireQueryString = 'include some more of that yummy string cheese;';
-          selectedQueryString = 'some silly string cheese';
-          setUpSpies(selectedQueryString, entireQueryString);
-
-          spyCall();
-        });
-
-        it('calls trigger_csv_download with filename having .csv extension', callCSVDownload);
-
-        it('calls trigger_csv_download filename having .txt extension', () => {
-          sqlEditorController.preferences.csv_field_separator = ';';
-          let filename = 'data-' + time + '.txt';
-
-          queryToolActions.download(sqlEditorController);
-
-          expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith(filename);
-        });
-      });
-
-      describe('when there is no selection', () => {
-        beforeEach(() => {
-          selectedQueryString = '';
-          entireQueryString = 'include some more of that yummy string cheese;';
-          setUpSpies(selectedQueryString, entireQueryString);
-          spyCall();
-        });
-
-        it('calls trigger_csv_download with filename', callCSVDownload);
-      });
-    });
-
-    describe('when the table was opened through tables, view all data', () => {
-      it('calls trigger_csv_download with the sqlQuery and the table name', () => {
-        let query = 'a very long query';
-        setUpSpies('', query);
-        sqlEditorController.is_query_tool = false;
-
-        queryToolActions.download(sqlEditorController);
-
-        expect(sqlEditorController.trigger_csv_download).toHaveBeenCalledWith('iAmATable' + '.csv');
-      });
-    });
-
-  });
-
-  describe('commentBlockCode', () => {
-    describe('when there is no query text', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-      it('does nothing', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentBlockCode(sqlEditorController);
-
-        expect(codeMirrorObj.toggleComment).not.toHaveBeenCalled();
-      });
-    });
-
-    describe('when there is empty selection', () => {
-      beforeEach(beforeEachEmptySelection);
-
-      it('comments the current line', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentBlockCode(sqlEditorController);
-
-        expect(codeMirrorObj.toggleComment).toHaveBeenCalledWith(3, 3);
-      });
-    });
-
-    describe('when some part of the query is selected', () => {
-      beforeEach(() => {
-        setUpSpies('a string\nddd', 'a string\nddd\nsss');
-      });
-
-      it('comments the selection', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentBlockCode(sqlEditorController);
-
-        expect(codeMirrorObj.toggleComment).toHaveBeenCalledWith(0, 12);
-      });
-    });
-  });
-
-  describe('commentLineCode', () => {
-    describe('when there is no query text', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-      it('does nothing', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.lineComment).not.toHaveBeenCalled();
-      });
-    });
-
-    describe('when there is empty selection', () => {
-      beforeEach(beforeEachEmptySelection);
-
-      it('comments the current line', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.lineComment).toHaveBeenCalledWith(3, 3, {lineComment: '--'});
-      });
-    });
-
-    describe('when some part of the query is selected', () => {
-      beforeEach(() => {
-        setUpSpies('tring\nddd', 'a string\nddd\nsss');
-      });
-
-      it('comments the selection', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.commentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.lineComment).toHaveBeenCalledWith(3, 12, {lineComment: '--'});
-      });
-    });
-  });
-
-  describe('uncommentLineCode', () => {
-    describe('when there is no query text', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-      it('does nothing', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.uncommentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.uncomment).not.toHaveBeenCalled();
-      });
-    });
-
-    describe('when there is empty selection', () => {
-      beforeEach(beforeEachEmptySelection);
-
-      it('uncomments the current line', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.uncommentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.uncomment).toHaveBeenCalledWith(3, 3, {lineComment: '--'});
-      });
-    });
-
-    describe('when some part of the query is selected', () => {
-      beforeEach(() => {
-        setUpSpies('tring\nddd', 'a string\nddd\nsss');
-      });
-
-      it('uncomments the selection', () => {
-        let codeMirrorObj = sqlEditorController.gridView.query_tool_obj;
-
-        queryToolActions.uncommentLineCode(sqlEditorController);
-
-        expect(codeMirrorObj.uncomment).toHaveBeenCalledWith(3, 12, {lineComment: '--'});
-      });
-    });
-  });
-
-  describe('toggleCaseOfSelectedText', () => {
-
-    let doesNothingTestCase = ()=> {
-      it('does nothing', () => {
-        expect(
-          queryToolActions.toggleCaseOfSelectedText(sqlEditorController)
-        ).not.toBeDefined();
-      });
-    };
-
-    describe('when there is no query text', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-      doesNothingTestCase();
-    });
-
-    describe('when there is empty selection', () => {
-      beforeEach(beforeEachEmptySelection);
-
-      doesNothingTestCase();
-    });
-
-    describe('when selected query is in lower case', () => {
-      beforeEach(() => {
-        setUpSpies('string', 'a string\nddd\nsss');
-      });
-
-      toggleSelectionStringUpperCase();
-
-      it('(negative scenario toggle the selection and string should be in upper case', () => {
-        queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
-        expect(replaceSelectionSpy
-        ).not.toHaveBeenCalledWith('string');
-      });
-    });
-
-    describe('when selected query is in upper case', () => {
-      beforeEach(() => {
-        setUpSpies('STRING', 'a string\nddd\nsss');
-      });
-
-      it('toggle the selection and string should be in lower case', () => {
-        queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
-        expect(replaceSelectionSpy
-        ).toHaveBeenCalledWith('string');
-      });
-
-      it('(negative scenario toggle the selection and string should be in lower case', () => {
-        queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
-        expect(replaceSelectionSpy
-        ).not.toHaveBeenCalledWith('STRING');
-      });
-    });
-
-    describe('when selected query is in mixed case', () => {
-      beforeEach(() => {
-        setUpSpies('sTRIng', 'a string\nddd\nsss');
-      });
-
-      toggleSelectionStringUpperCase();
-
-      it('(negative scenario toggle the selection and string should be in upper case', () => {
-        queryToolActions.toggleCaseOfSelectedText(sqlEditorController);
-        expect(replaceSelectionSpy
-        ).not.toHaveBeenCalledWith('sTRIng');
-      });
-    });
-  });
-
-  describe('executeCommit', () => {
-    describe('when commit action is being run from the query tool', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-
-      it('calls the execute commit function', () => {
-        queryToolActions.executeCommit(sqlEditorController);
-        expect(sqlEditorController.special_sql).toEqual('COMMIT;');
-      });
-    });
-  });
-
-  describe('executeRollback', () => {
-    describe('when rollback action is being run from the query tool', () => {
-      beforeEach(() => {
-        setUpSpies('', '');
-      });
-
-      it('calls the execute rollback function', () => {
-        queryToolActions.executeRollback(sqlEditorController);
-        expect(sqlEditorController.special_sql).toEqual('ROLLBACK;');
-      });
-    });
-  });
-
-  function setUpSpies(selectedQueryStringArg, entireQueryStringArg) {
-    getValueSpy = jasmine.createSpy('getValueSpy').and.returnValue(entireQueryStringArg);
-    getSelectionSpy = jasmine.createSpy('getSelectionSpy').and.returnValue(selectedQueryStringArg);
-    replaceSelectionSpy = jasmine.createSpy('replaceSelectionSpy');
-
-    sqlEditorController = {
-      gridView: {
-        query_tool_obj: {
-          getSelection: getSelectionSpy,
-          getValue: getValueSpy,
-          toggleComment: jasmine.createSpy('toggleCommentSpy'),
-          lineComment: jasmine.createSpy('lineCommentSpy'),
-          uncomment: jasmine.createSpy('uncommentSpy'),
-          replaceSelection: replaceSelectionSpy,
-          getCursor: (isFrom) => {
-            return entireQueryStringArg.indexOf(selectedQueryStringArg) + (isFrom ? 0 : selectedQueryStringArg.length);
-          },
-        },
-      },
-      trigger_csv_download: jasmine.createSpy('trigger_csv_download'),
-      trigger: jasmine.createSpy('trigger'),
-      table_name: 'iAmATable',
-      is_query_tool: true,
-      check_data_changes_to_execute_query: jasmine.createSpy('check_data_changes_to_execute_query'),
-      preferences: {
-        csv_field_separator: ',',
-      },
-    };
-  }
-});
diff --git a/web/regression/javascript/sqleditor/query_tool_http_error_handler_spec.js b/web/regression/javascript/sqleditor/query_tool_http_error_handler_spec.js
deleted file mode 100644
index ea15efaed..000000000
--- a/web/regression/javascript/sqleditor/query_tool_http_error_handler_spec.js
+++ /dev/null
@@ -1,191 +0,0 @@
-//////////////////////////////////////////////////////////////////////////
-//
-// pgAdmin 4 - PostgreSQL Tools
-//
-// Copyright (C) 2013 - 2022, The pgAdmin Development Team
-// This software is released under the PostgreSQL Licence
-//
-//////////////////////////////////////////////////////////////////////////
-
-import {
-  httpResponseRequiresNewTransaction,
-  handleQueryToolAjaxError,
-} from '../../../pgadmin/static/js/sqleditor/query_tool_http_error_handler';
-
-describe('#httpResponseRequiresNewTransaction', () => {
-  describe('when status is not 404', () => {
-    it('should return false', () => {
-      expect(httpResponseRequiresNewTransaction({
-        status: 300,
-      })).toEqual(false);
-    });
-  });
-
-  describe('when status is 404', () => {
-    describe('when data is not present', () => {
-      it('should return false', () => {
-        expect(httpResponseRequiresNewTransaction({
-          status: 404,
-        })).toBeFalsy();
-      });
-    });
-
-    describe('when data is present', () => {
-      describe('when info is not present inside data', () => {
-        it('should return false', () => {
-          expect(httpResponseRequiresNewTransaction({
-            status: 404,
-            data: {},
-          })).toBeFalsy();
-        });
-      });
-
-      describe('when info is present inside data', () => {
-        describe('when info value is not "DATAGRID_TRANSACTION_REQUIRED"', () => {
-          it('should return false', () => {
-            expect(httpResponseRequiresNewTransaction({
-              status: 404,
-              data: {
-                info: 'some information',
-              },
-            })).toEqual(false);
-          });
-        });
-
-        describe('when info value is "DATAGRID_TRANSACTION_REQUIRED"', () => {
-          it('should return false', () => {
-            expect(httpResponseRequiresNewTransaction({
-              status: 404,
-              data: {
-                info: 'DATAGRID_TRANSACTION_REQUIRED',
-              },
-            })).toEqual(true);
-          });
-        });
-      });
-    });
-  });
-});
-
-
-describe('#handleQueryToolAjaxError', () => {
-  let sqlEditorHandler,
-    exceptionSpy, stateToSave,
-    stateParameters, checkTransaction,
-    pgBrowserMock;
-
-  beforeEach(() => {
-    stateToSave = 'testState';
-    stateParameters = [];
-    checkTransaction = false;
-    sqlEditorHandler = jasmine.createSpyObj(
-      'handler', ['initTransaction', 'saveState', 'handle_connection_lost']
-    );
-    exceptionSpy = {
-      readyState: 0,
-      status: 404,
-      data: {
-        info: 'CONNECTION_LOST',
-      },
-    };
-    pgBrowserMock = {
-      'Browser': {
-        'UserManagement': jasmine.createSpyObj('UserManagement', ['isPgaLoginRequired', 'pgaLogin']),
-      },
-    };
-  });
-
-  describe('when ready state is 0', () => {
-    it('should return connection', () => {
-      expect(
-        handleQueryToolAjaxError(
-          pgBrowserMock, sqlEditorHandler, exceptionSpy, stateToSave,
-          stateParameters, checkTransaction
-        )
-      ).toEqual('Not connected to the server or the connection to the server has been closed.');
-    });
-  });
-
-  describe('when there is an ajax error due to login is required', () => {
-    beforeEach(() => {
-      exceptionSpy.readyState = 1;
-      exceptionSpy.status = 401;
-      exceptionSpy.data.info = 'PGADMIN_LOGIN_REQUIRED';
-      pgBrowserMock.Browser.UserManagement.isPgaLoginRequired.and.returnValue(true);
-    });
-
-    it('should save the current state and call login handler', () => {
-      handleQueryToolAjaxError(
-        pgBrowserMock, sqlEditorHandler, exceptionSpy, stateToSave,
-        stateParameters, checkTransaction
-      );
-      expect(sqlEditorHandler.saveState).toHaveBeenCalledWith(stateToSave, stateParameters);
-      expect(pgBrowserMock.Browser.UserManagement.pgaLogin).toHaveBeenCalled();
-    });
-  });
-
-  describe('when there is an ajax error and new transaction initialization required', () => {
-    beforeEach(() => {
-      exceptionSpy.readyState = 1;
-      exceptionSpy.status = 404;
-      exceptionSpy.data.info = 'DATAGRID_TRANSACTION_REQUIRED';
-      pgBrowserMock.Browser.UserManagement.isPgaLoginRequired.and.returnValue(false);
-      checkTransaction = true;
-    });
-
-    it('should save the current state and call login handler', () => {
-      handleQueryToolAjaxError(
-        pgBrowserMock, sqlEditorHandler, exceptionSpy, stateToSave,
-        stateParameters, checkTransaction
-      );
-      expect(pgBrowserMock.Browser.UserManagement.pgaLogin).not.toHaveBeenCalled();
-      expect(sqlEditorHandler.saveState).toHaveBeenCalledWith(stateToSave, stateParameters);
-      expect(sqlEditorHandler.initTransaction).toHaveBeenCalled();
-    });
-  });
-
-  describe('when there is an ajax error due to database connection has been lost', () => {
-    beforeEach(() => {
-      exceptionSpy.readyState = 1;
-      exceptionSpy.status = 503;
-      exceptionSpy.responseJSON = {
-        'info': 'CONNECTION_LOST',
-      };
-      pgBrowserMock.Browser.UserManagement.isPgaLoginRequired.and.returnValue(false);
-      checkTransaction = false;
-    });
-
-    it('should save the current state and call connection lost handler', (done) => {
-      handleQueryToolAjaxError(
-        pgBrowserMock, sqlEditorHandler, exceptionSpy, stateToSave,
-        stateParameters, checkTransaction
-      );
-      expect(pgBrowserMock.Browser.UserManagement.pgaLogin).not.toHaveBeenCalled();
-      setTimeout(() => {
-        expect(sqlEditorHandler.saveState).toHaveBeenCalledWith(stateToSave, stateParameters);
-        expect(sqlEditorHandler.handle_connection_lost).toHaveBeenCalledWith(false, exceptionSpy);
-        done();
-      }, 0);
-    });
-  });
-
-  describe('when there is an ajax error due to unknown reason', () => {
-    beforeEach(() => {
-      exceptionSpy.readyState = 1;
-      exceptionSpy.status = 803;
-      exceptionSpy.responseText = 'ajax failed with unknown reason';
-      pgBrowserMock.Browser.UserManagement.isPgaLoginRequired.and.returnValue(false);
-      checkTransaction = false;
-    });
-
-    it('should return proper error message from ajax exception', () => {
-      expect(
-        handleQueryToolAjaxError(
-          pgBrowserMock, sqlEditorHandler, exceptionSpy, stateToSave,
-          stateParameters, checkTransaction
-        )
-      ).toEqual('ajax failed with unknown reason');
-    });
-  });
-
-});
diff --git a/web/regression/javascript/sqleditor_utils_spec.js b/web/regression/javascript/sqleditor_utils_spec.js
index 29e9855ea..886625769 100644
--- a/web/regression/javascript/sqleditor_utils_spec.js
+++ b/web/regression/javascript/sqleditor_utils_spec.js
@@ -10,25 +10,6 @@
 define(['sources/sqleditor_utils'],
   function (SqlEditorUtils) {
     describe('SqlEditorUtils', function () {
-
-      describe('Generate a random string of size 10', function () {
-        it('returns string of length 10', function () {
-          expect(SqlEditorUtils.epicRandomString(10).length).toEqual(10);
-        });
-      });
-
-      describe('Generate a unique hash for given string', function () {
-        it('returns unique hash', function () {
-          expect(SqlEditorUtils.getHash('select * from test')).toEqual(403379630);
-        });
-      });
-
-      describe('Capitalize the first letter of given string', function () {
-        it('returns string with First letter Capital', function () {
-          expect(SqlEditorUtils.capitalizeFirstLetter('create script')).toEqual('Create script');
-        });
-      });
-
       describe('Calculate font size of input number passed', function () {
         it('calcFontSize', function () {
           expect(SqlEditorUtils.calcFontSize(1.456)).toEqual('1.46em');
diff --git a/web/webpack.config.js b/web/webpack.config.js
index ed9ad4472..66e39f500 100644
--- a/web/webpack.config.js
+++ b/web/webpack.config.js
@@ -374,7 +374,7 @@ module.exports = [{
     'app.bundle': sourceDir + '/bundle/app.js',
     codemirror: sourceDir + '/bundle/codemirror.js',
     slickgrid: sourceDir + '/bundle/slickgrid.js',
-    sqleditor: './pgadmin/tools/sqleditor/static/js/sqleditor.js',
+    sqleditor: './pgadmin/tools/sqleditor/static/js/index.js',
     debugger_direct: './pgadmin/tools/debugger/static/js/direct.js',
     schema_diff: './pgadmin/tools/schema_diff/static/js/schema_diff_hook.js',
     erd_tool: './pgadmin/tools/erd/static/js/erd_tool_hook.js',
@@ -463,7 +463,7 @@ module.exports = [{
       // var jQuery = require('jquery'); var browser = require('pgadmin.browser')
       // It solves number of problems
       // Ref: http:/github.com/webpack-contrib/imports-loader/
-      test: require.resolve('./pgadmin/tools/datagrid/static/js/datagrid'),
+      test: require.resolve('./pgadmin/tools/sqleditor/static/js/index'),
       use: {
         loader: 'imports-loader',
         options: {
@@ -564,6 +564,7 @@ module.exports = [{
             'pure|pgadmin.tools.search_objects',
             'pure|pgadmin.tools.erd_module',
             'pure|pgadmin.tools.psql_module',
+            'pure|pgadmin.tools.sqleditor',
             'pure|pgadmin.misc.cloud',
           ],
         },
diff --git a/web/webpack.shim.js b/web/webpack.shim.js
index f9af1531f..27096081a 100644
--- a/web/webpack.shim.js
+++ b/web/webpack.shim.js
@@ -215,7 +215,6 @@ var webpackShimConfig = {
     'pgadmin.browser.utils': '/browser/js/utils',
     'pgadmin.browser.wizard': path.join(__dirname, './pgadmin/browser/static/js/wizard'),
     'pgadmin.dashboard': path.join(__dirname, './pgadmin/dashboard/static/js/Dashboard'),
-    'pgadmin.datagrid': path.join(__dirname, './pgadmin/tools/datagrid/static/js/datagrid'),
     'pgadmin.file_manager': path.join(__dirname, './pgadmin/misc/file_manager/static/js/file_manager'),
     'pgadmin.file_utility': path.join(__dirname, './pgadmin/misc/file_manager/static/js/utility'),
     'pgadmin.help': path.join(__dirname, './pgadmin/help/static/js/help'),
@@ -284,7 +283,6 @@ var webpackShimConfig = {
     'pgadmin.preferences': path.join(__dirname, './pgadmin/preferences/static/js/'),
     'pgadmin.settings': path.join(__dirname, './pgadmin/settings/static/js/settings'),
     'pgadmin.server.supported_servers': '/browser/server/supported_servers',
-    'pgadmin.sqleditor': path.join(__dirname, './pgadmin/tools/sqleditor/static/js/sqleditor'),
     'pgadmin.tables.js': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/'),
     'pgadmin.tools.backup': path.join(__dirname, './pgadmin/tools/backup/static/js/backup'),
     'pgadmin.tools.debugger.controller': path.join(__dirname, './pgadmin/tools/debugger/static/js/debugger'),
@@ -304,7 +302,7 @@ var webpackShimConfig = {
     'pgadmin.tools.erd': path.join(__dirname, './pgadmin/tools/erd/static/js'),
     'pgadmin.tools.psql_module': path.join(__dirname, './pgadmin/tools/psql/static/js/psql_module'),
     'pgadmin.tools.psql': path.join(__dirname, './pgadmin/tools/psql/static/js'),
-    'pgadmin.tools.query_tool': path.join(__dirname, './pgadmin/tools/query_tool/static/js'),
+    'pgadmin.tools.sqleditor': path.join(__dirname, './pgadmin/tools/sqleditor/static/js'),
     'pgadmin.search_objects': path.join(__dirname, './pgadmin/tools/search_objects/static/js'),
     'pgadmin.tools.user_management': path.join(__dirname, './pgadmin/tools/user_management/static/js/user_management'),
     'pgadmin.user_management.current_user': '/user_management/current_user',
diff --git a/web/webpack.test.config.js b/web/webpack.test.config.js
index c36d463b9..197395d99 100644
--- a/web/webpack.test.config.js
+++ b/web/webpack.test.config.js
@@ -185,6 +185,8 @@ module.exports = {
       'backform': path.join(__dirname, './node_modules/backform/src/backform'),
       'backgrid': path.join(__dirname, './pgadmin/static/vendor/backgrid/backgrid'),
       'backgrid.filter': path.join(__dirname, './node_modules/backgrid-filter/backgrid-filter'),
+      'react': path.join(__dirname, 'node_modules/react'),
+      'react-dom': path.join(__dirname, 'node_modules/react-dom'),
       'sources': sourcesDir + '/js',
       'translations': regressionDir + '/javascript/fake_translations',
       'pgadmin.browser.messages': regressionDir + '/javascript/fake_messages',
@@ -194,6 +196,7 @@ module.exports = {
       'slickgrid.plugins': nodeModulesDir + '/slickgrid/plugins/',
       'slickgrid.grid': nodeModulesDir + '/slickgrid/slick.grid',
       'moment': path.join(__dirname, './node_modules/moment/moment'),
+      'jsoneditor.min': path.join(__dirname, './node_modules/jsoneditor/dist/jsoneditor.min'),
       'browser': path.resolve(__dirname, 'pgadmin/browser/static/js'),
       'pgadmin': sourcesDir + '/js/pgadmin',
       'pgadmin.sqlfoldcode': sourcesDir + '/js/codemirror/addon/fold/pgadmin-sqlfoldcode',
@@ -208,6 +211,8 @@ module.exports = {
       'pgadmin.browser.activity': path.join(__dirname, './pgadmin/browser/static/js/activity'),
       'pgadmin.tools.erd': path.join(__dirname, './pgadmin/tools/erd/static/js'),
       'pgadmin.tools.psql': path.join(__dirname, './pgadmin/tools/psql/static/js'),
+      'pgadmin.tools.sqleditor': path.join(__dirname, './pgadmin/tools/sqleditor/static/js'),
+      'pgadmin.authenticate.kerberos': path.join(__dirname, './pgadmin/authenticate/static/js/kerberos'),
       'bundled_codemirror': path.join(__dirname, './pgadmin/static/bundle/codemirror'),
       'tools': path.join(__dirname, './pgadmin/tools/'),
       'pgadmin.user_management.current_user': regressionDir + '/javascript/fake_current_user',
diff --git a/web/yarn.lock b/web/yarn.lock
index bfb47913f..3868618e6 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -31,6 +31,13 @@
   dependencies:
     "@babel/highlight" "^7.14.5"
 
+"@babel/code-frame@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431"
+  integrity sha512-IF4EOMEV+bfYwOmNxGzSnjR2EmQod7f1UXOpZM3l4i4o4QNwzjtJAu/HxdjHq0aYBvdqMuQEY1eg0nqW9ZPORA==
+  dependencies:
+    "@babel/highlight" "^7.16.0"
+
 "@babel/code-frame@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
@@ -38,12 +45,7 @@
   dependencies:
     "@babel/highlight" "^7.16.7"
 
-"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7":
-  version "7.14.7"
-  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08"
-  integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw==
-
-"@babel/compat-data@^7.13.11", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8":
+"@babel/compat-data@^7.13.0", "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.13.8", "@babel/compat-data@^7.14.5", "@babel/compat-data@^7.14.7", "@babel/compat-data@^7.16.4", "@babel/compat-data@^7.16.8":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.0.tgz#86850b8597ea6962089770952075dcaabb8dba34"
   integrity sha512-392byTlpGWXMv4FbyWw3sAZ/FrW/DrwqLGXpy0mbyNe9Taqv1mg9yON5/o0cnr8XYCkFTZbC1eV+c+LAROgrng==
@@ -107,16 +109,7 @@
   dependencies:
     eslint-rule-composer "^0.3.0"
 
-"@babel/generator@^7.13.0", "@babel/generator@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.5.tgz#848d7b9f031caca9d0cd0af01b063f226f52d785"
-  integrity sha512-y3rlP+/G25OIX3mYKKIOlQRcqj7YgrvHxOLbVmyLJ9bPmi5ttvUmpydVjcFjZphOktWuA7ovbx91ECloWTfjIA==
-  dependencies:
-    "@babel/types" "^7.14.5"
-    jsesc "^2.5.1"
-    source-map "^0.5.0"
-
-"@babel/generator@^7.17.0":
+"@babel/generator@^7.13.0", "@babel/generator@^7.14.5", "@babel/generator@^7.17.0":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.17.0.tgz#7bd890ba706cd86d3e2f727322346ffdbf98f65e"
   integrity sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==
@@ -125,12 +118,21 @@
     jsesc "^2.5.1"
     source-map "^0.5.0"
 
-"@babel/helper-annotate-as-pure@^7.0.0", "@babel/helper-annotate-as-pure@^7.10.4", "@babel/helper-annotate-as-pure@^7.12.13", "@babel/helper-annotate-as-pure@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
-  integrity sha512-EivH9EgBIb+G8ij1B2jAwSH36WnGvkQSEC6CkX/6v6ZFlw5fVOHvsgGF4uiEHO2GzMvunZb6tDLQEQSdrdocrA==
+"@babel/generator@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2"
+  integrity sha512-RR8hUCfRQn9j9RPKEVXo9LiwoxLPYn6hNZlvUOR8tSnaxlD0p0+la00ZP9/SnRt6HchKr+X0fO2r8vrETiJGew==
   dependencies:
-    "@babel/types" "^7.14.5"
+    "@babel/types" "^7.16.0"
+    jsesc "^2.5.1"
+    source-map "^0.5.0"
+
+"@babel/helper-annotate-as-pure@^7.15.4", "@babel/helper-annotate-as-pure@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.0.tgz#9a1f0ebcda53d9a2d00108c4ceace6a5d5f1f08d"
+  integrity sha512-ItmYF9vR4zA8cByDocY05o0LGUkp1zhbTQOH1NFyl5xXEqlTJQCEJjieriw+aFpxo16swMxUnUiKS7a/r4vtHg==
+  dependencies:
+    "@babel/types" "^7.16.0"
 
 "@babel/helper-annotate-as-pure@^7.16.7":
   version "7.16.7"
@@ -139,14 +141,6 @@
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz#6bc20361c88b0a74d05137a65cac8d3cbf6f61fc"
-  integrity sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA==
-  dependencies:
-    "@babel/helper-explode-assignable-expression" "^7.12.13"
-    "@babel/types" "^7.12.13"
-
 "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b"
@@ -155,7 +149,7 @@
     "@babel/helper-explode-assignable-expression" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.8", "@babel/helper-compilation-targets@^7.14.5":
+"@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.13.8":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.14.5.tgz#7a99c5d0967911e972fe2c3411f7d5b498498ecf"
   integrity sha512-v+QtZqXEiOnpO6EYvlImB6zCD2Lel06RzOPzmkz/D/XgQiUu3C/Jb1LOqSt/AIA34TYi/Q+KlT8vTQrgdxkbLw==
@@ -165,7 +159,7 @@
     browserslist "^4.16.6"
     semver "^6.3.0"
 
-"@babel/helper-compilation-targets@^7.16.7":
+"@babel/helper-compilation-targets@^7.14.5", "@babel/helper-compilation-targets@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.16.7.tgz#06e66c5f299601e6c7da350049315e83209d551b"
   integrity sha512-mGojBwIWcwGD6rfqgRXVlVYmPAv7eOpIemUG3dGnDdCY4Pae70ROij3XmfrH6Fa1h1aiDylpglbZyktfzyo/hA==
@@ -175,17 +169,30 @@
     browserslist "^4.17.5"
     semver "^6.3.0"
 
-"@babel/helper-create-class-features-plugin@^7.13.0", "@babel/helper-create-class-features-plugin@^7.14.6":
-  version "7.14.6"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.14.6.tgz#f114469b6c06f8b5c59c6c4e74621f5085362542"
-  integrity sha512-Z6gsfGofTxH/+LQXqYEK45kxmcensbzmk/oi8DmaQytlQCgqNZt9XQF8iqlI/SeXWVjaMNxvYvzaYw+kh42mDg==
+"@babel/helper-create-class-features-plugin@^7.13.0":
+  version "7.17.1"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz#9699f14a88833a7e055ce57dcd3ffdcd25186b21"
+  integrity sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.14.5"
-    "@babel/helper-function-name" "^7.14.5"
-    "@babel/helper-member-expression-to-functions" "^7.14.5"
-    "@babel/helper-optimise-call-expression" "^7.14.5"
-    "@babel/helper-replace-supers" "^7.14.5"
-    "@babel/helper-split-export-declaration" "^7.14.5"
+    "@babel/helper-annotate-as-pure" "^7.16.7"
+    "@babel/helper-environment-visitor" "^7.16.7"
+    "@babel/helper-function-name" "^7.16.7"
+    "@babel/helper-member-expression-to-functions" "^7.16.7"
+    "@babel/helper-optimise-call-expression" "^7.16.7"
+    "@babel/helper-replace-supers" "^7.16.7"
+    "@babel/helper-split-export-declaration" "^7.16.7"
+
+"@babel/helper-create-class-features-plugin@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz#090d4d166b342a03a9fec37ef4fd5aeb9c7c6a4b"
+  integrity sha512-XLwWvqEaq19zFlF5PTgOod4bUA+XbkR4WLQBct1bkzmxJGB0ZEJaoKF4c8cgH9oBtCDuYJ8BP5NB9uFiEgO5QA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.16.0"
+    "@babel/helper-function-name" "^7.16.0"
+    "@babel/helper-member-expression-to-functions" "^7.16.0"
+    "@babel/helper-optimise-call-expression" "^7.16.0"
+    "@babel/helper-replace-supers" "^7.16.0"
+    "@babel/helper-split-export-declaration" "^7.16.0"
 
 "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7":
   version "7.17.0"
@@ -200,15 +207,7 @@
     "@babel/helper-replace-supers" "^7.16.7"
     "@babel/helper-split-export-declaration" "^7.16.7"
 
-"@babel/helper-create-regexp-features-plugin@^7.12.13":
-  version "7.14.3"
-  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.14.3.tgz#149aa6d78c016e318c43e2409a0ae9c136a86688"
-  integrity sha512-JIB2+XJrb7v3zceV2XzDhGIB902CmKGSpSl4q2C6agU9SNLG/2V1RtFRGPG1Ajh9STj3+q6zJMOC+N/pp2P9DA==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.12.13"
-    regexpu-core "^4.7.1"
-
-"@babel/helper-create-regexp-features-plugin@^7.16.7":
+"@babel/helper-create-regexp-features-plugin@^7.12.13", "@babel/helper-create-regexp-features-plugin@^7.16.7":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1"
   integrity sha512-awO2So99wG6KnlE+TPs6rn83gCz5WlEePJDTnLEqbchMVrBeAujURVphRdigsk094VhvZehFoNOihSlcBjwsXA==
@@ -216,6 +215,14 @@
     "@babel/helper-annotate-as-pure" "^7.16.7"
     regexpu-core "^5.0.1"
 
+"@babel/helper-create-regexp-features-plugin@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.16.0.tgz#06b2348ce37fccc4f5e18dcd8d75053f2a7c44ff"
+  integrity sha512-3DyG0zAFAZKcOp7aVr33ddwkxJ0Z0Jr5V99y3I690eYLpukJsJvAbzTy1ewoCqsML8SbIrjH14Jc/nSQ4TvNPA==
+  dependencies:
+    "@babel/helper-annotate-as-pure" "^7.16.0"
+    regexpu-core "^4.7.1"
+
 "@babel/helper-define-polyfill-provider@^0.1.5":
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.1.5.tgz#3c2f91b7971b9fc11fe779c945c014065dea340e"
@@ -251,13 +258,6 @@
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-explode-assignable-expression@^7.12.13":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f"
-  integrity sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA==
-  dependencies:
-    "@babel/types" "^7.13.0"
-
 "@babel/helper-explode-assignable-expression@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a"
@@ -265,7 +265,7 @@
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-function-name@^7.12.13", "@babel/helper-function-name@^7.14.5":
+"@babel/helper-function-name@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.14.5.tgz#89e2c474972f15d8e233b52ee8c480e2cfcd50c4"
   integrity sha512-Gjna0AsXWfFvrAuX+VKcN/aNNWonizBj39yGwUzVDVTlMYJMK2Wp6xdpy72mfArFq5uK+NOuexfzZlzI1z9+AQ==
@@ -274,6 +274,15 @@
     "@babel/template" "^7.14.5"
     "@babel/types" "^7.14.5"
 
+"@babel/helper-function-name@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.0.tgz#b7dd0797d00bbfee4f07e9c4ea5b0e30c8bb1481"
+  integrity sha512-BZh4mEk1xi2h4HFjWUXRQX5AEx4rvaZxHgax9gcjdLWdkjsY7MKt5p0otjsg5noXw+pB+clMCjw+aEVYADMjog==
+  dependencies:
+    "@babel/helper-get-function-arity" "^7.16.0"
+    "@babel/template" "^7.16.0"
+    "@babel/types" "^7.16.0"
+
 "@babel/helper-function-name@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.16.7.tgz#f1ec51551fb1c8956bc8dd95f38523b6cf375f8f"
@@ -283,27 +292,34 @@
     "@babel/template" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-get-function-arity@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815"
-  integrity sha512-I1Db4Shst5lewOM4V+ZKJzQ0JGGaZ6VY1jYvMghRjqs6DWgxLCIyFt30GlnKkfUeFLpJt2vzbMVEXVSXlIFYUg==
-  dependencies:
-    "@babel/types" "^7.14.5"
-
-"@babel/helper-get-function-arity@^7.16.7":
+"@babel/helper-get-function-arity@^7.14.5", "@babel/helper-get-function-arity@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419"
   integrity sha512-flc+RLSOBXzNzVhcLu6ujeHUrD6tANAOU5ojrRx/as+tbzf8+stUCj7+IfRRoAbEZqj/ahXEMsjhOhgeZsrnTw==
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-hoist-variables@^7.13.0", "@babel/helper-hoist-variables@^7.14.5":
+"@babel/helper-get-function-arity@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.0.tgz#0088c7486b29a9cb5d948b1a1de46db66e089cfa"
+  integrity sha512-ASCquNcywC1NkYh/z7Cgp3w31YW8aojjYIlNg4VeJiHkqyP4AzIvr4qx7pYDb4/s8YcsZWqqOSxgkvjUz1kpDQ==
+  dependencies:
+    "@babel/types" "^7.16.0"
+
+"@babel/helper-hoist-variables@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.14.5.tgz#e0dd27c33a78e577d7c8884916a3e7ef1f7c7f8d"
   integrity sha512-R1PXiz31Uc0Vxy4OEOm07x0oSjKAdPPCh3tPivn/Eo8cvz6gveAeuyUUPB21Hoiif0uoPQSSdhIPS3352nvdyQ==
   dependencies:
     "@babel/types" "^7.14.5"
 
+"@babel/helper-hoist-variables@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.0.tgz#4c9023c2f1def7e28ff46fc1dbcd36a39beaa81a"
+  integrity sha512-1AZlpazjUR0EQZQv3sgRNfM9mEVWPK3M6vlalczA+EECcPz3XPh6VplbErL5UoMpChhSck5wAJHthlj1bYpcmg==
+  dependencies:
+    "@babel/types" "^7.16.0"
+
 "@babel/helper-hoist-variables@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.16.7.tgz#86bcb19a77a509c7b77d0e22323ef588fa58c246"
@@ -311,12 +327,12 @@
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-member-expression-to-functions@^7.14.5":
-  version "7.14.7"
-  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970"
-  integrity sha512-TMUt4xKxJn6ccjcOW7c4hlwyJArizskAhoSTOCkA0uZ+KghIaci0Qg9R043kUMWI9mtQfgny+NQ5QATnZ+paaA==
+"@babel/helper-member-expression-to-functions@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4"
+  integrity sha512-bsjlBFPuWT6IWhl28EdrQ+gTvSvj5tqVP5Xeftp07SEuz5pLnsXZuDkDD3Rfcxy0IsHmbZ+7B2/9SHzxO0T+sQ==
   dependencies:
-    "@babel/types" "^7.14.5"
+    "@babel/types" "^7.16.0"
 
 "@babel/helper-member-expression-to-functions@^7.16.7":
   version "7.16.7"
@@ -325,13 +341,20 @@
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12":
+"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.13":
   version "7.13.12"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz#c6a369a6f3621cb25da014078684da9196b61977"
   integrity sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA==
   dependencies:
     "@babel/types" "^7.13.12"
 
+"@babel/helper-module-imports@^7.15.4":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz#90538e60b672ecf1b448f5f4f5433d37e79a3ec3"
+  integrity sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==
+  dependencies:
+    "@babel/types" "^7.16.0"
+
 "@babel/helper-module-imports@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437"
@@ -368,12 +391,12 @@
     "@babel/traverse" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-optimise-call-expression@^7.12.13", "@babel/helper-optimise-call-expression@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
-  integrity sha512-IqiLIrODUOdnPU9/F8ib1Fx2ohlgDhxnIDU7OEVi+kAbEZcyiF7BLU8W6PfvPi9LzztjS7kcbzbmL7oG8kD6VA==
+"@babel/helper-optimise-call-expression@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.0.tgz#cecdb145d70c54096b1564f8e9f10cd7d193b338"
+  integrity sha512-SuI467Gi2V8fkofm2JPnZzB/SUuXoJA5zXe/xzyPP2M04686RzFKFHPK6HDVN6JvWBIEW8tt9hPR7fXdn2Lgpw==
   dependencies:
-    "@babel/types" "^7.14.5"
+    "@babel/types" "^7.16.0"
 
 "@babel/helper-optimise-call-expression@^7.16.7":
   version "7.16.7"
@@ -392,15 +415,6 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
   integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
 
-"@babel/helper-remap-async-to-generator@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz#376a760d9f7b4b2077a9dd05aa9c3927cadb2209"
-  integrity sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.12.13"
-    "@babel/helper-wrap-function" "^7.13.0"
-    "@babel/types" "^7.13.0"
-
 "@babel/helper-remap-async-to-generator@^7.16.8":
   version "7.16.8"
   resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
@@ -410,17 +424,7 @@
     "@babel/helper-wrap-function" "^7.16.8"
     "@babel/types" "^7.16.8"
 
-"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94"
-  integrity sha512-3i1Qe9/8x/hCHINujn+iuHy+mMRLoc77b2nI9TB0zjH1hvn9qGlXjWlggdwUcju36PkPCy/lpM7LLUdcTyH4Ow==
-  dependencies:
-    "@babel/helper-member-expression-to-functions" "^7.14.5"
-    "@babel/helper-optimise-call-expression" "^7.14.5"
-    "@babel/traverse" "^7.14.5"
-    "@babel/types" "^7.14.5"
-
-"@babel/helper-replace-supers@^7.16.7":
+"@babel/helper-replace-supers@^7.13.0", "@babel/helper-replace-supers@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.7.tgz#e9f5f5f32ac90429c1a4bdec0f231ef0c2838ab1"
   integrity sha512-y9vsWilTNaVnVh6xiJfABzsNpgDPKev9HnAgz6Gb1p6UUwf9NepdlsV7VXGCftJM+jqD5f7JIEubcpLjZj5dBw==
@@ -431,27 +435,23 @@
     "@babel/traverse" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/helper-simple-access@^7.12.13":
-  version "7.13.12"
-  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.13.12.tgz#dd6c538afb61819d205a012c31792a39c7a5eaf6"
-  integrity sha512-7FEjbrx5SL9cWvXioDbnlYTppcZGuCY6ow3/D5vMggb2Ywgu4dMrpTJX0JdQAIcRRUElOIxF3yEooa9gUb9ZbA==
+"@babel/helper-replace-supers@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.16.0.tgz#73055e8d3cf9bcba8ddb55cad93fedc860f68f17"
+  integrity sha512-TQxuQfSCdoha7cpRNJvfaYxxxzmbxXw/+6cS7V02eeDYyhxderSoMVALvwupA54/pZcOTtVeJ0xccp1nGWladA==
   dependencies:
-    "@babel/types" "^7.13.12"
+    "@babel/helper-member-expression-to-functions" "^7.16.0"
+    "@babel/helper-optimise-call-expression" "^7.16.0"
+    "@babel/traverse" "^7.16.0"
+    "@babel/types" "^7.16.0"
 
-"@babel/helper-simple-access@^7.16.7":
+"@babel/helper-simple-access@^7.12.13", "@babel/helper-simple-access@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.16.7.tgz#d656654b9ea08dbb9659b69d61063ccd343ff0f7"
   integrity sha512-ZIzHVyoeLMvXMN/vok/a4LWRy8G2v205mNP0XOuf9XRLyX5/u9CnVulUtDgUTama3lT+bf/UqucuZjqiGuTS1g==
   dependencies:
     "@babel/types" "^7.16.7"
 
-"@babel/helper-skip-transparent-expression-wrappers@^7.12.1":
-  version "7.12.1"
-  resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf"
-  integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==
-  dependencies:
-    "@babel/types" "^7.12.1"
-
 "@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
   version "7.16.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09"
@@ -459,49 +459,46 @@
   dependencies:
     "@babel/types" "^7.16.0"
 
-"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.14.5":
+"@babel/helper-split-export-declaration@^7.12.13", "@babel/helper-split-export-declaration@^7.16.7":
+  version "7.16.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
+  integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+  dependencies:
+    "@babel/types" "^7.16.7"
+
+"@babel/helper-split-export-declaration@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
   integrity sha512-hprxVPu6e5Kdp2puZUmvOGjaLv9TCe58E/Fl6hRq4YiVQxIcNvuq6uTM2r1mT/oPskuS9CgR+I94sqAYv0NGKA==
   dependencies:
     "@babel/types" "^7.14.5"
 
-"@babel/helper-split-export-declaration@^7.16.7":
-  version "7.16.7"
-  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.7.tgz#0b648c0c42da9d3920d85ad585f2778620b8726b"
-  integrity sha512-xbWoy/PFoxSWazIToT9Sif+jJTlrMcndIsaOKvTA6u7QEo7ilkRZpjew18/W3c7nm8fXdUDXh02VXTbZ0pGDNw==
+"@babel/helper-split-export-declaration@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.16.0.tgz#29672f43663e936df370aaeb22beddb3baec7438"
+  integrity sha512-0YMMRpuDFNGTHNRiiqJX19GjNXA4H0E8jZ2ibccfSxaCogbm3am5WN/2nQNj0YnQwGWM1J06GOcQ2qnh3+0paw==
   dependencies:
-    "@babel/types" "^7.16.7"
-
-"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9":
-  version "7.14.9"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.9.tgz#6654d171b2024f6d8ee151bf2509699919131d48"
-  integrity sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==
+    "@babel/types" "^7.16.0"
 
-"@babel/helper-validator-identifier@^7.16.7":
+"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.9", "@babel/helper-validator-identifier@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
   integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
 
-"@babel/helper-validator-option@^7.12.17", "@babel/helper-validator-option@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
-  integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
+"@babel/helper-validator-identifier@^7.15.7":
+  version "7.15.7"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
+  integrity sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==
 
-"@babel/helper-validator-option@^7.16.7":
+"@babel/helper-validator-option@^7.12.17", "@babel/helper-validator-option@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
   integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
 
-"@babel/helper-wrap-function@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz#bdb5c66fda8526ec235ab894ad53a1235c79fcc4"
-  integrity sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA==
-  dependencies:
-    "@babel/helper-function-name" "^7.12.13"
-    "@babel/template" "^7.12.13"
-    "@babel/traverse" "^7.13.0"
-    "@babel/types" "^7.13.0"
+"@babel/helper-validator-option@^7.14.5":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
+  integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
 
 "@babel/helper-wrap-function@^7.16.8":
   version "7.16.8"
@@ -540,6 +537,15 @@
     chalk "^2.0.0"
     js-tokens "^4.0.0"
 
+"@babel/highlight@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a"
+  integrity sha512-t8MH41kUQylBtu2+4IQA3atqevA2lRgqA2wyVB/YiWmsDSuylZZuXOUy9ric30hfzauEFfdsuk/eXTRrGrfd0g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.15.7"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
 "@babel/highlight@^7.16.7":
   version "7.16.10"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88"
@@ -554,6 +560,11 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.7.tgz#6099720c8839ca865a2637e6c85852ead0bdb595"
   integrity sha512-X67Z5y+VBJuHB/RjwECp8kSl5uYi0BvRbNeWqkaJCVh+LiTPl19WBUfG627psSgp9rSf6ojuXghQM3ha6qHHdA==
 
+"@babel/parser@^7.16.0":
+  version "7.16.2"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.2.tgz#3723cd5c8d8773eef96ce57ea1d9b7faaccd12ac"
+  integrity sha512-RUVpT0G2h6rOZwqLDTrKk7ksNv7YpAilTnYe1/Q+eDjxEceRMKVWbCsX7t8h6C1qCFi/1Y8WZjcEPBAFG27GPw==
+
 "@babel/parser@^7.16.7", "@babel/parser@^7.17.0":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.0.tgz#f0ac33eddbe214e4105363bb17c3341c5ffcc43c"
@@ -575,16 +586,7 @@
     "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
     "@babel/plugin-proposal-optional-chaining" "^7.16.7"
 
-"@babel/plugin-proposal-async-generator-functions@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1"
-  integrity sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-remap-async-to-generator" "^7.13.0"
-    "@babel/plugin-syntax-async-generators" "^7.8.4"
-
-"@babel/plugin-proposal-async-generator-functions@^7.16.8":
+"@babel/plugin-proposal-async-generator-functions@^7.13.8", "@babel/plugin-proposal-async-generator-functions@^7.16.8":
   version "7.16.8"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8"
   integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==
@@ -593,7 +595,7 @@
     "@babel/helper-remap-async-to-generator" "^7.16.8"
     "@babel/plugin-syntax-async-generators" "^7.8.4"
 
-"@babel/plugin-proposal-class-properties@^7.10.4", "@babel/plugin-proposal-class-properties@^7.13.0":
+"@babel/plugin-proposal-class-properties@^7.10.4":
   version "7.13.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37"
   integrity sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg==
@@ -601,7 +603,7 @@
     "@babel/helper-create-class-features-plugin" "^7.13.0"
     "@babel/helper-plugin-utils" "^7.13.0"
 
-"@babel/plugin-proposal-class-properties@^7.16.7":
+"@babel/plugin-proposal-class-properties@^7.13.0", "@babel/plugin-proposal-class-properties@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0"
   integrity sha512-IobU0Xme31ewjYOShSIqd/ZGM/r/cuOz2z0MDbNrhF5FW+ZVgi0f2lyeoj9KFPDOAqsYxmLWZte1WOwlvY9aww==
@@ -618,15 +620,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-class-static-block" "^7.14.5"
 
-"@babel/plugin-proposal-dynamic-import@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d"
-  integrity sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-dynamic-import" "^7.8.3"
-
-"@babel/plugin-proposal-dynamic-import@^7.16.7":
+"@babel/plugin-proposal-dynamic-import@^7.13.8", "@babel/plugin-proposal-dynamic-import@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2"
   integrity sha512-I8SW9Ho3/8DRSdmDdH3gORdyUuYnk1m4cMxUAdu5oy4n3OfN8flDEH+d60iG7dUfi0KkYwSvoalHzzdRzpWHTg==
@@ -634,15 +628,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-dynamic-import" "^7.8.3"
 
-"@babel/plugin-proposal-export-namespace-from@^7.12.13":
-  version "7.14.2"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.14.2.tgz#62542f94aa9ce8f6dba79eec698af22112253791"
-  integrity sha512-sRxW3z3Zp3pFfLAgVEvzTFutTXax837oOatUIvSG9o5gRj9mKwm3br1Se5f4QalTQs9x4AzlA/HrCWbQIHASUQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
-
-"@babel/plugin-proposal-export-namespace-from@^7.16.7":
+"@babel/plugin-proposal-export-namespace-from@^7.12.13", "@babel/plugin-proposal-export-namespace-from@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163"
   integrity sha512-ZxdtqDXLRGBL64ocZcs7ovt71L3jhC1RGSyR996svrCi3PYqHNkb3SwPJCs8RIzD86s+WPpt2S73+EHCGO+NUA==
@@ -650,15 +636,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
 
-"@babel/plugin-proposal-json-strings@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b"
-  integrity sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-json-strings" "^7.8.3"
-
-"@babel/plugin-proposal-json-strings@^7.16.7":
+"@babel/plugin-proposal-json-strings@^7.13.8", "@babel/plugin-proposal-json-strings@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8"
   integrity sha512-lNZ3EEggsGY78JavgbHsK9u5P3pQaW7k4axlgFLYkMd7UBsiNahCITShLjNQschPyjtO6dADrL24757IdhBrsQ==
@@ -666,15 +644,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-json-strings" "^7.8.3"
 
-"@babel/plugin-proposal-logical-assignment-operators@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a"
-  integrity sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
-
-"@babel/plugin-proposal-logical-assignment-operators@^7.16.7":
+"@babel/plugin-proposal-logical-assignment-operators@^7.13.8", "@babel/plugin-proposal-logical-assignment-operators@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea"
   integrity sha512-K3XzyZJGQCr00+EtYtrDjmwX7o7PLK6U9bi1nCwkQioRFVUv6dJoxbQjtWVtP+bCPy82bONBKG8NPyQ4+i6yjg==
@@ -682,15 +652,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4"
 
-"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3"
-  integrity sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
-
-"@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7":
+"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99"
   integrity sha512-aUOrYU3EVtjf62jQrCj63pYZ7k6vns2h/DQvHPWGmsJRYzWXZ6/AsfgpiRy6XiuIDADhJzP2Q9MwSMKauBQ+UQ==
@@ -698,15 +660,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
 
-"@babel/plugin-proposal-numeric-separator@^7.12.13":
-  version "7.14.2"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.14.2.tgz#82b4cc06571143faf50626104b335dd71baa4f9e"
-  integrity sha512-DcTQY9syxu9BpU3Uo94fjCB3LN9/hgPS8oUL7KrSW3bA2ePrKZZPJcc5y0hoJAM9dft3pGfErtEUvxXQcfLxUg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-numeric-separator" "^7.10.4"
-
-"@babel/plugin-proposal-numeric-separator@^7.16.7":
+"@babel/plugin-proposal-numeric-separator@^7.12.13", "@babel/plugin-proposal-numeric-separator@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9"
   integrity sha512-vQgPMknOIgiuVqbokToyXbkY/OmmjAzr/0lhSIbG/KmnzXPGwW/AdhdKpi+O4X/VkWiWjnkKOBiqJrTaC98VKw==
@@ -714,7 +668,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-numeric-separator" "^7.10.4"
 
-"@babel/plugin-proposal-object-rest-spread@^7.10.1", "@babel/plugin-proposal-object-rest-spread@^7.13.8":
+"@babel/plugin-proposal-object-rest-spread@^7.10.1":
   version "7.14.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.14.7.tgz#5920a2b3df7f7901df0205974c0641b13fd9d363"
   integrity sha512-082hsZz+sVabfmDWo1Oct1u1AgbKbUAyVgmX4otIc7bdsRgHBXwTwb3DpDmD4Eyyx6DNiuz5UAATT655k+kL5g==
@@ -725,7 +679,7 @@
     "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
     "@babel/plugin-transform-parameters" "^7.14.5"
 
-"@babel/plugin-proposal-object-rest-spread@^7.16.7":
+"@babel/plugin-proposal-object-rest-spread@^7.13.8", "@babel/plugin-proposal-object-rest-spread@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.16.7.tgz#94593ef1ddf37021a25bdcb5754c4a8d534b01d8"
   integrity sha512-3O0Y4+dw94HA86qSg9IHfyPktgR7q3gpNVAeiKQd+8jBKFaU5NQS1Yatgo4wY+UFNuLjvxcSmzcsHqrhgTyBUA==
@@ -736,15 +690,7 @@
     "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
     "@babel/plugin-transform-parameters" "^7.16.7"
 
-"@babel/plugin-proposal-optional-catch-binding@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107"
-  integrity sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
-
-"@babel/plugin-proposal-optional-catch-binding@^7.16.7":
+"@babel/plugin-proposal-optional-catch-binding@^7.13.8", "@babel/plugin-proposal-optional-catch-binding@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf"
   integrity sha512-eMOH/L4OvWSZAE1VkHbr1vckLG1WUcHGJSLqqQwl2GaUqG6QjddvrOaTUMNYiv77H5IKPMZ9U9P7EaHwvAShfA==
@@ -752,16 +698,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
 
-"@babel/plugin-proposal-optional-chaining@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz#e39df93efe7e7e621841babc197982e140e90756"
-  integrity sha512-hpbBwbTgd7Cz1QryvwJZRo1U0k1q8uyBmeXOSQUjdg/A2TASkhR/rz7AyqZ/kS8kbpsNA80rOYbxySBJAqmhhQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
-    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
-
-"@babel/plugin-proposal-optional-chaining@^7.16.7":
+"@babel/plugin-proposal-optional-chaining@^7.13.8", "@babel/plugin-proposal-optional-chaining@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a"
   integrity sha512-eC3xy+ZrUcBtP7x+sq62Q/HYd674pPTb/77XZMb5wbDPGWIdUbSr4Agr052+zaUPSb+gGRnjxXfKFvx5iMJ+DA==
@@ -770,15 +707,7 @@
     "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
 
-"@babel/plugin-proposal-private-methods@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz#04bd4c6d40f6e6bbfa2f57e2d8094bad900ef787"
-  integrity sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q==
-  dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.13.0"
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-proposal-private-methods@^7.16.11":
+"@babel/plugin-proposal-private-methods@^7.13.0", "@babel/plugin-proposal-private-methods@^7.16.11":
   version "7.16.11"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50"
   integrity sha512-F/2uAkPlXDr8+BHpZvo19w3hLFKge+k75XUprE6jaqKxjGkSYcK+4c+bup5PdW/7W/Rpjwql7FTVEDW+fRAQsw==
@@ -796,15 +725,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/plugin-syntax-private-property-in-object" "^7.14.5"
 
-"@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.4.4":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba"
-  integrity sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg==
-  dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-proposal-unicode-property-regex@^7.16.7":
+"@babel/plugin-proposal-unicode-property-regex@^7.12.13", "@babel/plugin-proposal-unicode-property-regex@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2"
   integrity sha512-QRK0YI/40VLhNVGIjRNAAQkEHws0cswSdFFjpFyt943YmJIU1da9uW63Iu6NFV6CxTZW5eTDCrwZUstBWgp/Rg==
@@ -812,6 +733,14 @@
     "@babel/helper-create-regexp-features-plugin" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
+"@babel/plugin-proposal-unicode-property-regex@^7.4.4":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.0.tgz#890482dfc5ea378e42e19a71e709728cabf18612"
+  integrity sha512-ti7IdM54NXv29cA4+bNNKEMS4jLMCbJgl+Drv+FgYy0erJLAxNAIXcNjNjrRZEcWq0xJHsNVwQezskMFpF8N9g==
+  dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.16.0"
+    "@babel/helper-plugin-utils" "^7.14.5"
+
 "@babel/plugin-syntax-async-generators@^7.8.4":
   version "7.8.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -854,13 +783,6 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
 
-"@babel/plugin-syntax-jsx@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.12.13.tgz#044fb81ebad6698fe62c478875575bcbb9b70f15"
-  integrity sha512-d4HM23Q1K7oq/SLNmG6mRt85l2csmQ0cHRaxRXjKW0YFdEXqlZ5kzFQKH5Uc3rDJECgu+yCRgPkG04Mm98R/1g==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
 "@babel/plugin-syntax-jsx@^7.16.0", "@babel/plugin-syntax-jsx@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.7.tgz#50b6571d13f764266a113d77c82b4a6508bbe665"
@@ -917,51 +839,28 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-syntax-top-level-await@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178"
-  integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-syntax-top-level-await@^7.14.5":
+"@babel/plugin-syntax-top-level-await@^7.12.13", "@babel/plugin-syntax-top-level-await@^7.14.5":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
   integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-syntax-typescript@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716"
-  integrity sha512-u6OXzDaIXjEstBRRoBCQ/uKQKlbuaeE5in0RvWdA4pN6AhqxTIwUsnHPU1CFZA/amYObMsuWhYfRl3Ch90HD0Q==
+"@babel/plugin-syntax-typescript@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.16.0.tgz#2feeb13d9334cc582ea9111d3506f773174179bb"
+  integrity sha512-Xv6mEXqVdaqCBfJFyeab0fH2DnUoMsDmhamxsSi4j8nLd4Vtw213WMJr55xxqipC/YVWyPY3K0blJncPYji+dQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
 
-"@babel/plugin-transform-arrow-functions@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae"
-  integrity sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-arrow-functions@^7.16.7":
+"@babel/plugin-transform-arrow-functions@^7.13.0", "@babel/plugin-transform-arrow-functions@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.7.tgz#44125e653d94b98db76369de9c396dc14bef4154"
   integrity sha512-9ffkFFMbvzTvv+7dTp/66xvZAWASuPD5Tl9LK3Z9vhOmANo6j94rik+5YMBt4CwHVMWLWpMsriIc2zsa3WW3xQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-async-to-generator@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz#8e112bf6771b82bf1e974e5e26806c5c99aa516f"
-  integrity sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg==
-  dependencies:
-    "@babel/helper-module-imports" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-remap-async-to-generator" "^7.13.0"
-
-"@babel/plugin-transform-async-to-generator@^7.16.8":
+"@babel/plugin-transform-async-to-generator@^7.13.0", "@babel/plugin-transform-async-to-generator@^7.16.8":
   version "7.16.8"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808"
   integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==
@@ -970,48 +869,21 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/helper-remap-async-to-generator" "^7.16.8"
 
-"@babel/plugin-transform-block-scoped-functions@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4"
-  integrity sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-block-scoped-functions@^7.16.7":
+"@babel/plugin-transform-block-scoped-functions@^7.12.13", "@babel/plugin-transform-block-scoped-functions@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620"
   integrity sha512-JUuzlzmF40Z9cXyytcbZEZKckgrQzChbQJw/5PuEHYeqzCsvebDx0K0jWnIIVcmmDOAVctCgnYs0pMcrYj2zJg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-block-scoping@^7.12.13":
-  version "7.14.2"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.14.2.tgz#761cb12ab5a88d640ad4af4aa81f820e6b5fdf5c"
-  integrity sha512-neZZcP19NugZZqNwMTH+KoBjx5WyvESPSIOQb4JHpfd+zPfqcH65RMu5xJju5+6q/Y2VzYrleQTr+b6METyyxg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-block-scoping@^7.16.7":
+"@babel/plugin-transform-block-scoping@^7.12.13", "@babel/plugin-transform-block-scoping@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87"
   integrity sha512-ObZev2nxVAYA4bhyusELdo9hb3H+A56bxH3FZMbEImZFiEDYVHXQSJ1hQKFlDnlt8G9bBrCZ5ZpURZUrV4G5qQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-classes@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b"
-  integrity sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.12.13"
-    "@babel/helper-function-name" "^7.12.13"
-    "@babel/helper-optimise-call-expression" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-replace-supers" "^7.13.0"
-    "@babel/helper-split-export-declaration" "^7.12.13"
-    globals "^11.1.0"
-
-"@babel/plugin-transform-classes@^7.16.7":
+"@babel/plugin-transform-classes@^7.13.0", "@babel/plugin-transform-classes@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00"
   integrity sha512-WY7og38SFAGYRe64BrjKf8OrE6ulEHtr5jEYaZMwox9KebgqPi67Zqz8K53EKk1fFEJgm96r32rkKZ3qA2nCWQ==
@@ -1025,43 +897,21 @@
     "@babel/helper-split-export-declaration" "^7.16.7"
     globals "^11.1.0"
 
-"@babel/plugin-transform-computed-properties@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz#845c6e8b9bb55376b1fa0b92ef0bdc8ea06644ed"
-  integrity sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-computed-properties@^7.16.7":
+"@babel/plugin-transform-computed-properties@^7.13.0", "@babel/plugin-transform-computed-properties@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470"
   integrity sha512-gN72G9bcmenVILj//sv1zLNaPyYcOzUho2lIJBMh/iakJ9ygCo/hEF9cpGb61SCMEDxbbyBoVQxrt+bWKu5KGw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-destructuring@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963"
-  integrity sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-destructuring@^7.16.7":
+"@babel/plugin-transform-destructuring@^7.13.0", "@babel/plugin-transform-destructuring@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz#ca9588ae2d63978a4c29d3f33282d8603f618e23"
   integrity sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.4.4":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad"
-  integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==
-  dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-dotall-regex@^7.16.7":
+"@babel/plugin-transform-dotall-regex@^7.12.13", "@babel/plugin-transform-dotall-regex@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241"
   integrity sha512-Lyttaao2SjZF6Pf4vk1dVKv8YypMpomAbygW+mU5cYP3S5cWTfCJjG8xV6CFdzGFlfWK81IjL9viiTvpb6G7gQ==
@@ -1069,29 +919,22 @@
     "@babel/helper-create-regexp-features-plugin" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-duplicate-keys@^7.12.13":
+"@babel/plugin-transform-dotall-regex@^7.4.4":
   version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz#6f06b87a8b803fd928e54b81c258f0a0033904de"
-  integrity sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ==
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad"
+  integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ==
   dependencies:
+    "@babel/helper-create-regexp-features-plugin" "^7.12.13"
     "@babel/helper-plugin-utils" "^7.12.13"
 
-"@babel/plugin-transform-duplicate-keys@^7.16.7":
+"@babel/plugin-transform-duplicate-keys@^7.12.13", "@babel/plugin-transform-duplicate-keys@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9"
   integrity sha512-03DvpbRfvWIXyK0/6QiR1KMTWeT6OcQ7tbhjrXyFS02kjuX/mu5Bvnh5SDSWHxyawit2g5aWhKwI86EE7GUnTw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-exponentiation-operator@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz#4d52390b9a273e651e4aba6aee49ef40e80cd0a1"
-  integrity sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA==
-  dependencies:
-    "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-exponentiation-operator@^7.16.7":
+"@babel/plugin-transform-exponentiation-operator@^7.12.13", "@babel/plugin-transform-exponentiation-operator@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b"
   integrity sha512-8UYLSlyLgRixQvlYH3J2ekXFHDFLQutdy7FfFAMm3CPZ6q9wHCwnUyiXpQCe3gVVnQlHc5nsuiEVziteRNTXEA==
@@ -1099,29 +942,14 @@
     "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-for-of@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz#c799f881a8091ac26b54867a845c3e97d2696062"
-  integrity sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-for-of@^7.16.7":
+"@babel/plugin-transform-for-of@^7.13.0", "@babel/plugin-transform-for-of@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c"
   integrity sha512-/QZm9W92Ptpw7sjI9Nx1mbcsWz33+l8kuMIQnDwgQBG5s3fAfQvkRjQ7NqXhtNcKOnPkdICmUHyCaWW06HCsqg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-function-name@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051"
-  integrity sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ==
-  dependencies:
-    "@babel/helper-function-name" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-function-name@^7.16.7":
+"@babel/plugin-transform-function-name@^7.12.13", "@babel/plugin-transform-function-name@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf"
   integrity sha512-SU/C68YVwTRxqWj5kgsbKINakGag0KTgq9f2iZEXdStoAbOzLHEBRYzImmA6yFo8YZhJVflvXmIHUO7GWHmxxA==
@@ -1130,44 +958,21 @@
     "@babel/helper-function-name" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-literals@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9"
-  integrity sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-literals@^7.16.7":
+"@babel/plugin-transform-literals@^7.12.13", "@babel/plugin-transform-literals@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1"
   integrity sha512-6tH8RTpTWI0s2sV6uq3e/C9wPo4PTqqZps4uF0kzQ9/xPLFQtipynvmT1g/dOfEJ+0EQsHhkQ/zyRId8J2b8zQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-member-expression-literals@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40"
-  integrity sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-member-expression-literals@^7.16.7":
+"@babel/plugin-transform-member-expression-literals@^7.12.13", "@babel/plugin-transform-member-expression-literals@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384"
   integrity sha512-mBruRMbktKQwbxaJof32LT9KLy2f3gH+27a5XSuXo6h7R3vqltl0PgZ80C8ZMKw98Bf8bqt6BEVi3svOh2PzMw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-modules-amd@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz#19f511d60e3d8753cc5a6d4e775d3a5184866cc3"
-  integrity sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ==
-  dependencies:
-    "@babel/helper-module-transforms" "^7.13.0"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-amd@^7.16.7":
+"@babel/plugin-transform-modules-amd@^7.13.0", "@babel/plugin-transform-modules-amd@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186"
   integrity sha512-KaaEtgBL7FKYwjJ/teH63oAmE3lP34N3kshz8mm4VMAw7U3PxjVwwUmxEFksbgsNUaO3wId9R2AVQYSEGRa2+g==
@@ -1176,17 +981,7 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-commonjs@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b"
-  integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw==
-  dependencies:
-    "@babel/helper-module-transforms" "^7.13.0"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-simple-access" "^7.12.13"
-    babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-commonjs@^7.16.8":
+"@babel/plugin-transform-modules-commonjs@^7.13.8", "@babel/plugin-transform-modules-commonjs@^7.16.8":
   version "7.16.8"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe"
   integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==
@@ -1196,18 +991,7 @@
     "@babel/helper-simple-access" "^7.16.7"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-systemjs@^7.13.8":
-  version "7.13.8"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3"
-  integrity sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A==
-  dependencies:
-    "@babel/helper-hoist-variables" "^7.13.0"
-    "@babel/helper-module-transforms" "^7.13.0"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-validator-identifier" "^7.12.11"
-    babel-plugin-dynamic-import-node "^2.3.3"
-
-"@babel/plugin-transform-modules-systemjs@^7.16.7":
+"@babel/plugin-transform-modules-systemjs@^7.13.8", "@babel/plugin-transform-modules-systemjs@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.16.7.tgz#887cefaef88e684d29558c2b13ee0563e287c2d7"
   integrity sha512-DuK5E3k+QQmnOqBR9UkusByy5WZWGRxfzV529s9nPra1GE7olmxfqO2FHobEOYSPIjPBTr4p66YDcjQnt8cBmw==
@@ -1218,15 +1002,7 @@
     "@babel/helper-validator-identifier" "^7.16.7"
     babel-plugin-dynamic-import-node "^2.3.3"
 
-"@babel/plugin-transform-modules-umd@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz#8a3d96a97d199705b9fd021580082af81c06e70b"
-  integrity sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw==
-  dependencies:
-    "@babel/helper-module-transforms" "^7.13.0"
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-modules-umd@^7.16.7":
+"@babel/plugin-transform-modules-umd@^7.13.0", "@babel/plugin-transform-modules-umd@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618"
   integrity sha512-EMh7uolsC8O4xhudF2F6wedbSHm1HHZ0C6aJ7K67zcDNidMzVcxWdGr+htW9n21klm+bOn+Rx4CBsAntZd3rEQ==
@@ -1234,43 +1010,21 @@
     "@babel/helper-module-transforms" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-named-capturing-groups-regex@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9"
-  integrity sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA==
-  dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.12.13"
-
-"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8":
+"@babel/plugin-transform-named-capturing-groups-regex@^7.12.13", "@babel/plugin-transform-named-capturing-groups-regex@^7.16.8":
   version "7.16.8"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252"
   integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==
   dependencies:
     "@babel/helper-create-regexp-features-plugin" "^7.16.7"
 
-"@babel/plugin-transform-new-target@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz#e22d8c3af24b150dd528cbd6e685e799bf1c351c"
-  integrity sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-new-target@^7.16.7":
+"@babel/plugin-transform-new-target@^7.12.13", "@babel/plugin-transform-new-target@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244"
   integrity sha512-xiLDzWNMfKoGOpc6t3U+etCE2yRnn3SM09BXqWPIZOBpL2gvVrBWUKnsJx0K/ADi5F5YC5f8APFfWrz25TdlGg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-object-super@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7"
-  integrity sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-    "@babel/helper-replace-supers" "^7.12.13"
-
-"@babel/plugin-transform-object-super@^7.16.7":
+"@babel/plugin-transform-object-super@^7.12.13", "@babel/plugin-transform-object-super@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94"
   integrity sha512-14J1feiQVWaGvRxj2WjyMuXS2jsBkgB3MdSN5HuC2G5nRspa5RK9COcs82Pwy5BuGcjb+fYaUj94mYcOj7rCvw==
@@ -1278,28 +1032,14 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/helper-replace-supers" "^7.16.7"
 
-"@babel/plugin-transform-parameters@^7.13.0", "@babel/plugin-transform-parameters@^7.14.5":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.14.5.tgz#49662e86a1f3ddccac6363a7dfb1ff0a158afeb3"
-  integrity sha512-Tl7LWdr6HUxTmzQtzuU14SqbgrSKmaR77M0OKyq4njZLQTPfOvzblNKyNkGwOfEFCEx7KeYHQHDI0P3F02IVkA==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.14.5"
-
-"@babel/plugin-transform-parameters@^7.16.7":
+"@babel/plugin-transform-parameters@^7.13.0", "@babel/plugin-transform-parameters@^7.14.5", "@babel/plugin-transform-parameters@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f"
   integrity sha512-AT3MufQ7zZEhU2hwOA11axBnExW0Lszu4RL/tAlUJBuNoRak+wehQW8h6KcXOcgjY42fHtDxswuMhMjFEuv/aw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-property-literals@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81"
-  integrity sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-property-literals@^7.16.7":
+"@babel/plugin-transform-property-literals@^7.12.13", "@babel/plugin-transform-property-literals@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55"
   integrity sha512-z4FGr9NMGdoIl1RqavCqGG+ZuYjfZ/hkCIeuH6Do7tXmSm0ls11nYVSJqFEUOSJbDab5wC6lRE/w6YjVcr6Hqw==
@@ -1313,46 +1053,21 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-react-display-name@^7.12.13":
-  version "7.14.2"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.14.2.tgz#2e854544d42ab3bb9c21f84e153d62e800fbd593"
-  integrity sha512-zCubvP+jjahpnFJvPaHPiGVfuVUjXHhFvJKQdNnsmSsiU9kR/rCZ41jHc++tERD2zV+p7Hr6is+t5b6iWTCqSw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-react-display-name@^7.16.7":
+"@babel/plugin-transform-react-display-name@^7.12.13", "@babel/plugin-transform-react-display-name@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340"
   integrity sha512-qgIg8BcZgd0G/Cz916D5+9kqX0c7nPZyXaP8R2tLNN5tkyIZdG5fEwBrxwplzSnjC1jvQmyMNVwUCZPcbGY7Pg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-react-jsx-development@^7.12.17":
-  version "7.12.17"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.17.tgz#f510c0fa7cd7234153539f9a362ced41a5ca1447"
-  integrity sha512-BPjYV86SVuOaudFhsJR1zjgxxOhJDt6JHNoD48DxWEIxUCAMjV1ys6DYw4SDYZh0b1QsS2vfIA9t/ZsQGsDOUQ==
-  dependencies:
-    "@babel/plugin-transform-react-jsx" "^7.12.17"
-
-"@babel/plugin-transform-react-jsx-development@^7.16.7":
+"@babel/plugin-transform-react-jsx-development@^7.12.17", "@babel/plugin-transform-react-jsx-development@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.16.7.tgz#43a00724a3ed2557ed3f276a01a929e6686ac7b8"
   integrity sha512-RMvQWvpla+xy6MlBpPlrKZCMRs2AGiHOGHY3xRwl0pEeim348dDyxeH4xBsMPbIMhujeq7ihE702eM2Ew0Wo+A==
   dependencies:
     "@babel/plugin-transform-react-jsx" "^7.16.7"
 
-"@babel/plugin-transform-react-jsx@^7.12.17", "@babel/plugin-transform-react-jsx@^7.13.12":
-  version "7.13.12"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.13.12.tgz#1df5dfaf0f4b784b43e96da6f28d630e775f68b3"
-  integrity sha512-jcEI2UqIcpCqB5U5DRxIl0tQEProI2gcu+g8VTIqxLO5Iidojb4d77q+fwGseCvd8af/lJ9masp4QWzBXFE2xA==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.12.13"
-    "@babel/helper-module-imports" "^7.13.12"
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/plugin-syntax-jsx" "^7.12.13"
-    "@babel/types" "^7.13.12"
-
-"@babel/plugin-transform-react-jsx@^7.16.7":
+"@babel/plugin-transform-react-jsx@^7.13.12", "@babel/plugin-transform-react-jsx@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz#86a6a220552afd0e4e1f0388a68a372be7add0d4"
   integrity sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==
@@ -1363,15 +1078,7 @@
     "@babel/plugin-syntax-jsx" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/plugin-transform-react-pure-annotations@^7.12.1":
-  version "7.12.1"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.12.1.tgz#05d46f0ab4d1339ac59adf20a1462c91b37a1a42"
-  integrity sha512-RqeaHiwZtphSIUZ5I85PEH19LOSzxfuEazoY7/pWASCAIBuATQzpSVD+eT6MebeeZT2F4eSL0u4vw6n4Nm0Mjg==
-  dependencies:
-    "@babel/helper-annotate-as-pure" "^7.10.4"
-    "@babel/helper-plugin-utils" "^7.10.4"
-
-"@babel/plugin-transform-react-pure-annotations@^7.16.7":
+"@babel/plugin-transform-react-pure-annotations@^7.12.1", "@babel/plugin-transform-react-pure-annotations@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.16.7.tgz#232bfd2f12eb551d6d7d01d13fe3f86b45eb9c67"
   integrity sha512-hs71ToC97k3QWxswh2ElzMFABXHvGiJ01IB1TbYQDGeWRKWz/MPUTh5jGExdHvosYKpnJW5Pm3S4+TA3FyX+GA==
@@ -1379,57 +1086,28 @@
     "@babel/helper-annotate-as-pure" "^7.16.7"
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-regenerator@^7.12.13":
-  version "7.13.15"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.13.15.tgz#e5eb28945bf8b6563e7f818945f966a8d2997f39"
-  integrity sha512-Bk9cOLSz8DiurcMETZ8E2YtIVJbFCPGW28DJWUakmyVWtQSm6Wsf0p4B4BfEr/eL2Nkhe/CICiUiMOCi1TPhuQ==
-  dependencies:
-    regenerator-transform "^0.14.2"
-
-"@babel/plugin-transform-regenerator@^7.16.7":
+"@babel/plugin-transform-regenerator@^7.12.13", "@babel/plugin-transform-regenerator@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.16.7.tgz#9e7576dc476cb89ccc5096fff7af659243b4adeb"
   integrity sha512-mF7jOgGYCkSJagJ6XCujSQg+6xC1M77/03K2oBmVJWoFGNUtnVJO4WHKJk3dnPC8HCcj4xBQP1Egm8DWh3Pb3Q==
   dependencies:
     regenerator-transform "^0.14.2"
 
-"@babel/plugin-transform-reserved-words@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz#7d9988d4f06e0fe697ea1d9803188aa18b472695"
-  integrity sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-reserved-words@^7.16.7":
+"@babel/plugin-transform-reserved-words@^7.12.13", "@babel/plugin-transform-reserved-words@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586"
   integrity sha512-KQzzDnZ9hWQBjwi5lpY5v9shmm6IVG0U9pB18zvMu2i4H90xpT4gmqwPYsn8rObiadYe2M0gmgsiOIF5A/2rtg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-shorthand-properties@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad"
-  integrity sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-shorthand-properties@^7.16.7":
+"@babel/plugin-transform-shorthand-properties@^7.12.13", "@babel/plugin-transform-shorthand-properties@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.16.7.tgz#e8549ae4afcf8382f711794c0c7b6b934c5fbd2a"
   integrity sha512-hah2+FEnoRoATdIb05IOXf+4GzXYTq75TVhIn1PewihbpyrNWUt2JbudKQOETWw6QpLe+AIUpJ5MVLYTQbeeUg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-spread@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz#84887710e273c1815ace7ae459f6f42a5d31d5fd"
-  integrity sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-    "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1"
-
-"@babel/plugin-transform-spread@^7.16.7":
+"@babel/plugin-transform-spread@^7.13.0", "@babel/plugin-transform-spread@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44"
   integrity sha512-+pjJpgAngb53L0iaA5gU/1MLXJIfXcYepLgXB3esVRf4fqmj8f2cxM3/FKaHsZms08hFQJkFccEWuIpm429TXg==
@@ -1437,80 +1115,44 @@
     "@babel/helper-plugin-utils" "^7.16.7"
     "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
 
-"@babel/plugin-transform-sticky-regex@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f"
-  integrity sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-sticky-regex@^7.16.7":
+"@babel/plugin-transform-sticky-regex@^7.12.13", "@babel/plugin-transform-sticky-regex@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660"
   integrity sha512-NJa0Bd/87QV5NZZzTuZG5BPJjLYadeSZ9fO6oOUoL4iQx+9EEuw/eEM92SrsT19Yc2jgB1u1hsjqDtH02c3Drw==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-template-literals@^7.13.0":
-  version "7.13.0"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz#a36049127977ad94438dee7443598d1cefdf409d"
-  integrity sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.13.0"
-
-"@babel/plugin-transform-template-literals@^7.16.7":
+"@babel/plugin-transform-template-literals@^7.13.0", "@babel/plugin-transform-template-literals@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab"
   integrity sha512-VwbkDDUeenlIjmfNeDX/V0aWrQH2QiVyJtwymVQSzItFDTpxfyJh3EVaQiS0rIN/CqbLGr0VcGmuwyTdZtdIsA==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-typeof-symbol@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f"
-  integrity sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-typeof-symbol@^7.16.7":
+"@babel/plugin-transform-typeof-symbol@^7.12.13", "@babel/plugin-transform-typeof-symbol@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e"
   integrity sha512-p2rOixCKRJzpg9JB4gjnG4gjWkWa89ZoYUnl9snJ1cWIcTH/hvxZqfO+WjG6T8DRBpctEol5jw1O5rA8gkCokQ==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-typescript@^7.14.5":
-  version "7.14.6"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.14.6.tgz#6e9c2d98da2507ebe0a883b100cde3c7279df36c"
-  integrity sha512-XlTdBq7Awr4FYIzqhmYY80WN0V0azF74DMPyFqVHBvf81ZUgc4X7ZOpx6O8eLDK6iM5cCQzeyJw0ynTaefixRA==
+"@babel/plugin-transform-typescript@^7.16.0":
+  version "7.16.1"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.1.tgz#cc0670b2822b0338355bc1b3d2246a42b8166409"
+  integrity sha512-NO4XoryBng06jjw/qWEU2LhcLJr1tWkhpMam/H4eas/CDKMX/b2/Ylb6EI256Y7+FVPCawwSM1rrJNOpDiz+Lg==
   dependencies:
-    "@babel/helper-create-class-features-plugin" "^7.14.6"
+    "@babel/helper-create-class-features-plugin" "^7.16.0"
     "@babel/helper-plugin-utils" "^7.14.5"
-    "@babel/plugin-syntax-typescript" "^7.14.5"
-
-"@babel/plugin-transform-unicode-escapes@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74"
-  integrity sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.12.13"
+    "@babel/plugin-syntax-typescript" "^7.16.0"
 
-"@babel/plugin-transform-unicode-escapes@^7.16.7":
+"@babel/plugin-transform-unicode-escapes@^7.12.13", "@babel/plugin-transform-unicode-escapes@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.16.7.tgz#da8717de7b3287a2c6d659750c964f302b31ece3"
   integrity sha512-TAV5IGahIz3yZ9/Hfv35TV2xEm+kaBDaZQCn2S/hG9/CZ0DktxJv9eKfPc7yYCvOYR4JGx1h8C+jcSOvgaaI/Q==
   dependencies:
     "@babel/helper-plugin-utils" "^7.16.7"
 
-"@babel/plugin-transform-unicode-regex@^7.12.13":
-  version "7.12.13"
-  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz#b52521685804e155b1202e83fc188d34bb70f5ac"
-  integrity sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA==
-  dependencies:
-    "@babel/helper-create-regexp-features-plugin" "^7.12.13"
-    "@babel/helper-plugin-utils" "^7.12.13"
-
-"@babel/plugin-transform-unicode-regex@^7.16.7":
+"@babel/plugin-transform-unicode-regex@^7.12.13", "@babel/plugin-transform-unicode-regex@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2"
   integrity sha512-oC5tYYKw56HO75KZVLQ+R/Nl3Hro9kf8iG0hXoaHP7tjAyCpvqBiSNe6vGrZni1Z6MggmUOC6A7VP7AVmw225Q==
@@ -1672,18 +1314,7 @@
     core-js-compat "^3.20.2"
     semver "^6.3.0"
 
-"@babel/preset-modules@^0.1.4":
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e"
-  integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==
-  dependencies:
-    "@babel/helper-plugin-utils" "^7.0.0"
-    "@babel/plugin-proposal-unicode-property-regex" "^7.4.4"
-    "@babel/plugin-transform-dotall-regex" "^7.4.4"
-    "@babel/types" "^7.4.4"
-    esutils "^2.0.2"
-
-"@babel/preset-modules@^0.1.5":
+"@babel/preset-modules@^0.1.4", "@babel/preset-modules@^0.1.5":
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.5.tgz#ef939d6e7f268827e1841638dc6ff95515e115d9"
   integrity sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==
@@ -1719,18 +1350,18 @@
     "@babel/plugin-transform-react-pure-annotations" "^7.16.7"
 
 "@babel/preset-typescript@^7.8.3":
-  version "7.14.5"
-  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.14.5.tgz#aa98de119cf9852b79511f19e7f44a2d379bcce0"
-  integrity sha512-u4zO6CdbRKbS9TypMqrlGH7sd2TAJppZwn3c/ZRLeO/wGsbddxgbPDUZVNrie3JWYLQ9vpineKlsrWFvO6Pwkw==
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.16.0.tgz#b0b4f105b855fb3d631ec036cdc9d1ffd1fa5eac"
+  integrity sha512-txegdrZYgO9DlPbv+9QOVpMnKbOtezsLHWsnsRF4AjbSIsVaujrq1qg8HK0mxQpWv0jnejt0yEoW1uWpvbrDTg==
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/helper-validator-option" "^7.14.5"
-    "@babel/plugin-transform-typescript" "^7.14.5"
+    "@babel/plugin-transform-typescript" "^7.16.0"
 
 "@babel/runtime@^7.0.0", "@babel/runtime@^7.12.0", "@babel/runtime@^7.13.10", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
-  version "7.14.0"
-  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
-  integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.0.tgz#e27b977f2e2088ba24748bf99b5e1dece64e4f0b"
+  integrity sha512-Nht8L0O8YCktmsDV6FqFue7vQLRx3Hb0B37lS5y0jDRqRxlBG4wIJHnf9/bgSE2UyipKFA01YtS+npRdTWBUyw==
   dependencies:
     regenerator-runtime "^0.13.4"
 
@@ -1748,7 +1379,7 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
-"@babel/template@^7.12.13", "@babel/template@^7.14.5":
+"@babel/template@^7.12.13":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
   integrity sha512-6Z3Po85sfxRGachLULUhOmvAaOo7xCvqGQtxINai2mEGPFm6pQ4z5QInFnUrRpfoSV60BnjyF5F3c+15fxFV1g==
@@ -1757,7 +1388,7 @@
     "@babel/parser" "^7.14.5"
     "@babel/types" "^7.14.5"
 
-"@babel/template@^7.16.7":
+"@babel/template@^7.14.5", "@babel/template@^7.16.7":
   version "7.16.7"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
   integrity sha512-I8j/x8kHUrbYRTUxXrrMbfCa7jxkE7tZre39x3kjr9hvI82cK1FfqLygotcWN5kdPGWcLdWMHpSBavse5tWw3w==
@@ -1766,7 +1397,16 @@
     "@babel/parser" "^7.16.7"
     "@babel/types" "^7.16.7"
 
-"@babel/traverse@^7.13.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.4.5":
+"@babel/template@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.0.tgz#d16a35ebf4cd74e202083356fab21dd89363ddd6"
+  integrity sha512-MnZdpFD/ZdYhXwiunMqqgyZyucaYsbL0IrjoGjaVhGilz+x8YB++kRfygSOIj1yOtWKPlx7NBp+9I1RQSgsd5A==
+  dependencies:
+    "@babel/code-frame" "^7.16.0"
+    "@babel/parser" "^7.16.0"
+    "@babel/types" "^7.16.0"
+
+"@babel/traverse@^7.13.0", "@babel/traverse@^7.4.5":
   version "7.14.7"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.7.tgz#64007c9774cfdc3abd23b0780bc18a3ce3631753"
   integrity sha512-9vDr5NzHu27wgwejuKL7kIOm4bwEtaPQ4Z6cpCmjSuaRqpH/7xc4qcGEscwMqlkwgcXl6MvqoAjZkQ24uSdIZQ==
@@ -1781,6 +1421,21 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
+"@babel/traverse@^7.16.0":
+  version "7.16.0"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.0.tgz#965df6c6bfc0a958c1e739284d3c9fa4a6e3c45b"
+  integrity sha512-qQ84jIs1aRQxaGaxSysII9TuDaguZ5yVrEuC0BN2vcPlalwfLovVmCjbFDPECPXcYM/wLvNFfp8uDOliLxIoUQ==
+  dependencies:
+    "@babel/code-frame" "^7.16.0"
+    "@babel/generator" "^7.16.0"
+    "@babel/helper-function-name" "^7.16.0"
+    "@babel/helper-hoist-variables" "^7.16.0"
+    "@babel/helper-split-export-declaration" "^7.16.0"
+    "@babel/parser" "^7.16.0"
+    "@babel/types" "^7.16.0"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
 "@babel/traverse@^7.16.7", "@babel/traverse@^7.16.8", "@babel/traverse@^7.17.0":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.17.0.tgz#3143e5066796408ccc880a33ecd3184f3e75cd30"
@@ -1797,15 +1452,7 @@
     debug "^4.1.0"
     globals "^11.1.0"
 
-"@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.13.12", "@babel/types@^7.14.5", "@babel/types@^7.4.4":
-  version "7.15.4"
-  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.4.tgz#74eeb86dbd6748d2741396557b9860e57fce0a0d"
-  integrity sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==
-  dependencies:
-    "@babel/helper-validator-identifier" "^7.14.9"
-    to-fast-properties "^2.0.0"
-
-"@babel/types@^7.12.6", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0":
+"@babel/types@^7.12.6", "@babel/types@^7.13.12", "@babel/types@^7.16.0", "@babel/types@^7.16.7", "@babel/types@^7.16.8", "@babel/types@^7.17.0":
   version "7.17.0"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b"
   integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==
@@ -1813,6 +1460,14 @@
     "@babel/helper-validator-identifier" "^7.16.7"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.13.0", "@babel/types@^7.14.5", "@babel/types@^7.4.4":
+  version "7.15.4"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.15.4.tgz#74eeb86dbd6748d2741396557b9860e57fce0a0d"
+  integrity sha512-0f1HJFuGmmbrKTCZtbm3cU+b/AqdEYk5toj5iQur58xkVMlS0JWaKxTBSmCXd47uiN7vbcozAupm6Mvs80GNhw==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.14.9"
+    to-fast-properties "^2.0.0"
+
 "@colors/colors@1.5.0":
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -1831,9 +1486,9 @@
     "@date-io/core" "^1.3.13"
 
 "@discoveryjs/json-ext@^0.5.0":
-  version "0.5.3"
-  resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
-  integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==
+  version "0.5.5"
+  resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3"
+  integrity sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA==
 
 "@emotion/cache@^10.0.27":
   version "10.0.29"
@@ -1845,16 +1500,16 @@
     "@emotion/utils" "0.11.3"
     "@emotion/weak-memoize" "0.2.5"
 
-"@emotion/cache@^11.4.0":
-  version "11.4.0"
-  resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.4.0.tgz#293fc9d9a7a38b9aad8e9337e5014366c3b09ac0"
-  integrity sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==
+"@emotion/cache@^11.4.0", "@emotion/cache@^11.5.0":
+  version "11.5.0"
+  resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.5.0.tgz#a5eb78cbef8163939ee345e3ddf0af217b845e62"
+  integrity sha512-mAZ5QRpLriBtaj/k2qyrXwck6yeoz1V5lMt/jfj6igWU35yYlNKs2LziXVgvH81gnJZ+9QQNGelSsnuoAy6uIw==
   dependencies:
     "@emotion/memoize" "^0.7.4"
-    "@emotion/sheet" "^1.0.0"
+    "@emotion/sheet" "^1.0.3"
     "@emotion/utils" "^1.0.0"
     "@emotion/weak-memoize" "^0.2.5"
-    stylis "^4.0.3"
+    stylis "^4.0.10"
 
 "@emotion/cache@^11.7.1":
   version "11.7.1"
@@ -1918,14 +1573,14 @@
   integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
 
 "@emotion/react@^11.1.1", "@emotion/react@^11.1.5":
-  version "11.4.0"
-  resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.4.0.tgz#2465ad7b073a691409b88dfd96dc17097ddad9b7"
-  integrity sha512-4XklWsl9BdtatLoJpSjusXhpKv9YVteYKh9hPKP1Sxl+mswEFoUe0WtmtWjxEjkA51DQ2QRMCNOvKcSlCQ7ivg==
+  version "11.5.0"
+  resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.5.0.tgz#19b5771bbfbda5e8517e948a2d9064810f0022bd"
+  integrity sha512-MYq/bzp3rYbee4EMBORCn4duPQfgpiEB5XzrZEBnUZAL80Qdfr7CEv/T80jwaTl/dnZmt9SnTa8NkTrwFNpLlw==
   dependencies:
     "@babel/runtime" "^7.13.10"
-    "@emotion/cache" "^11.4.0"
+    "@emotion/cache" "^11.5.0"
     "@emotion/serialize" "^1.0.2"
-    "@emotion/sheet" "^1.0.1"
+    "@emotion/sheet" "^1.0.3"
     "@emotion/utils" "^1.0.0"
     "@emotion/weak-memoize" "^0.2.5"
     hoist-non-react-statics "^3.3.1"
@@ -1957,10 +1612,10 @@
   resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-0.9.4.tgz#894374bea39ec30f489bbfc3438192b9774d32e5"
   integrity sha512-zM9PFmgVSqBw4zL101Q0HrBVTGmpAxFZH/pYx/cjJT5advXguvcgjHFTCaIO3enL/xr89vK2bh0Mfyj9aa0ANA==
 
-"@emotion/sheet@^1.0.0", "@emotion/sheet@^1.0.1":
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.1.tgz#245f54abb02dfd82326e28689f34c27aa9b2a698"
-  integrity sha512-GbIvVMe4U+Zc+929N1V7nW6YYJtidj31lidSmdYcWozwoBIObXBnaJkKNDjZrLm9Nc0BR+ZyHNaRZxqNZbof5g==
+"@emotion/sheet@^1.0.1", "@emotion/sheet@^1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.0.3.tgz#00c326cd7985c5ccb8fe2c1b592886579dcfab8f"
+  integrity sha512-YoX5GyQ4db7LpbmXHMuc8kebtBGP6nZfRC5Z13OKJMixBEwdZrJ914D6yJv/P+ZH/YY3F5s89NYX2hlZAf3SRQ==
 
 "@emotion/sheet@^1.1.0":
   version "1.1.0"
@@ -2010,15 +1665,15 @@
   resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
   integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
 
-"@eslint/eslintrc@^0.4.0":
-  version "0.4.0"
-  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.0.tgz#99cc0a0584d72f1df38b900fb062ba995f395547"
-  integrity sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==
+"@eslint/eslintrc@^0.4.3":
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.3.tgz#9e42981ef035beb3dd49add17acb96e8ff6f394c"
+  integrity sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==
   dependencies:
     ajv "^6.12.4"
     debug "^4.1.1"
     espree "^7.3.0"
-    globals "^12.1.0"
+    globals "^13.9.0"
     ignore "^4.0.6"
     import-fresh "^3.2.1"
     js-yaml "^3.13.1"
@@ -2035,6 +1690,20 @@
   resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
   integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
 
+"@humanwhocodes/config-array@^0.5.0":
+  version "0.5.0"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9"
+  integrity sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==
+  dependencies:
+    "@humanwhocodes/object-schema" "^1.2.0"
+    debug "^4.1.1"
+    minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.0":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+  integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
 "@istanbuljs/schema@^0.1.2":
   version "0.1.3"
   resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
@@ -2090,9 +1759,9 @@
     react-is "^16.8.0 || ^17.0.0"
 
 "@material-ui/pickers@^3.2.10":
-  version "3.2.10"
-  resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae"
-  integrity sha512-B8G6Obn5S3RCl7hwahkQj9sKUapwXWFjiaz/Bsw1fhYFdNMnDUolRiWQSoKPb1/oKe37Dtfszoywi1Ynbo3y8w==
+  version "3.3.10"
+  resolved "https://registry.yarnpkg.com/@material-ui/pickers/-/pickers-3.3.10.tgz#f1b0f963348cc191645ef0bdeff7a67c6aa25485"
+  integrity sha512-hS4pxwn1ZGXVkmgD4tpFpaumUaAg2ZzbTrxltfC5yPw4BJV+mGkfnQOB4VpWEYZw2jv65Z0wLwDE/piQiPPZ3w==
   dependencies:
     "@babel/runtime" "^7.6.0"
     "@date-io/core" "1.x"
@@ -2234,25 +1903,25 @@
     prop-types "^15.7.2"
     react-is "^17.0.2"
 
-"@nodelib/fs.scandir@2.1.4":
-  version "2.1.4"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69"
-  integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA==
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
   dependencies:
-    "@nodelib/fs.stat" "2.0.4"
+    "@nodelib/fs.stat" "2.0.5"
     run-parallel "^1.1.9"
 
-"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2":
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655"
-  integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q==
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
 
 "@nodelib/fs.walk@^1.2.3":
-  version "1.2.6"
-  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063"
-  integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow==
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
   dependencies:
-    "@nodelib/fs.scandir" "2.1.4"
+    "@nodelib/fs.scandir" "2.1.5"
     fastq "^1.6.0"
 
 "@npmcli/fs@^1.0.0":
@@ -2271,20 +1940,20 @@
     mkdirp "^1.0.4"
     rimraf "^3.0.2"
 
-"@polka/url@^1.0.0-next.15":
-  version "1.0.0-next.15"
-  resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.15.tgz#6a9d143f7f4f49db2d782f9e1c8839a29b43ae23"
-  integrity sha512-15spi3V28QdevleWBNXE4pIls3nFZmBbUGrW9IVPwiQczuSb9n76TCB4bsk8TSel+I1OkHEdPhu5QKMfY6rQHA==
+"@polka/url@^1.0.0-next.20":
+  version "1.0.0-next.21"
+  resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1"
+  integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==
 
 "@popperjs/core@^2.11.4":
   version "2.11.4"
   resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503"
   integrity sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==
 
-"@popperjs/core@^2.8.3":
-  version "2.9.0"
-  resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.0.tgz#32e63212293dd3efbb521cd35a5020ab66eaa546"
-  integrity sha512-wjtKehFAIARq2OxK8j3JrggNlEslJfNuSm2ArteIbKyRMts2g0a7KzTxfRVNUM+O0gnBJ2hNV8nWPOYBgI1sew==
+"@popperjs/core@^2.9.0":
+  version "2.10.2"
+  resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590"
+  integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==
 
 "@projectstorm/geometry@^6.6.1":
   version "6.6.1"
@@ -2331,12 +2000,17 @@
     "@projectstorm/react-diagrams-defaults" "^6.6.1"
     "@projectstorm/react-diagrams-routing" "^6.6.1"
 
+"@react-leaflet/core@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@react-leaflet/core/-/core-1.1.1.tgz#827fd05bb542cf874116176d8ef48d5b12163f81"
+  integrity sha512-7PGLWa9MZ5x/cWy8EH2VzI4T8q5WpuHbixzCDXqixP/WyqwIrg5NDUPgYuFnB4IEIZF+6nA265mYzswFo/h1Pw==
+
 "@simonwep/pickr@^1.5.1":
-  version "1.8.1"
-  resolved "https://registry.yarnpkg.com/@simonwep/pickr/-/pickr-1.8.1.tgz#e136cbd9c345ddbb7d71eb14af544c798165d495"
-  integrity sha512-3Q5+INWW0Py+/E9hgy0cyD0/0w/yGZbkxam6RzFVFDOEHgAqMVJR+x9znx58/ky/ZIvE/78FbH189yIC9h111A==
+  version "1.8.2"
+  resolved "https://registry.yarnpkg.com/@simonwep/pickr/-/pickr-1.8.2.tgz#96dc86675940d7cad63d69c22083dd1cbb9797cb"
+  integrity sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==
   dependencies:
-    core-js "^3.12.1"
+    core-js "^3.15.1"
     nanopop "^2.1.0"
 
 "@sindresorhus/is@^0.7.0":
@@ -2349,6 +2023,11 @@
   resolved "https://registry.yarnpkg.com/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#568d9beae00b0d835f4f8c53fd55714986492e61"
   integrity sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ==
 
+"@socket.io/component-emitter@~3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.0.0.tgz#8863915676f837d9dad7b76f50cb500c1e9422e9"
+  integrity sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==
+
 "@sphinxxxx/color-conversion@^2.2.2":
   version "2.2.2"
   resolved "https://registry.yarnpkg.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz#03ecc29279e3c0c832f6185a5bfa3497858ac8ca"
@@ -2466,9 +2145,9 @@
     react-transition-state "^1.1.3"
 
 "@tippyjs/react@^4.2.0":
-  version "4.2.3"
-  resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.3.tgz#c2dbe9eb71bd9ffa3974f9ba8e3a4a6fc1538aed"
-  integrity sha512-44vBapqROQI7Q5nDtX1MMAgcAV+3DsIi+m/45CxQ72C5LDNmNDq9h3f04x3NHMrUhWcfgfgjYA2EmeLSH/4eRg==
+  version "4.2.6"
+  resolved "https://registry.yarnpkg.com/@tippyjs/react/-/react-4.2.6.tgz#971677a599bf663f20bb1c60a62b9555b749cc71"
+  integrity sha512-91RicDR+H7oDSyPycI13q3b7o4O60wa2oRbjlz2fyRLmHImc4vyDwuUP8NtZaN0VARJY5hybvDYrFzhY9+Lbyw==
   dependencies:
     tippy.js "^6.3.1"
 
@@ -2490,9 +2169,9 @@
     classnames "*"
 
 "@types/component-emitter@^1.2.10":
-  version "1.2.10"
-  resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.10.tgz#ef5b1589b9f16544642e473db5ea5639107ef3ea"
-  integrity sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==
+  version "1.2.11"
+  resolved "https://registry.yarnpkg.com/@types/component-emitter/-/component-emitter-1.2.11.tgz#50d47d42b347253817a39709fef03ce66a108506"
+  integrity sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ==
 
 "@types/cookie@^0.4.1":
   version "0.4.1"
@@ -2505,68 +2184,63 @@
   integrity sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==
 
 "@types/eslint-scope@^3.7.0":
-  version "3.7.0"
-  resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.0.tgz#4792816e31119ebd506902a482caec4951fabd86"
-  integrity sha512-O/ql2+rrCUe2W2rs7wMR+GqPRcgB6UiqN5RhrR5xruFlY7l9YLMn0ZkDzjoHLeiFkR8MCQZVudUuuvQ2BLC9Qw==
+  version "3.7.1"
+  resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.1.tgz#8dc390a7b4f9dd9f1284629efce982e41612116e"
+  integrity sha512-SCFeogqiptms4Fg29WpOTk5nHIzfpKCemSN63ksBQYKTcXoJEmJagV+DhVmbapZzY4/5YaOV1nZwrsU79fFm1g==
   dependencies:
     "@types/eslint" "*"
     "@types/estree" "*"
 
 "@types/eslint@*":
-  version "7.2.7"
-  resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.2.7.tgz#f7ef1cf0dceab0ae6f9a976a0a9af14ab1baca26"
-  integrity sha512-EHXbc1z2GoQRqHaAT7+grxlTJ3WE2YNeD6jlpPoRc83cCoThRY+NUWjCUZaYmk51OICkPXn2hhphcWcWXgNW0Q==
+  version "7.28.2"
+  resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-7.28.2.tgz#0ff2947cdd305897c52d5372294e8c76f351db68"
+  integrity sha512-KubbADPkfoU75KgKeKLsFHXnU4ipH7wYg0TRT33NK3N3yiu7jlFAAoygIWBV+KbuHx/G+AvuGX6DllnK35gfJA==
   dependencies:
     "@types/estree" "*"
     "@types/json-schema" "*"
 
-"@types/estree@*", "@types/estree@^0.0.46":
-  version "0.0.46"
-  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.46.tgz#0fb6bfbbeabd7a30880504993369c4bf1deab1fe"
-  integrity sha512-laIjwTQaD+5DukBZaygQ79K1Z0jb1bPEMRrkXSLjtCcZm+abyp5YbrqpSLzD42FwWW6gK/aS4NYpJ804nG2brg==
+"@types/estree@*", "@types/estree@^0.0.50":
+  version "0.0.50"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
+  integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
 
 "@types/glob@^7.1.1":
-  version "7.1.3"
-  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183"
-  integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
+  integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==
   dependencies:
     "@types/minimatch" "*"
     "@types/node" "*"
 
-"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6":
-  version "7.0.7"
-  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
-  integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
+"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8":
+  version "7.0.9"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
+  integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
 
 "@types/minimatch@*":
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.4.tgz#f0ec25dbf2f0e4b18647313ac031134ca5b24b21"
-  integrity sha512-1z8k4wzFnNjVK/tlxvrWuK5WMt6mydWWP7+zvH5eFep4oj+UkrfiJTRtjCeBXNpwaA/FYqqtb4/QS4ianFpIRA==
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40"
+  integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==
 
 "@types/node@*", "@types/node@>=10.0.0":
-  version "14.14.32"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.32.tgz#90c5c4a8d72bbbfe53033f122341343249183448"
-  integrity sha512-/Ctrftx/zp4m8JOujM5ZhwzlWLx22nbQJiVqz8/zE15gOeEW+uly3FSX4fGFpcfEvFzXcMCJwq9lGVWgyARXhg==
+  version "16.11.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.6.tgz#6bef7a2a0ad684cf6e90fcfe31cecabd9ce0a3ae"
+  integrity sha512-ua7PgUoeQFjmWPcoo9khiPum3Pd60k4/2ZGXt18sm2Slk0W0xZTqt5Y0Ny1NyBiN1EVQ/+FaF9NcY4Qe6rwk5w==
 
 "@types/parse-json@^4.0.0":
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
   integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
 
-"@types/prop-types@*":
-  version "15.7.3"
-  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7"
-  integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==
-
-"@types/prop-types@^15.7.4":
+"@types/prop-types@*", "@types/prop-types@^15.7.4":
   version "15.7.4"
   resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
   integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
 
 "@types/q@^1.5.1":
-  version "1.5.4"
-  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24"
-  integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==
+  version "1.5.5"
+  resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df"
+  integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ==
 
 "@types/react-dom@^16.0.11":
   version "16.9.14"
@@ -2596,7 +2270,7 @@
   dependencies:
     "@types/react" "*"
 
-"@types/react@*", "@types/react@^16":
+"@types/react@*":
   version "16.14.10"
   resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.10.tgz#76bc1c42ed5ab0d2ab13e5c58faaccaad3449477"
   integrity sha512-QadBsMyF6ldjEAXEhsmEW/L0uBDJT8yw7Qoe5sRnEKVrzMkiYoJwqoL5TKJOlArsn/wvIJM/XdVzkdL6+AS64Q==
@@ -2605,24 +2279,24 @@
     "@types/scheduler" "*"
     csstype "^3.0.2"
 
-"@types/react@^16.7.18":
-  version "16.14.18"
-  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.18.tgz#b2bcea05ee244fde92d409f91bd888ca8e54b20f"
-  integrity sha512-eeyqd1mqoG43mI0TvNKy9QNf1Tjz3DEOsRP3rlPo35OeMIt05I+v9RR8ZvL2GuYZeF2WAcLXJZMzu6zdz3VbtQ==
+"@types/react@^16", "@types/react@^16.7.18":
+  version "16.14.20"
+  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.20.tgz#ff6e932ad71d92c27590e4a8667c7a53a7d0baad"
+  integrity sha512-SV7TaVc8e9E/5Xuv6TIyJ5VhQpZoVFJqX6IZgj5HZoFCtIDCArE3qXkcHlc6O/Ud4UwcMoX+tlvDA95YrKdLgA==
   dependencies:
     "@types/prop-types" "*"
     "@types/scheduler" "*"
     csstype "^3.0.2"
 
 "@types/scheduler@*":
-  version "0.16.1"
-  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
-  integrity sha512-EaCxbanVeyxDRTQBkdLb3Bvl/HK7PBK6UJjsSixB0iHKoWxE5uu2Q/DgtpOhPIojN0Zl1whvOd7PoHs2P0s5eA==
+  version "0.16.2"
+  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
+  integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
 
 "@types/styled-jsx@^2.2.8":
-  version "2.2.8"
-  resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.8.tgz#b50d13d8a3c34036282d65194554cf186bab7234"
-  integrity sha512-Yjye9VwMdYeXfS71ihueWRSxrruuXTwKCbzue4+5b2rjnQ//AtyM7myZ1BEhNhBQ/nL/RE7bdToUoLln2miKvg==
+  version "2.2.9"
+  resolved "https://registry.yarnpkg.com/@types/styled-jsx/-/styled-jsx-2.2.9.tgz#e50b3f868c055bcbf9bc353eca6c10fdad32a53f"
+  integrity sha512-W/iTlIkGEyTBGTEvZCey8EgQlQ5l0DwMqi3iOXlLs2kyBwYTXHKEiU6IZ5EwoRwngL8/dGYuzezSup89ttVHLw==
   dependencies:
     "@types/react" "*"
 
@@ -2642,143 +2316,143 @@
     underscore "^1.9.1"
     url-join "^4.0.0"
 
-"@webassemblyjs/ast@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.0.tgz#a5aa679efdc9e51707a4207139da57920555961f"
-  integrity sha512-kX2W49LWsbthrmIRMbQZuQDhGtjyqXfEmmHyEi4XWnSZtPmxY0+3anPIzsnRb45VH/J55zlOfWvZuY47aJZTJg==
+"@webassemblyjs/ast@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7"
+  integrity sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==
   dependencies:
-    "@webassemblyjs/helper-numbers" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
+    "@webassemblyjs/helper-numbers" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
 
-"@webassemblyjs/floating-point-hex-parser@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.0.tgz#34d62052f453cd43101d72eab4966a022587947c"
-  integrity sha512-Q/aVYs/VnPDVYvsCBL/gSgwmfjeCb4LW8+TMrO3cSzJImgv8lxxEPM2JA5jMrivE7LSz3V+PFqtMbls3m1exDA==
+"@webassemblyjs/floating-point-hex-parser@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz#f6c61a705f0fd7a6aecaa4e8198f23d9dc179e4f"
+  integrity sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==
 
-"@webassemblyjs/helper-api-error@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.0.tgz#aaea8fb3b923f4aaa9b512ff541b013ffb68d2d4"
-  integrity sha512-baT/va95eXiXb2QflSx95QGT5ClzWpGaa8L7JnJbgzoYeaA27FCvuBXU758l+KXWRndEmUXjP0Q5fibhavIn8w==
+"@webassemblyjs/helper-api-error@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz#1a63192d8788e5c012800ba6a7a46c705288fd16"
+  integrity sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==
 
-"@webassemblyjs/helper-buffer@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.0.tgz#d026c25d175e388a7dbda9694e91e743cbe9b642"
-  integrity sha512-u9HPBEl4DS+vA8qLQdEQ6N/eJQ7gT7aNvMIo8AAWvAl/xMrcOSiI2M0MAnMCy3jIFke7bEee/JwdX1nUpCtdyA==
+"@webassemblyjs/helper-buffer@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz#832a900eb444884cde9a7cad467f81500f5e5ab5"
+  integrity sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==
 
-"@webassemblyjs/helper-numbers@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.0.tgz#7ab04172d54e312cc6ea4286d7d9fa27c88cd4f9"
-  integrity sha512-DhRQKelIj01s5IgdsOJMKLppI+4zpmcMQ3XboFPLwCpSNH6Hqo1ritgHgD0nqHeSYqofA6aBN/NmXuGjM1jEfQ==
+"@webassemblyjs/helper-numbers@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz#64d81da219fbbba1e3bd1bfc74f6e8c4e10a62ae"
+  integrity sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==
   dependencies:
-    "@webassemblyjs/floating-point-hex-parser" "1.11.0"
-    "@webassemblyjs/helper-api-error" "1.11.0"
+    "@webassemblyjs/floating-point-hex-parser" "1.11.1"
+    "@webassemblyjs/helper-api-error" "1.11.1"
     "@xtuc/long" "4.2.2"
 
-"@webassemblyjs/helper-wasm-bytecode@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.0.tgz#85fdcda4129902fe86f81abf7e7236953ec5a4e1"
-  integrity sha512-MbmhvxXExm542tWREgSFnOVo07fDpsBJg3sIl6fSp9xuu75eGz5lz31q7wTLffwL3Za7XNRCMZy210+tnsUSEA==
+"@webassemblyjs/helper-wasm-bytecode@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz#f328241e41e7b199d0b20c18e88429c4433295e1"
+  integrity sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==
 
-"@webassemblyjs/helper-wasm-section@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.0.tgz#9ce2cc89300262509c801b4af113d1ca25c1a75b"
-  integrity sha512-3Eb88hcbfY/FCukrg6i3EH8H2UsD7x8Vy47iVJrP967A9JGqgBVL9aH71SETPx1JrGsOUVLo0c7vMCN22ytJew==
+"@webassemblyjs/helper-wasm-section@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz#21ee065a7b635f319e738f0dd73bfbda281c097a"
+  integrity sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==
   dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
 
-"@webassemblyjs/ieee754@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.0.tgz#46975d583f9828f5d094ac210e219441c4e6f5cf"
-  integrity sha512-KXzOqpcYQwAfeQ6WbF6HXo+0udBNmw0iXDmEK5sFlmQdmND+tr773Ti8/5T/M6Tl/413ArSJErATd8In3B+WBA==
+"@webassemblyjs/ieee754@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz#963929e9bbd05709e7e12243a099180812992614"
+  integrity sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==
   dependencies:
     "@xtuc/ieee754" "^1.2.0"
 
-"@webassemblyjs/leb128@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.0.tgz#f7353de1df38aa201cba9fb88b43f41f75ff403b"
-  integrity sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==
+"@webassemblyjs/leb128@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.1.tgz#ce814b45574e93d76bae1fb2644ab9cdd9527aa5"
+  integrity sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==
   dependencies:
     "@xtuc/long" "4.2.2"
 
-"@webassemblyjs/utf8@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.0.tgz#86e48f959cf49e0e5091f069a709b862f5a2cadf"
-  integrity sha512-A/lclGxH6SpSLSyFowMzO/+aDEPU4hvEiooCMXQPcQFPPJaYcPQNKGOCLUySJsYJ4trbpr+Fs08n4jelkVTGVw==
-
-"@webassemblyjs/wasm-edit@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.0.tgz#ee4a5c9f677046a210542ae63897094c2027cb78"
-  integrity sha512-JHQ0damXy0G6J9ucyKVXO2j08JVJ2ntkdJlq1UTiUrIgfGMmA7Ik5VdC/L8hBK46kVJgujkBIoMtT8yVr+yVOQ==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/helper-wasm-section" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
-    "@webassemblyjs/wasm-opt" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
-    "@webassemblyjs/wast-printer" "1.11.0"
-
-"@webassemblyjs/wasm-gen@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.0.tgz#3cdb35e70082d42a35166988dda64f24ceb97abe"
-  integrity sha512-BEUv1aj0WptCZ9kIS30th5ILASUnAPEvE3tVMTrItnZRT9tXCLW2LEXT8ezLw59rqPP9klh9LPmpU+WmRQmCPQ==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/ieee754" "1.11.0"
-    "@webassemblyjs/leb128" "1.11.0"
-    "@webassemblyjs/utf8" "1.11.0"
-
-"@webassemblyjs/wasm-opt@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.0.tgz#1638ae188137f4bb031f568a413cd24d32f92978"
-  integrity sha512-tHUSP5F4ywyh3hZ0+fDQuWxKx3mJiPeFufg+9gwTpYp324mPCQgnuVKwzLTZVqj0duRDovnPaZqDwoyhIO8kYg==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-buffer" "1.11.0"
-    "@webassemblyjs/wasm-gen" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
-
-"@webassemblyjs/wasm-parser@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.0.tgz#3e680b8830d5b13d1ec86cc42f38f3d4a7700754"
-  integrity sha512-6L285Sgu9gphrcpDXINvm0M9BskznnzJTE7gYkjDbxET28shDqp27wpruyx3C2S/dvEwiigBwLA1cz7lNUi0kw==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/helper-api-error" "1.11.0"
-    "@webassemblyjs/helper-wasm-bytecode" "1.11.0"
-    "@webassemblyjs/ieee754" "1.11.0"
-    "@webassemblyjs/leb128" "1.11.0"
-    "@webassemblyjs/utf8" "1.11.0"
-
-"@webassemblyjs/wast-printer@1.11.0":
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.0.tgz#680d1f6a5365d6d401974a8e949e05474e1fab7e"
-  integrity sha512-Fg5OX46pRdTgB7rKIUojkh9vXaVN6sGYCnEiJN1GYkb0RPwShZXp6KTDqmoMdQPKhcroOXh3fEzmkWmCYaKYhQ==
-  dependencies:
-    "@webassemblyjs/ast" "1.11.0"
+"@webassemblyjs/utf8@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.1.tgz#d1f8b764369e7c6e6bae350e854dec9a59f0a3ff"
+  integrity sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==
+
+"@webassemblyjs/wasm-edit@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz#ad206ebf4bf95a058ce9880a8c092c5dec8193d6"
+  integrity sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/helper-wasm-section" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
+    "@webassemblyjs/wasm-opt" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
+    "@webassemblyjs/wast-printer" "1.11.1"
+
+"@webassemblyjs/wasm-gen@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz#86c5ea304849759b7d88c47a32f4f039ae3c8f76"
+  integrity sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/ieee754" "1.11.1"
+    "@webassemblyjs/leb128" "1.11.1"
+    "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wasm-opt@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz#657b4c2202f4cf3b345f8a4c6461c8c2418985f2"
+  integrity sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-buffer" "1.11.1"
+    "@webassemblyjs/wasm-gen" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
+
+"@webassemblyjs/wasm-parser@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz#86ca734534f417e9bd3c67c7a1c75d8be41fb199"
+  integrity sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/helper-api-error" "1.11.1"
+    "@webassemblyjs/helper-wasm-bytecode" "1.11.1"
+    "@webassemblyjs/ieee754" "1.11.1"
+    "@webassemblyjs/leb128" "1.11.1"
+    "@webassemblyjs/utf8" "1.11.1"
+
+"@webassemblyjs/wast-printer@1.11.1":
+  version "1.11.1"
+  resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz#d0c73beda8eec5426f10ae8ef55cee5e7084c2f0"
+  integrity sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==
+  dependencies:
+    "@webassemblyjs/ast" "1.11.1"
     "@xtuc/long" "4.2.2"
 
-"@webpack-cli/configtest@^1.0.3":
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.0.3.tgz#204bcff87cda3ea4810881f7ea96e5f5321b87b9"
-  integrity sha512-WQs0ep98FXX2XBAfQpRbY0Ma6ADw8JR6xoIkaIiJIzClGOMqVRvPCWqndTxf28DgFopWan0EKtHtg/5W1h0Zkw==
+"@webpack-cli/configtest@^1.1.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.0.tgz#8342bef0badfb7dfd3b576f2574ab80c725be043"
+  integrity sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==
 
-"@webpack-cli/info@^1.2.4":
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.2.4.tgz#7381fd41c9577b2d8f6c2594fad397ef49ad5573"
-  integrity sha512-ogE2T4+pLhTTPS/8MM3IjHn0IYplKM4HbVNMCWA9N4NrdPzunwenpCsqKEXyejMfRu6K8mhauIPYf8ZxWG5O6g==
+"@webpack-cli/info@^1.4.0":
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.0.tgz#b9179c3227ab09cbbb149aa733475fcf99430223"
+  integrity sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==
   dependencies:
     envinfo "^7.7.3"
 
-"@webpack-cli/serve@^1.4.0":
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.4.0.tgz#f84fd07bcacefe56ce762925798871092f0f228e"
-  integrity sha512-xgT/HqJ+uLWGX+Mzufusl3cgjAcnqYYskaB7o0vRcwOEfuu6hMzSILQpnIzFMGsTaeaX4Nnekl+6fadLbl1/Vg==
+"@webpack-cli/serve@^1.6.0":
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.0.tgz#2c275aa05c895eccebbfc34cfb223c6e8bd591a2"
+  integrity sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==
 
 "@wojtekmaj/enzyme-adapter-react-17@^0.4.1":
   version "0.4.1"
@@ -2832,18 +2506,23 @@ accepts@~1.3.4:
     negotiator "0.6.2"
 
 ace-builds@^1.4.12:
-  version "1.4.12"
-  resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.12.tgz#888efa386e36f4345f40b5233fcc4fe4c588fae7"
-  integrity sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg==
+  version "1.4.13"
+  resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.4.13.tgz#186f42d3849ebcc6a48b93088a058489897514c1"
+  integrity sha512-SOLzdaQkY6ecPKYRDDg+MY1WoGgXA34cIvYJNNoBMGGUswHmlauU2Hy0UL96vW0Fs/LgFbMUjD+6vqzWTldIYQ==
 
 "acitree@git+https://github.com/imsurinder90/jquery-aciTree.git#rc.7":
   version "4.5.0-rc.7"
   resolved "git+https://github.com/imsurinder90/jquery-aciTree.git#24dcd7536a008abe25da6adb7bfde8eeb53892f1"
 
+acorn-import-assertions@^1.7.6:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
+  integrity sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==
+
 acorn-jsx@^5.3.1:
-  version "5.3.1"
-  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
-  integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
 
 acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.6.1:
   version "1.8.2"
@@ -2860,19 +2539,19 @@ acorn-walk@^7.0.0:
   integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
 
 acorn-walk@^8.0.0:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.1.0.tgz#d3c6a9faf00987a5e2b9bdb506c2aa76cd707f83"
-  integrity sha512-mjmzmv12YIG/G8JQdQuz2MUDShEJ6teYpT5bmWA4q7iwoGen8xtt3twF3OvzIUl+Q06aWIjvnwQUKvQ6TtMRjg==
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+  integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
 
 acorn@^7.0.0, acorn@^7.4.0:
   version "7.4.1"
   resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
   integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
 
-acorn@^8.0.4:
-  version "8.2.4"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0"
-  integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg==
+acorn@^8.0.4, acorn@^8.4.1:
+  version "8.5.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2"
+  integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==
 
 agent-base@6, agent-base@^6.0.2:
   version "6.0.2"
@@ -2944,9 +2623,9 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.12.6:
     uri-js "^4.2.2"
 
 ajv@^8.0.1:
-  version "8.5.0"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.5.0.tgz#695528274bcb5afc865446aa275484049a18ae4b"
-  integrity sha512-Y2l399Tt1AguU3BPRP9Fn4eN+Or+StUGWCUpbnFyXSo8NZ9S4uj+AG2pjs5apK+ZMOwYOz1+a+VKvKH7CudXgQ==
+  version "8.6.3"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.6.3.tgz#11a66527761dc3e9a3845ea775d2d3c0414e8764"
+  integrity sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==
   dependencies:
     fast-deep-equal "^3.1.1"
     json-schema-traverse "^1.0.0"
@@ -2982,7 +2661,7 @@ ansi-regex@^2.0.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
   integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
 
-ansi-regex@^5.0.0:
+ansi-regex@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
   integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
@@ -3006,7 +2685,7 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
   dependencies:
     color-convert "^2.0.1"
 
-anymatch@~3.1.1, anymatch@~3.1.2:
+anymatch@~3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
   integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -3051,16 +2730,16 @@ argparse@^2.0.1:
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
   integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
 
-array-includes@^3.1.2, array-includes@^3.1.3:
-  version "3.1.3"
-  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
-  integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==
+array-includes@^3.1.3:
+  version "3.1.4"
+  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9"
+  integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.2"
+    es-abstract "^1.19.1"
     get-intrinsic "^1.1.1"
-    is-string "^1.0.5"
+    is-string "^1.0.7"
 
 array-union@^2.1.0:
   version "2.1.0"
@@ -3068,34 +2747,35 @@ array-union@^2.1.0:
   integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
 
 array.prototype.filter@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.0.tgz#24d63e38983cdc6bf023a3c574b2f2a3f384c301"
-  integrity sha512-TfO1gz+tLm+Bswq0FBOXPqAchtCr2Rn48T8dLJoRFl8NoEosjZmzptmuo1X8aZBzZcqsR1W8U761tjACJtngTQ==
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.1.tgz#20688792acdb97a09488eaaee9eebbf3966aae21"
+  integrity sha512-Dk3Ty7N42Odk7PjU/Ci3zT4pLj20YvuVnneG/58ICM6bt4Ij5kZaJTVQ9TSaWaIECX2sFyz4KItkVZqHNnciqw==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0"
+    es-abstract "^1.19.0"
     es-array-method-boxes-properly "^1.0.0"
-    is-string "^1.0.5"
+    is-string "^1.0.7"
 
 array.prototype.find@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.1.tgz#3baca26108ca7affb08db06bf0be6cb3115a969c"
-  integrity sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA==
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.2.tgz#6abbd0c2573925d8094f7d23112306af8c16d534"
+  integrity sha512-00S1O4ewO95OmmJW7EesWfQlrCrLEL8kZ40w3+GkLX2yTt0m2ggcePPa2uHPJ9KUmJvwRq+lCV9bD8Yim23x/Q==
   dependencies:
+    call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.17.4"
+    es-abstract "^1.19.0"
 
 array.prototype.flat@^1.2.3:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123"
-  integrity sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13"
+  integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==
   dependencies:
-    call-bind "^1.0.0"
+    call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.1"
+    es-abstract "^1.19.0"
 
-array.prototype.flatmap@1.2.4, array.prototype.flatmap@^1.2.4:
+array.prototype.flatmap@1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
   integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==
@@ -3105,6 +2785,15 @@ array.prototype.flatmap@1.2.4, array.prototype.flatmap@^1.2.4:
     es-abstract "^1.18.0-next.1"
     function-bind "^1.1.1"
 
+array.prototype.flatmap@^1.2.4:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.5.tgz#908dc82d8a406930fdf38598d51e7411d18d4446"
+  integrity sha512-08u6rVyi1Lj7oqWbS9nUxliETrtIROT4XGTA4D/LWGten6E3ocm7cy9SIrmNHOL5XVbVuckUp3X6Xyg8/zpvHA==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+    es-abstract "^1.19.0"
+
 asn1.js@^5.2.0:
   version "5.4.1"
   resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
@@ -3116,9 +2805,9 @@ asn1.js@^5.2.0:
     safer-buffer "^2.1.0"
 
 aspen-core@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/aspen-core/-/aspen-core-1.0.4.tgz#a2d6a23c80303e73aaa836c124c116e447ba7a7d"
-  integrity sha512-mQ79JxHstB2rf47Zgw2yduAH9L47b+3bwQtpbEAKeSJsLTPe8X7lsQ6lLP3tFbS204TNILC5LxSkVWv45FXQYg==
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/aspen-core/-/aspen-core-1.0.5.tgz#7745def811f761bca49d475d9addc8a5dbb1c71c"
+  integrity sha512-iqWORNQeQTn6XHStl1uCQ4t1yMPMw/nSWygfXVAEflf8vAWs4vR2M2TqDEZGvyoTkrxIPONv+wyQQFDnN5QCkg==
   dependencies:
     notificar "^1.0.1"
     p-series "^1.1.0"
@@ -3165,34 +2854,35 @@ async@^2.1.4:
     lodash "^4.17.14"
 
 async@^3.2.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
-  integrity sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/async/-/async-3.2.2.tgz#2eb7671034bb2194d45d30e31e24ec7e7f9670cd"
+  integrity sha512-H0E+qZaDEfx/FY4t7iLRv1W2fFI6+pyCeTw1uN20AQPiwqwM6ojPxHxdLv4z8hi2DtnW9BOckSspLucW7pIE5g==
 
 autoprefixer@^10.2.4:
-  version "10.2.6"
-  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.2.6.tgz#aadd9ec34e1c98d403e01950038049f0eb252949"
-  integrity sha512-8lChSmdU6dCNMCQopIf4Pe5kipkAGj/fvTMslCsih0uHpOrXOPUEVOmYMMqmw3cekQkSD7EhIeuYl5y0BLdKqg==
+  version "10.4.0"
+  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.0.tgz#c3577eb32a1079a440ec253e404eaf1eb21388c8"
+  integrity sha512-7FdJ1ONtwzV1G43GDD0kpVMn/qbiNqyOPMFTX5nRffI+7vgWoFEc6DcXOxHJxrWNDXrZh18eDsZjvZGUljSRGA==
   dependencies:
-    browserslist "^4.16.6"
-    caniuse-lite "^1.0.30001230"
-    colorette "^1.2.2"
+    browserslist "^4.17.5"
+    caniuse-lite "^1.0.30001272"
     fraction.js "^4.1.1"
     normalize-range "^0.1.2"
+    picocolors "^1.0.0"
     postcss-value-parser "^4.1.0"
 
-available-typed-arrays@^1.0.2:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.4.tgz#9e0ae84ecff20caae6a94a1c3bc39b955649b7a9"
-  integrity sha512-SA5mXJWrId1TaQjfxUYghbqQ/hYioKmLJvPJyDuYRtXXenFNMjj4hSSt1Cf1xsuXSXrtxrVC5Ot4eU6cOtBDdA==
+available-typed-arrays@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
+  integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
 
 axios-mock-adapter@^1.17.0:
-  version "1.19.0"
-  resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.19.0.tgz#9d72e321a6c5418e1eff067aa99761a86c5188a4"
-  integrity sha512-D+0U4LNPr7WroiBDvWilzTMYPYTuZlbo6BI8YHZtj7wYQS8NkARlP9KBt8IWWHTQJ0q/8oZ0ClPBtKCCkx8cQg==
+  version "1.20.0"
+  resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.20.0.tgz#21f5b4b625306f43e8c05673616719da86e20dcb"
+  integrity sha512-shZRhTjLP0WWdcvHKf3rH3iW9deb3UdKbdnKUoHmmsnBhVXN3sjPJM6ZvQ2r/ywgvBVQrMnjrSyQab60G1sr2w==
   dependencies:
     fast-deep-equal "^3.1.3"
-    is-buffer "^2.0.3"
+    is-blob "^2.1.0"
+    is-buffer "^2.0.5"
 
 axios@^0.21.1:
   version "0.21.4"
@@ -3225,9 +2915,9 @@ babel-generator@^6.18.0:
     trim-right "^1.0.1"
 
 babel-loader@^8.1.0:
-  version "8.2.2"
-  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
-  integrity sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==
+  version "8.2.3"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.3.tgz#8986b40f1a64cacfcb4b8429320085ef68b1342d"
+  integrity sha512-n4Zeta8NC3QAsuyiizu0GkmRcQ6clkV9WFUnUf1iXP//IeSKbWjofW3UHyZVwlOB4y039YQKefawyTn64Zwbuw==
   dependencies:
     find-cache-dir "^3.3.1"
     loader-utils "^1.4.0"
@@ -3321,13 +3011,13 @@ babel-plugin-polyfill-regenerator@^0.3.0:
   dependencies:
     "@babel/helper-define-polyfill-provider" "^0.3.1"
 
-"babel-plugin-styled-components@>= 1":
-  version "1.12.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.12.0.tgz#1dec1676512177de6b827211e9eda5a30db4f9b9"
-  integrity sha512-FEiD7l5ZABdJPpLssKXjBUJMYqzbcNzBowfXDCdJhOpbhWiewapUaY+LZGT8R4Jg2TwOjGjG4RKeyrO5p9sBkA==
+"babel-plugin-styled-components@>= 1.12.0":
+  version "1.13.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-styled-components/-/babel-plugin-styled-components-1.13.3.tgz#1f1cb3927d4afa1e324695c78f690900e3d075bc"
+  integrity sha512-meGStRGv+VuKA/q0/jXxrPNWEm4LPfYIqxooDTdmh8kFsP/Ph7jJG5rUPwUPX3QHUvggwdbgdGpo88P/rRYsVw==
   dependencies:
-    "@babel/helper-annotate-as-pure" "^7.0.0"
-    "@babel/helper-module-imports" "^7.0.0"
+    "@babel/helper-annotate-as-pure" "^7.15.4"
+    "@babel/helper-module-imports" "^7.15.4"
     babel-plugin-syntax-jsx "^6.18.0"
     lodash "^4.17.11"
 
@@ -3449,16 +3139,16 @@ balanced-match@^1.0.0:
   resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
   integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
 
-base64-arraybuffer@0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812"
-  integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=
-
 base64-arraybuffer@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45"
   integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ==
 
+base64-arraybuffer@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz#87bd13525626db4a9838e00a508c2b73efcf348c"
+  integrity sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA==
+
 base64-js@^1.0.2, base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@@ -3596,9 +3286,9 @@ bootstrap4-toggle@^3.6.1:
   integrity sha512-eRejcTc9YurhZ64nHY9Ii9DQn+F9/R74H9RPoeANVM3N1+C2lZ2tUuFCx1w3orOJ1y/iG4A7iCwdDZphMDIrYg==
 
 bootstrap@^4.3.1, bootstrap@^4.5.2:
-  version "4.6.0"
-  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.0.tgz#97b9f29ac98f98dfa43bf7468262d84392552fd7"
-  integrity sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==
+  version "4.6.1"
+  resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.1.tgz#bc25380c2c14192374e8dec07cf01b2742d222a2"
+  integrity sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==
 
 bowser@^2.11.0:
   version "2.11.0"
@@ -3772,16 +3462,16 @@ browserify@^17.0.0:
     vm-browserify "^1.0.0"
     xtend "^4.0.0"
 
-browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.3, browserslist@^4.16.6:
-  version "4.16.6"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.6.tgz#d7901277a5a88e554ed305b183ec9b0c08f66fa2"
-  integrity sha512-Wspk/PqO+4W9qp5iUTJsa1B/QrYn1keNCcEP5OvP7WBwT4KaDly0uONYmC6Xa3Z5IqnUgS0KcgLYu1l74x0ZXQ==
+browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.16.0, browserslist@^4.16.6:
+  version "4.17.6"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d"
+  integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==
   dependencies:
-    caniuse-lite "^1.0.30001219"
-    colorette "^1.2.2"
-    electron-to-chromium "^1.3.723"
+    caniuse-lite "^1.0.30001274"
+    electron-to-chromium "^1.3.886"
     escalade "^3.1.1"
-    node-releases "^1.1.71"
+    node-releases "^2.0.1"
+    picocolors "^1.0.0"
 
 browserslist@^4.17.5, browserslist@^4.19.1:
   version "4.19.1"
@@ -3818,9 +3508,9 @@ buffer-fill@^1.0.0:
   integrity sha1-+PeLdniYiO858gXNY39o5wISKyw=
 
 buffer-from@^1.0.0:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
-  integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
 
 buffer-xor@^1.0.3:
   version "1.0.3"
@@ -3943,11 +3633,16 @@ caniuse-api@^3.0.0:
     lodash.memoize "^4.1.2"
     lodash.uniq "^4.5.0"
 
-caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001219, caniuse-lite@^1.0.30001230, caniuse-lite@^1.0.30001286:
+caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001286:
   version "1.0.30001309"
   resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001309.tgz"
   integrity sha512-Pl8vfigmBXXq+/yUz1jUwULeq9xhMJznzdc/xwl4WclDAuebcTHVefpz8lE/bMI+UN7TOkSSe7B7RnZd6+dzjA==
 
+caniuse-lite@^1.0.30001272, caniuse-lite@^1.0.30001274:
+  version "1.0.30001278"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz#51cafc858df77d966b17f59b5839250b24417fff"
+  integrity sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==
+
 caw@^2.0.0, caw@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95"
@@ -3979,9 +3674,9 @@ chalk@^2.0.0, chalk@^2.4.1, chalk@^2.4.2:
     supports-color "^5.3.0"
 
 chalk@^4.0.0, chalk@^4.1.0:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
-  integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
   dependencies:
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
@@ -4009,44 +3704,44 @@ chartjs-color@^2.1.0:
     chartjs-color-string "^0.6.0"
     color-convert "^1.9.3"
 
-cheerio-select@^1.4.0:
-  version "1.4.0"
-  resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.4.0.tgz#3a16f21e37a2ef0f211d6d1aa4eff054bb22cdc9"
-  integrity sha512-sobR3Yqz27L553Qa7cK6rtJlMDbiKPdNywtR95Sj/YgfpLfy0u6CGJuaBKe5YE/vTc23SCRKxWSdlon/w6I/Ew==
+cheerio-select@^1.5.0:
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.5.0.tgz#faf3daeb31b17c5e1a9dabcee288aaf8aafa5823"
+  integrity sha512-qocaHPv5ypefh6YNxvnbABM07KMxExbtbfuJoIie3iZXX1ERwYmJcIiRrr9H05ucQP1k28dav8rpdDgjQd8drg==
   dependencies:
-    css-select "^4.1.2"
-    css-what "^5.0.0"
+    css-select "^4.1.3"
+    css-what "^5.0.1"
     domelementtype "^2.2.0"
     domhandler "^4.2.0"
-    domutils "^2.6.0"
+    domutils "^2.7.0"
 
 cheerio@^1.0.0-rc.3:
-  version "1.0.0-rc.9"
-  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.9.tgz#a3ae6b7ce7af80675302ff836f628e7cb786a67f"
-  integrity sha512-QF6XVdrLONO6DXRF5iaolY+odmhj2CLj+xzNod7INPWMi/x9X4SOylH0S/vaPpX+AUU6t04s34SQNh7DbkuCng==
+  version "1.0.0-rc.10"
+  resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e"
+  integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==
   dependencies:
-    cheerio-select "^1.4.0"
-    dom-serializer "^1.3.1"
+    cheerio-select "^1.5.0"
+    dom-serializer "^1.3.2"
     domhandler "^4.2.0"
     htmlparser2 "^6.1.0"
     parse5 "^6.0.1"
     parse5-htmlparser2-tree-adapter "^6.0.1"
     tslib "^2.2.0"
 
-"chokidar@>=2.0.0 <4.0.0":
-  version "3.5.1"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a"
-  integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==
+"chokidar@>=3.0.0 <4.0.0":
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
+  integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==
   dependencies:
-    anymatch "~3.1.1"
+    anymatch "~3.1.2"
     braces "~3.0.2"
-    glob-parent "~5.1.0"
+    glob-parent "~5.1.2"
     is-binary-path "~2.1.0"
     is-glob "~4.0.1"
     normalize-path "~3.0.0"
-    readdirp "~3.5.0"
+    readdirp "~3.6.0"
   optionalDependencies:
-    fsevents "~2.3.1"
+    fsevents "~2.3.2"
 
 chokidar@^3.5.1:
   version "3.5.3"
@@ -4153,9 +3848,9 @@ code-point-at@^1.0.0:
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
 codemirror@^5.59.2:
-  version "5.59.4"
-  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.59.4.tgz#bfc11c8ce32b04818e8d661bbd790a94f4b3a6f3"
-  integrity sha512-achw5JBgx8QPcACDDn+EUUXmCYzx/zxEtOGXyjvLEvYY8GleUrnfm5D+Zb+UjShHggXKDT9AXrbkBZX6a0YSQg==
+  version "5.63.3"
+  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527"
+  integrity sha512-1C+LELr+5grgJYqwZKqxrcbPsHFHapVaVAloBsFBASbpLnQqLw1U8yXJ3gT5D+rhxIiSpo+kTqN+hQ+9ialIXw==
 
 color-convert@^1.9.0, color-convert@^1.9.3:
   version "1.9.3"
@@ -4181,15 +3876,15 @@ color-name@^1.0.0, color-name@~1.1.4:
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
-colord@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/colord/-/colord-2.0.0.tgz#f8c19f2526b7dc5b22d6e57ef102f03a2a43a3d8"
-  integrity sha512-WMDFJfoY3wqPZNpKUFdse3HhD5BHCbE9JCdxRzoVH+ywRITGOeWAHNkGEmyxLlErEpN9OLMWgdM9dWQtDk5dog==
+colord@^2.9.1:
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.1.tgz#c961ea0efeb57c9f0f4834458f26cb9cc4a3f90e"
+  integrity sha512-4LBMSt09vR0uLnPVkOUBnmxgoaeN4ewRbx801wY/bXcltXfpR/G46OdWn96XpYmCWuYvO46aBZP4NgX8HpNAcw==
 
-colorette@^1.2.1, colorette@^1.2.2:
-  version "1.2.2"
-  resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
-  integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
+colorette@^2.0.14:
+  version "2.0.16"
+  resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da"
+  integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==
 
 combine-source-map@^0.8.0, combine-source-map@~0.8.0:
   version "0.8.0"
@@ -4211,17 +3906,7 @@ commander@^4.0.1:
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
   integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
 
-commander@^6.2.0:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
-  integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
-
-commander@^7.0.0:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff"
-  integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==
-
-commander@^7.2.0:
+commander@^7.0.0, commander@^7.2.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
   integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
@@ -4252,9 +3937,9 @@ concat-stream@^1.6.0, concat-stream@^1.6.1, concat-stream@~1.6.0:
     typedarray "^0.0.6"
 
 config-chain@^1.1.11:
-  version "1.1.12"
-  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa"
-  integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==
+  version "1.1.13"
+  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4"
+  integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==
   dependencies:
     ini "^1.3.4"
     proto-list "~1.2.1"
@@ -4320,13 +4005,6 @@ cookie@~0.4.1:
   resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
   integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
 
-copy-to-clipboard@^3.3.1:
-  version "3.3.1"
-  resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae"
-  integrity sha512-i13qo6kIHTTpCm8/Wup+0b1mVWETvu2kIMzKoK8FpkLkFxlt0znUAHcMzox+T8sPlqtZXq3CulEjQHsYiGFJUw==
-  dependencies:
-    toggle-selection "^1.0.6"
-
 copy-webpack-plugin@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-7.0.0.tgz#3506f867ca6e861ee2769d4deaf8fa0d2563ada9"
@@ -4341,7 +4019,7 @@ copy-webpack-plugin@^7.0.0:
     schema-utils "^3.0.0"
     serialize-javascript "^5.0.1"
 
-core-js-compat@^3.20.2, core-js-compat@^3.21.0:
+core-js-compat@^3.20.2, core-js-compat@^3.21.0, core-js-compat@^3.8.1, core-js-compat@^3.9.0:
   version "3.21.0"
   resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.21.0.tgz#bcc86aa5a589cee358e7a7fa0a4979d5a76c3885"
   integrity sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==
@@ -4349,28 +4027,20 @@ core-js-compat@^3.20.2, core-js-compat@^3.21.0:
     browserslist "^4.19.1"
     semver "7.0.0"
 
-core-js-compat@^3.8.1, core-js-compat@^3.9.0:
-  version "3.9.1"
-  resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455"
-  integrity sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA==
-  dependencies:
-    browserslist "^4.16.3"
-    semver "7.0.0"
-
 core-js@^2.4.0:
   version "2.6.12"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
   integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
 
-core-js@^3.12.1, core-js@^3.2.1:
-  version "3.12.1"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112"
-  integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==
+core-js@^3.15.1, core-js@^3.2.1:
+  version "3.19.1"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.1.tgz#f6f173cae23e73a7d88fa23b6e9da329276c6641"
+  integrity sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg==
 
 core-util-is@~1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
-  integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+  integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
 
 cors@~2.8.5:
   version "2.8.5"
@@ -4392,9 +4062,9 @@ cosmiconfig@^6.0.0:
     yaml "^1.7.2"
 
 cosmiconfig@^7.0.0:
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3"
-  integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==
+  version "7.0.1"
+  resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d"
+  integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==
   dependencies:
     "@types/parse-json" "^4.0.0"
     import-fresh "^3.2.1"
@@ -4491,59 +4161,52 @@ css-color-keywords@^1.0.0:
   resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
   integrity sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=
 
-css-color-names@^0.0.4:
-  version "0.0.4"
-  resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
-  integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=
-
 css-color-names@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-1.0.1.tgz#6ff7ee81a823ad46e020fa2fd6ab40a887e2ba67"
   integrity sha512-/loXYOch1qU1biStIFsHH8SxTmOseh1IJqFvy8IujXOm1h+QjUdDhkzOrR5HG8K8mlxREj0yfi8ewCHx0eMxzA==
 
 css-declaration-sorter@^6.0.3:
-  version "6.0.3"
-  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.0.3.tgz#9dfd8ea0df4cc7846827876fafb52314890c21a9"
-  integrity sha512-52P95mvW1SMzuRZegvpluT6yEv0FqQusydKQPZsNN5Q7hh8EwQvN8E2nwuJ16BBvNN6LcoIZXu/Bk58DAhrrxw==
+  version "6.1.3"
+  resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.1.3.tgz#e9852e4cf940ba79f509d9425b137d1f94438dc2"
+  integrity sha512-SvjQjNRZgh4ULK1LDJ2AduPKUKxIqmtU7ZAyi47BTV+M90Qvxr9AB6lKlLbDUfXqI9IQeYA8LbAsCZPpJEV3aA==
   dependencies:
     timsort "^0.3.0"
 
-css-line-break@1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef"
-  integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA==
+css-line-break@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-2.0.1.tgz#3dc74c2ed5eb64211480281932475790243e7338"
+  integrity sha512-gwKYIMUn7xodIcb346wgUhE2Dt5O1Kmrc16PWi8sL4FTfyDj8P5095rzH7+O8CTZudJr+uw2GCI/hwEkDJFI2w==
   dependencies:
     base64-arraybuffer "^0.2.0"
 
 css-loader@^5.0.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.1.1.tgz#9362d444a0f7c08c148a109596715c904e252879"
-  integrity sha512-5FfhpjwtuRgxqmusDidowqmLlcb+1HgnEDMsi2JhiUrZUcoc+cqw+mUtMIF/+OfeMYaaFCLYp1TaIt9H6I/fKA==
+  version "5.2.7"
+  resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-5.2.7.tgz#9b9f111edf6fb2be5dc62525644cbc9c232064ae"
+  integrity sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==
   dependencies:
-    camelcase "^6.2.0"
-    cssesc "^3.0.0"
     icss-utils "^5.1.0"
     loader-utils "^2.0.0"
-    postcss "^8.2.6"
+    postcss "^8.2.15"
     postcss-modules-extract-imports "^3.0.0"
     postcss-modules-local-by-default "^4.0.0"
     postcss-modules-scope "^3.0.0"
     postcss-modules-values "^4.0.0"
     postcss-value-parser "^4.1.0"
     schema-utils "^3.0.0"
-    semver "^7.3.4"
+    semver "^7.3.5"
 
 css-minimizer-webpack-plugin@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.0.0.tgz#5b1edbffe1a3e6450d8cb53fb4f4c5013b7af313"
-  integrity sha512-yIrqG0pPphR1RoNx2wDxYmxRf2ubRChLDXxv7ccipEm5bRKsZRYp8n+2peeXehtTF5s3yNxlqsdz3WQOsAgUkw==
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/css-minimizer-webpack-plugin/-/css-minimizer-webpack-plugin-3.1.1.tgz#27bafa3b75054713565b2266c64b0228acd18634"
+  integrity sha512-KlB8l5uoNcf9F7i5kXnkxoqJGd2BXH4f0+Lj2vSWSmuvMLYO1kNsJ1KHSzeDW8e45/whgSOPcKVT/3JopkT8dg==
   dependencies:
-    cssnano "^5.0.0"
-    jest-worker "^26.3.0"
+    cssnano "^5.0.6"
+    jest-worker "^27.0.2"
     p-limit "^3.0.2"
-    postcss "^8.2.9"
-    schema-utils "^3.0.0"
-    serialize-javascript "^5.0.1"
+    postcss "^8.3.5"
+    schema-utils "^3.1.0"
+    serialize-javascript "^6.0.0"
     source-map "^0.6.1"
 
 css-select-base-adapter@^0.1.1:
@@ -4561,7 +4224,7 @@ css-select@^2.0.0:
     domutils "^1.7.0"
     nth-check "^1.0.2"
 
-css-select@^4.1.2, css-select@^4.1.3:
+css-select@^4.1.3:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.1.3.tgz#a70440f70317f2669118ad74ff105e65849c7067"
   integrity sha512-gT3wBNd9Nj49rAbmtFHj1cljIAOLYSX1nZ8CB7TBO3INYckygm5B7LISU/szY//YmdiSLbJvDLOx9VnMVpMBxA==
@@ -4610,7 +4273,7 @@ css-what@^3.2.1:
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4"
   integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==
 
-css-what@^5.0.0:
+css-what@^5.0.0, css-what@^5.0.1:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.1.0.tgz#3f7b707aadf633baf62c2ceb8579b545bb40f7fe"
   integrity sha512-arSMRWIIFY0hV8pIxZMEfmMI47Wj3R/aWpZDDxWYCPEiOMv6tfOrnpDtgxBYPEQD4V0Y/958+1TdC3iWTFcUPw==
@@ -4620,24 +4283,24 @@ cssesc@^3.0.0:
   resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
   integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
 
-cssnano-preset-default@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.1.tgz#5cd783caed942cc94159aeb10583af4691445b8c"
-  integrity sha512-kAhR71Tascmnjlhl4UegGA3KGGbMLXHkkqVpA9idsRT1JmIhIsz1C3tDpBeQMUw5EX5Rfb1HGc/PRqD2AFk3Vg==
+cssnano-preset-default@^5.1.5:
+  version "5.1.5"
+  resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.5.tgz#6effb7175ee5d296f95330e137587e27ee974d44"
+  integrity sha512-fF00UI+d3PWkGfMd62geqmoUe5h+LOhGE2GH4Fqq3beNKdCU1LWwLUyIcu4/A72lWv0737cHey5zhhWw3rW0sA==
   dependencies:
     css-declaration-sorter "^6.0.3"
     cssnano-utils "^2.0.1"
     postcss-calc "^8.0.0"
-    postcss-colormin "^5.1.1"
-    postcss-convert-values "^5.0.1"
+    postcss-colormin "^5.2.1"
+    postcss-convert-values "^5.0.2"
     postcss-discard-comments "^5.0.1"
     postcss-discard-duplicates "^5.0.1"
     postcss-discard-empty "^5.0.1"
     postcss-discard-overridden "^5.0.1"
     postcss-merge-longhand "^5.0.2"
-    postcss-merge-rules "^5.0.1"
+    postcss-merge-rules "^5.0.2"
     postcss-minify-font-values "^5.0.1"
-    postcss-minify-gradients "^5.0.1"
+    postcss-minify-gradients "^5.0.3"
     postcss-minify-params "^5.0.1"
     postcss-minify-selectors "^5.1.0"
     postcss-normalize-charset "^5.0.1"
@@ -4647,12 +4310,12 @@ cssnano-preset-default@^5.1.1:
     postcss-normalize-string "^5.0.1"
     postcss-normalize-timing-functions "^5.0.1"
     postcss-normalize-unicode "^5.0.1"
-    postcss-normalize-url "^5.0.1"
+    postcss-normalize-url "^5.0.2"
     postcss-normalize-whitespace "^5.0.1"
-    postcss-ordered-values "^5.0.1"
+    postcss-ordered-values "^5.0.2"
     postcss-reduce-initial "^5.0.1"
     postcss-reduce-transforms "^5.0.1"
-    postcss-svgo "^5.0.1"
+    postcss-svgo "^5.0.3"
     postcss-unique-selectors "^5.0.1"
 
 cssnano-utils@^2.0.1:
@@ -4660,14 +4323,15 @@ cssnano-utils@^2.0.1:
   resolved "https://registry.yarnpkg.com/cssnano-utils/-/cssnano-utils-2.0.1.tgz#8660aa2b37ed869d2e2f22918196a9a8b6498ce2"
   integrity sha512-i8vLRZTnEH9ubIyfdZCAdIdgnHAUeQeByEeQ2I7oTilvP9oHO6RScpeq3GsFUVqeB8uZgOQ9pw8utofNn32hhQ==
 
-cssnano@^5.0.0, cssnano@^5.0.2:
-  version "5.0.4"
-  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.4.tgz#5ca90729c94c71c4bc3d45abb543be10740bf381"
-  integrity sha512-I+fDW74CJ4yb31765ov9xXe70XLZvFTXjwhmA//VgAAuSAU34Oblbe94Q9zffiCX1VhcSfQWARQnwhz+Nqgb4Q==
+cssnano@^5.0.2, cssnano@^5.0.6:
+  version "5.0.9"
+  resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-5.0.9.tgz#bd03168835c0883c16754085704f57861a32d99c"
+  integrity sha512-Y4olTKBKsPKl5izpcXHRDiB/1rVdbIDM4qVXgEKBt466kYT42SEEsnCYOQFFXzEkUYV8pJNCII9JKzb8KfDk+g==
   dependencies:
-    cosmiconfig "^7.0.0"
-    cssnano-preset-default "^5.1.1"
+    cssnano-preset-default "^5.1.5"
     is-resolvable "^1.1.0"
+    lilconfig "^2.0.3"
+    yaml "^1.10.2"
 
 csso@^4.0.2, csso@^4.2.0:
   version "4.2.0"
@@ -4677,9 +4341,9 @@ csso@^4.0.2, csso@^4.2.0:
     css-tree "^1.1.2"
 
 csstype@^2.5.2, csstype@^2.5.7:
-  version "2.6.16"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.16.tgz#544d69f547013b85a40d15bff75db38f34fe9c39"
-  integrity sha512-61FBWoDHp/gRtsoDkq/B1nWrCUG/ok1E3tUrcNbZjsE9Cxd9yzUirjS3+nAATB8U4cTtaQmAHbNndoFz5L6C9Q==
+  version "2.6.18"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.18.tgz#980a8b53085f34af313410af064f2bd241784218"
+  integrity sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==
 
 csstype@^3.0.11:
   version "3.0.11"
@@ -4687,9 +4351,9 @@ csstype@^3.0.11:
   integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==
 
 csstype@^3.0.2:
-  version "3.0.8"
-  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.8.tgz#d2266a792729fb227cd216fb572f43728e1ad340"
-  integrity sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==
+  version "3.0.9"
+  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.9.tgz#6410af31b26bd0520933d02cbc64fce9ce3fbf0b"
+  integrity sha512-rpw6JPxK6Rfg1zLOYCSwle2GFOOsnjmDYDaBwEcwoOg4qlsIVCN789VkBZDJAGi4T07gI4YSutR43t9Zz4Lzuw==
 
 cubic2quad@^1.0.0:
   version "1.2.1"
@@ -4715,14 +4379,14 @@ dash-ast@^1.0.0:
   integrity sha512-Vy4dx7gquTeMcQR/hDkYLGUnwVil6vk4FOOct+djUnHOUWt+zJPJAaRIXaAFkPXtJjvlY7o3rfRu0/3hpnwoUA==
 
 date-fns@^2.24.0:
-  version "2.24.0"
-  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.24.0.tgz#7d86dc0d93c87b76b63d213b4413337cfd1c105d"
-  integrity sha512-6ujwvwgPID6zbI0o7UbURi2vlLDR9uP26+tW6Lg+Ji3w7dd0i3DOcjcClLjLPranT60SSEFBwdSyYwn/ZkPIuw==
+  version "2.25.0"
+  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.25.0.tgz#8c5c8f1d958be3809a9a03f4b742eba894fc5680"
+  integrity sha512-ovYRFnTrbGPD4nqaEqescPEv1mNwvt+UTqI3Ay9SzNtey9NZnYu6E2qCcBBgJ6/2VF1zGGygpyTDITqpQQ5e+w==
 
 date-format@^4.0.3:
   version "4.0.3"
   resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.3.tgz#f63de5dc08dc02efd8ef32bf2a6918e486f35873"
-  integrity "sha1-9j3l3AjcAu/Y7zK/KmkY5IbzWHM= sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ=="
+  integrity sha512-7P3FyqDcfeznLZp2b+OMitV9Sz2lUnsT87WaTat9nVwqsBkTzPG3lPLNwW3en6F4pHUiWzr6vb8CLhjdK9bcxQ==
 
 debug@2.6.9, debug@^2.6.8:
   version "2.6.9"
@@ -4732,9 +4396,9 @@ debug@2.6.9, debug@^2.6.8:
     ms "2.0.0"
 
 debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@~4.3.1:
-  version "4.3.1"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
-  integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
+  integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==
   dependencies:
     ms "2.1.2"
 
@@ -4823,9 +4487,9 @@ deep-equal-ident@^1.1.1:
     lodash.isequal "^3.0"
 
 deep-is@^0.1.3:
-  version "0.1.3"
-  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
-  integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
 
 deepmerge@^4.2.2:
   version "4.2.2"
@@ -4894,12 +4558,12 @@ di@^0.0.1:
   integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=
 
 diff-arrays-of-objects@^1.1.8:
-  version "1.1.8"
-  resolved "https://registry.yarnpkg.com/diff-arrays-of-objects/-/diff-arrays-of-objects-1.1.8.tgz#a5b008bb409af7c7f997f0064e312df0d5a1bb24"
-  integrity sha512-OAaiDlQRiv5+EASUpwNSDa/sWyKHKvODQfah1CAx0dosR8OWXedFXgxAQHIdeWDobZ86D6g93BfK2X9ECIRuqw==
+  version "1.1.9"
+  resolved "https://registry.yarnpkg.com/diff-arrays-of-objects/-/diff-arrays-of-objects-1.1.9.tgz#b1f00485684c1bb2ac6610d2f604b51f9679064d"
+  integrity sha512-V3Uk0sm71RjLsEvH9NZcgz6+C8Fu2eBRLwlrFlwXDEj6dfHX5El6mlRUS1hTSEm6eNvLmE4ewJ+iQbXCqGvVgQ==
   dependencies:
     deep-diff "^1.0.2"
-    lodash "^4.17.19"
+    lodash "4"
 
 diffie-hellman@^5.0.0:
   version "5.0.3"
@@ -4942,9 +4606,9 @@ dom-align@^1.7.0:
   integrity sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg==
 
 dom-helpers@^5.0.1:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.0.tgz#57fd054c5f8f34c52a3eeffdb7e7e93cd357d95b"
-  integrity sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902"
+  integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==
   dependencies:
     "@babel/runtime" "^7.8.7"
     csstype "^3.0.2"
@@ -4967,7 +4631,7 @@ dom-serializer@0:
     domelementtype "^2.0.1"
     entities "^2.0.0"
 
-dom-serializer@^1.0.1, dom-serializer@^1.3.1:
+dom-serializer@^1.0.1, dom-serializer@^1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91"
   integrity sha512-5c54Bk5Dw4qAxNOI1pFEizPSjVsx5+bpJKmL2kPn8JhBUq2q09tTCa3mjijun2NfK78NMouDYNMBkOrPZiS+ig==
@@ -4991,10 +4655,10 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
   resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.2.0.tgz#9a0b6c2782ed6a1c7323d42267183df9bd8b1d57"
   integrity sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A==
 
-domhandler@4.2.0, domhandler@^4.0.0, domhandler@^4.2.0:
-  version "4.2.0"
-  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059"
-  integrity sha512-zk7sgt970kzPks2Bf+dwT/PLzghLnsivb9CcxkvR8Mzr66Olr0Ofd8neSbglHJHaHa2MadfoSdNlKYAaafmWfA==
+domhandler@4.2.2, domhandler@^4.0.0, domhandler@^4.2.0:
+  version "4.2.2"
+  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f"
+  integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w==
   dependencies:
     domelementtype "^2.2.0"
 
@@ -5006,10 +4670,10 @@ domutils@^1.7.0:
     dom-serializer "0"
     domelementtype "1"
 
-domutils@^2.5.2, domutils@^2.6.0:
-  version "2.6.0"
-  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.6.0.tgz#2e15c04185d43fb16ae7057cb76433c6edb938b7"
-  integrity sha512-y0BezHuy4MDYxh6OvolXYsH+1EMGmFbwv5FKW7ovwMG6zTPWqNPq3WF9ayZssFq+UlKdffGLbOEaghNdaOm1WA==
+domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0:
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+  integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
   dependencies:
     dom-serializer "^1.0.1"
     domelementtype "^2.2.0"
@@ -5084,10 +4748,10 @@ ejs@~3.1.6:
   dependencies:
     jake "^10.6.1"
 
-electron-to-chromium@^1.3.723:
-  version "1.3.740"
-  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.740.tgz#e38b7d2b848f632191b643e6dabca51be2162922"
-  integrity sha512-Mi2m55JrX2BFbNZGKYR+2ItcGnR4O5HhrvgoRRyZQlaMGQULqDhoGkLWHzJoshSzi7k1PUofxcDbNhlFrDZNhg==
+electron-to-chromium@^1.3.886:
+  version "1.3.889"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.889.tgz#0b7c6f7628559592d5406deda281788f37107790"
+  integrity sha512-suEUoPTD1mExjL9TdmH7cvEiWJVM2oEiAi+Y1p0QKxI2HcRlT44qDTP2c1aZmVwRemIPYOpxmV7CxQCOWcm4XQ==
 
 electron-to-chromium@^1.4.17:
   version "1.4.63"
@@ -5136,28 +4800,21 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
   dependencies:
     once "^1.4.0"
 
-engine.io-client@~5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-5.1.1.tgz#f5c3aaaef1bdc9443aac6ffde48b3b2fb2dc56fc"
-  integrity sha512-jPFpw2HLL0lhZ2KY0BpZhIJdleQcUO9W1xkIpo0h3d6s+5D6+EV/xgQw9qWOmymszv2WXef/6KUUehyxEKomlQ==
+engine.io-client@~6.0.1:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.0.2.tgz#ccfc059051e65ca63845e65929184757754cc34e"
+  integrity sha512-cAep9lhZV6Q8jMXx3TNSU5cydMzMed8/O7Tz5uzyqZvpNPtQ3WQXrLYGADxlsuaFmOLN7wZLmT7ImiFhUOku8g==
   dependencies:
-    base64-arraybuffer "0.1.4"
-    component-emitter "~1.3.0"
+    "@socket.io/component-emitter" "~3.0.0"
     debug "~4.3.1"
-    engine.io-parser "~4.0.1"
+    engine.io-parser "~5.0.0"
     has-cors "1.1.0"
     parseqs "0.0.6"
     parseuri "0.0.6"
-    ws "~7.4.2"
+    ws "~8.2.3"
+    xmlhttprequest-ssl "~2.0.0"
     yeast "0.1.2"
 
-engine.io-parser@~4.0.1:
-  version "4.0.2"
-  resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-4.0.2.tgz#e41d0b3fb66f7bf4a3671d2038a154024edb501e"
-  integrity sha512-sHfEQv6nmtJrq6TKuIz5kyEKH/qSdK56H/A+7DnAuUPWosnIZAS2NHNcPLmyjtY3cGS/MqJdZbUjW97JU72iYg==
-  dependencies:
-    base64-arraybuffer "0.1.4"
-
 engine.io-parser@~5.0.0:
   version "5.0.3"
   resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.3.tgz#ca1f0d7b11e290b4bfda251803baea765ed89c09"
@@ -5181,10 +4838,10 @@ engine.io@~6.1.0:
     engine.io-parser "~5.0.0"
     ws "~8.2.3"
 
-enhanced-resolve@^5.7.0:
-  version "5.8.2"
-  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz#15ddc779345cbb73e97c611cd00c01c1e7bf4d8b"
-  integrity sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==
+enhanced-resolve@^5.8.3:
+  version "5.8.3"
+  resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz#6d552d465cce0423f5b3d718511ea53826a7b2f0"
+  integrity sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==
   dependencies:
     graceful-fs "^4.2.4"
     tapable "^2.2.0"
@@ -5285,22 +4942,26 @@ error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.18.0, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2, es-abstract@^1.18.2:
-  version "1.18.2"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.2.tgz#6eb518b640262e8ddcbd48e0bc8549f82efd48a7"
-  integrity sha512-byRiNIQXE6HWNySaU6JohoNXzYgbBjztwFnBLUTiJmWXjaU9bSq3urQLUlNLQ292tc+gc07zYZXNZjaOoAX3sw==
+es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-abstract@^1.18.5, es-abstract@^1.19.0, es-abstract@^1.19.1:
+  version "1.19.1"
+  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3"
+  integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==
   dependencies:
     call-bind "^1.0.2"
     es-to-primitive "^1.2.1"
     function-bind "^1.1.1"
     get-intrinsic "^1.1.1"
+    get-symbol-description "^1.0.0"
     has "^1.0.3"
     has-symbols "^1.0.2"
-    is-callable "^1.2.3"
+    internal-slot "^1.0.3"
+    is-callable "^1.2.4"
     is-negative-zero "^2.0.1"
-    is-regex "^1.1.3"
-    is-string "^1.0.6"
-    object-inspect "^1.10.3"
+    is-regex "^1.1.4"
+    is-shared-array-buffer "^1.0.1"
+    is-string "^1.0.7"
+    is-weakref "^1.0.1"
+    object-inspect "^1.11.0"
     object-keys "^1.1.1"
     object.assign "^4.1.2"
     string.prototype.trimend "^1.0.4"
@@ -5312,10 +4973,10 @@ es-array-method-boxes-properly@^1.0.0:
   resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
   integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
 
-es-module-lexer@^0.4.0:
-  version "0.4.1"
-  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.4.1.tgz#dda8c6a14d8f340a24e34331e0fab0cb50438e0e"
-  integrity sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==
+es-module-lexer@^0.9.0:
+  version "0.9.3"
+  resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
+  integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
 
 es-to-primitive@^1.2.1:
   version "1.2.1"
@@ -5341,35 +5002,42 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
 
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
 eslint-plugin-react-hooks@^4.3.0:
   version "4.3.0"
   resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172"
   integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA==
 
 eslint-plugin-react@^7.20.5:
-  version "7.23.2"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.23.2.tgz#2d2291b0f95c03728b55869f01102290e792d494"
-  integrity sha512-AfjgFQB+nYszudkxRkTFu0UR1zEQig0ArVMPloKhxwlwkzaw/fBiH0QWcBBhZONlXqQC51+nfqFrkn4EzHcGBw==
+  version "7.26.1"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.26.1.tgz#41bcfe3e39e6a5ac040971c1af94437c80daa40e"
+  integrity sha512-Lug0+NOFXeOE+ORZ5pbsh6mSKjBKXDXItUD2sQoT+5Yl0eoT82DqnXeTMfUare4QVCn9QwXbfzO/dBLjLXwVjQ==
   dependencies:
     array-includes "^3.1.3"
     array.prototype.flatmap "^1.2.4"
     doctrine "^2.1.0"
-    has "^1.0.3"
+    estraverse "^5.2.0"
     jsx-ast-utils "^2.4.1 || ^3.0.0"
     minimatch "^3.0.4"
-    object.entries "^1.1.3"
+    object.entries "^1.1.4"
     object.fromentries "^2.0.4"
-    object.values "^1.1.3"
+    object.hasown "^1.0.0"
+    object.values "^1.1.4"
     prop-types "^15.7.2"
     resolve "^2.0.0-next.3"
-    string.prototype.matchall "^4.0.4"
+    semver "^6.3.0"
+    string.prototype.matchall "^4.0.5"
 
 eslint-rule-composer@^0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9"
   integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==
 
-eslint-scope@^5.1.1:
+eslint-scope@5.1.1, eslint-scope@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
   integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
@@ -5395,28 +5063,31 @@ eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0:
   integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
 
 eslint@^7.19.0:
-  version "7.21.0"
-  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.21.0.tgz#4ecd5b8c5b44f5dedc9b8a110b01bbfeb15d1c83"
-  integrity sha512-W2aJbXpMNofUp0ztQaF40fveSsJBjlSCSWpy//gzfTvwC+USs/nceBrKmlJOiM8r1bLwP2EuYkCqArn/6QTIgg==
+  version "7.32.0"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d"
+  integrity sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==
   dependencies:
     "@babel/code-frame" "7.12.11"
-    "@eslint/eslintrc" "^0.4.0"
+    "@eslint/eslintrc" "^0.4.3"
+    "@humanwhocodes/config-array" "^0.5.0"
     ajv "^6.10.0"
     chalk "^4.0.0"
     cross-spawn "^7.0.2"
     debug "^4.0.1"
     doctrine "^3.0.0"
     enquirer "^2.3.5"
+    escape-string-regexp "^4.0.0"
     eslint-scope "^5.1.1"
     eslint-utils "^2.1.0"
     eslint-visitor-keys "^2.0.0"
     espree "^7.3.1"
     esquery "^1.4.0"
     esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
     file-entry-cache "^6.0.1"
     functional-red-black-tree "^1.0.1"
-    glob-parent "^5.0.0"
-    globals "^12.1.0"
+    glob-parent "^5.1.2"
+    globals "^13.6.0"
     ignore "^4.0.6"
     import-fresh "^3.0.0"
     imurmurhash "^0.1.4"
@@ -5424,7 +5095,7 @@ eslint@^7.19.0:
     js-yaml "^3.13.1"
     json-stable-stringify-without-jsonify "^1.0.1"
     levn "^0.4.1"
-    lodash "^4.17.20"
+    lodash.merge "^4.6.2"
     minimatch "^3.0.4"
     natural-compare "^1.4.0"
     optionator "^0.9.1"
@@ -5433,7 +5104,7 @@ eslint@^7.19.0:
     semver "^7.2.1"
     strip-ansi "^6.0.0"
     strip-json-comments "^3.1.0"
-    table "^6.0.4"
+    table "^6.0.9"
     text-table "^0.2.0"
     v8-compile-cache "^2.0.3"
 
@@ -5471,9 +5142,9 @@ estraverse@^4.1.1:
   integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
 
 estraverse@^5.1.0, estraverse@^5.2.0:
-  version "5.2.0"
-  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880"
-  integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
 
 esutils@^2.0.2:
   version "2.0.3"
@@ -5556,9 +5227,9 @@ execa@^4.0.0:
     strip-final-newline "^2.0.0"
 
 execa@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376"
-  integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+  integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
   dependencies:
     cross-spawn "^7.0.3"
     get-stream "^6.0.0"
@@ -5615,16 +5286,15 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
 
 fast-glob@^3.0.3, fast-glob@^3.1.1, fast-glob@^3.2.4:
-  version "3.2.5"
-  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661"
-  integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==
+  version "3.2.7"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.7.tgz#fd6cb7a2d7e9aa7a7846111e85a196d6b2f766a1"
+  integrity sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==
   dependencies:
     "@nodelib/fs.stat" "^2.0.2"
     "@nodelib/fs.walk" "^1.2.3"
-    glob-parent "^5.1.0"
+    glob-parent "^5.1.2"
     merge2 "^1.3.0"
-    micromatch "^4.0.2"
-    picomatch "^2.2.1"
+    micromatch "^4.0.4"
 
 fast-json-stable-stringify@^2.0.0:
   version "2.1.0"
@@ -5642,14 +5312,16 @@ fast-memoize@^2.5.1:
   integrity sha512-Ue0LwpDYErFbmNnZSF0UH6eImUwDmogUO1jyE+JbN2gsQz/jICm1Ve7t9QT0rNSsfJt+Hs4/S3GnsDVjL4HVrw==
 
 fast-safe-stringify@^2.0.7:
-  version "2.0.7"
-  resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
-  integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
+  integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
 
 fast-xml-parser@^3.19.0:
-  version "3.19.0"
-  resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01"
-  integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg==
+  version "3.21.1"
+  resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.21.1.tgz#152a1d51d445380f7046b304672dd55d15c9e736"
+  integrity sha512-FTFVjYoBOZTJekiUsawGsSYV9QL0A+zDYCRj7y34IO6Jg+2IMYEtQa+bbictpdpV8dHxXywqU7C0gRDEOFtBFg==
+  dependencies:
+    strnum "^1.0.4"
 
 fastest-levenshtein@^1.0.12:
   version "1.0.12"
@@ -5657,9 +5329,9 @@ fastest-levenshtein@^1.0.12:
   integrity sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==
 
 fastq@^1.6.0:
-  version "1.11.0"
-  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858"
-  integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==
+  version "1.13.0"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
+  integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
   dependencies:
     reusify "^1.0.4"
 
@@ -5754,9 +5426,9 @@ finalhandler@1.1.2:
     unpipe "~1.0.0"
 
 find-cache-dir@^3.3.1:
-  version "3.3.1"
-  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880"
-  integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+  integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
   dependencies:
     commondir "^1.0.1"
     make-dir "^3.0.2"
@@ -5791,14 +5463,14 @@ flat-cache@^3.0.4:
     rimraf "^3.0.2"
 
 flatted@^3.1.0:
-  version "3.1.1"
-  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
-  integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.2.tgz#64bfed5cb68fe3ca78b3eb214ad97b63bedce561"
+  integrity sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==
 
 flatted@^3.2.4:
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
-  integrity "sha1-dshYT0/IQ9tkcCpr0Eq3qL1mbaM= sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg=="
+  integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
 
 follow-redirects@^1.0.0, follow-redirects@^1.14.0:
   version "1.14.8"
@@ -5831,7 +5503,7 @@ fs-constants@^1.0.0:
 fs-extra@^10.0.0:
   version "10.0.0"
   resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1"
-  integrity "sha1-n/YbZV3eU/s0qC34S7IUzoAuF8E= sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ=="
+  integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==
   dependencies:
     graceful-fs "^4.2.0"
     jsonfile "^6.0.1"
@@ -5849,7 +5521,7 @@ fs.realpath@^1.0.0:
   resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
   integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
 
-fsevents@~2.3.1, fsevents@~2.3.2:
+fsevents@~2.3.2:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@@ -5860,13 +5532,13 @@ function-bind@^1.1.1:
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
 function.prototype.name@^1.1.2, function.prototype.name@^1.1.3:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.4.tgz#e4ea839b9d3672ae99d0efd9f38d9191c5eaac83"
-  integrity sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ==
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
+  integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.2"
+    es-abstract "^1.19.0"
     functions-have-names "^1.2.2"
 
 functional-red-black-tree@^1.0.1:
@@ -5961,7 +5633,15 @@ get-stream@^6.0.0:
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
   integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
 
-glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0, glob-parent@~5.1.2:
+get-symbol-description@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
+  integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
+  dependencies:
+    call-bind "^1.0.2"
+    get-intrinsic "^1.1.1"
+
+glob-parent@^5.1.1, glob-parent@^5.1.2, glob-parent@~5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -5973,19 +5653,7 @@ glob-to-regexp@^0.4.1:
   resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
   integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
 
-glob@^7.1.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
-  version "7.1.6"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
-  integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
-  dependencies:
-    fs.realpath "^1.0.0"
-    inflight "^1.0.4"
-    inherits "2"
-    minimatch "^3.0.4"
-    once "^1.3.0"
-    path-is-absolute "^1.0.0"
-
-glob@^7.1.7:
+glob@^7.1.0, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
   integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
@@ -6002,12 +5670,12 @@ globals@^11.1.0:
   resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
   integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
 
-globals@^12.1.0:
-  version "12.4.0"
-  resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8"
-  integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==
+globals@^13.6.0, globals@^13.9.0:
+  version "13.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e"
+  integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg==
   dependencies:
-    type-fest "^0.8.1"
+    type-fest "^0.20.2"
 
 globals@^9.18.0:
   version "9.18.0"
@@ -6029,9 +5697,9 @@ globby@^10.0.0:
     slash "^3.0.0"
 
 globby@^11.0.1:
-  version "11.0.3"
-  resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb"
-  integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==
+  version "11.0.4"
+  resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5"
+  integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==
   dependencies:
     array-union "^2.1.0"
     dir-glob "^3.0.1"
@@ -6084,9 +5752,9 @@ got@^8.3.1:
     url-to-options "^1.0.1"
 
 graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.6:
-  version "4.2.6"
-  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
-  integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+  version "4.2.8"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a"
+  integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==
 
 graphlib@^2.1.8:
   version "2.1.8"
@@ -6158,6 +5826,13 @@ has-to-string-tag-x@^1.2.0:
   dependencies:
     has-symbol-support-x "^1.4.1"
 
+has-tostringtag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+  integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
+  dependencies:
+    has-symbols "^1.0.2"
+
 has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -6197,11 +5872,6 @@ heap@0.2.5:
   resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.5.tgz#713b65590ebcc40fcbeeaf55e851694092b39af1"
   integrity sha1-cTtlWQ68xA/L7q9V6FFpQJKzmvE=
 
-hex-color-regex@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e"
-  integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==
-
 history@^5.2.0:
   version "5.3.0"
   resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
@@ -6222,25 +5892,15 @@ hoist-non-react-statics@^3.0.0, hoist-non-react-statics@^3.3.0, hoist-non-react-
   version "3.3.2"
   resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
   integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
-  dependencies:
-    react-is "^16.7.0"
-
-hsl-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e"
-  integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=
-
-hsla-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38"
-  integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg=
+  dependencies:
+    react-is "^16.7.0"
 
-html-dom-parser@1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-1.0.1.tgz#5d147fed6656c12918edbcea4a423eefe8d0e715"
-  integrity sha512-uKXISKlHzB/l9A08jrs2wseQJ9b864ZfEdmIZskj10cuP6HxCOMHSK0RdluV8NVQaWs0PwefN7d8wqG3jR0IbQ==
+html-dom-parser@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/html-dom-parser/-/html-dom-parser-1.0.2.tgz#bb5ff844f214657d899aa4fb7b0a9e7d15607e96"
+  integrity sha512-Jq4oVkVSn+10ut3fyc2P/Fs1jqTo0l45cP6Q8d2ef/9jfkYwulO0QXmyLI0VUiZrXF4czpGgMEJRa52CQ6Fk8Q==
   dependencies:
-    domhandler "4.2.0"
+    domhandler "4.2.2"
     htmlparser2 "6.1.0"
 
 html-element-map@^1.2.0:
@@ -6257,21 +5917,22 @@ html-escaper@^2.0.0:
   integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
 
 html-react-parser@^1.2.7:
-  version "1.2.7"
-  resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-1.2.7.tgz#1674ed4b96b3440ad922962a3ff000e7f3325293"
-  integrity sha512-gUUEgrZV0YaCxtZO2XuJDUnHSq7gOqKu1krye97cxgiZ+ipaIzspGMhATeq9lhy9gwYmwBF2YCHe/accrMMo8Q==
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/html-react-parser/-/html-react-parser-1.4.0.tgz#bf264f38b9fdf4d94e2120f6a39586c15cb81bd0"
+  integrity sha512-v8Kxy+7L90ZFSM690oJWBNRzZWZOQquYPpQt6kDQPzQyZptXgOJ69kHSi7xdqNdm1mOfsDPwF4K9Bo/dS5gRTQ==
   dependencies:
-    domhandler "4.2.0"
-    html-dom-parser "1.0.1"
-    react-property "1.0.1"
+    domhandler "4.2.2"
+    html-dom-parser "1.0.2"
+    react-property "2.0.0"
     style-to-js "1.1.0"
 
 html2canvas@^1.0.0-rc.7:
-  version "1.0.0-rc.7"
-  resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.0.0-rc.7.tgz#70c159ce0e63954a91169531894d08ad5627ac98"
-  integrity sha512-yvPNZGejB2KOyKleZspjK/NruXVQuowu8NnV2HYG7gW7ytzl+umffbtUI62v2dCHQLDdsK6HIDtyJZ0W3neerA==
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.3.2.tgz#951cc8388a3ce939fdac02131007ee28124afc27"
+  integrity sha512-4+zqv87/a1LsaCrINV69wVLGG8GBZcYBboz1JPWEgiXcWoD9kroLzccsBRU/L9UlfV2MAZ+3J92U9IQPVMDeSQ==
   dependencies:
-    css-line-break "1.1.1"
+    css-line-break "2.0.1"
+    text-segmentation "^1.0.2"
 
 htmlescape@^1.1.0:
   version "1.1.1"
@@ -6392,9 +6053,9 @@ ignore@^4.0.6:
   integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
 
 ignore@^5.1.1, ignore@^5.1.4:
-  version "5.1.8"
-  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57"
-  integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
+  version "5.1.9"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb"
+  integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ==
 
 image-minimizer-webpack-plugin@^2.2.0:
   version "2.2.0"
@@ -6476,9 +6137,9 @@ import-lazy@^3.1.0:
   integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==
 
 import-local@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6"
-  integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.3.tgz#4d51c2c495ca9393da259ec66b62e022920211e0"
+  integrity sha512-bE9iaUY3CXH8Cwfan/abDKAxe1KGT9kyGsBPqf6DMK/z0a2OzAsrukeYNgIH6cH5Xr452jb1TUL8rSfCLjZ9uA==
   dependencies:
     pkg-dir "^4.2.0"
     resolve-cwd "^3.0.0"
@@ -6497,13 +6158,6 @@ imurmurhash@^0.1.4:
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
   integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
 
-indefinite-observable@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/indefinite-observable/-/indefinite-observable-2.0.1.tgz#574af29bfbc17eb5947793797bddc94c9d859400"
-  integrity sha512-G8vgmork+6H9S8lUAg1gtXEj2JxIQTo0g2PbFiYOdjkziSI0F7UYBiVwhZRuixhBCNGczAls34+5HJPyZysvxQ==
-  dependencies:
-    symbol-observable "1.2.0"
-
 indent-string@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
@@ -6623,16 +6277,17 @@ is-absolute-url@^3.0.3:
   integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==
 
 is-any-array@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-1.0.0.tgz#bcb2c7e2d28aaa2fa02ee8f6b604b0b3a957bba7"
-  integrity sha512-0o0ZsgObnylv72nO39P6M+PL7jPUEx39O6BEfZuX36IKPy/RpdudxluAIaRn/LZi5eVPDMlMBaLABzOK6bwPlw==
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-any-array/-/is-any-array-1.0.1.tgz#05fedec1a4dced1854bd279b2ec5df431e5bea9e"
+  integrity sha512-m+FSiaONxBt2W0h9XOUngMBu/WW8uzAKbSk4Ty2aeCcQJ+muBqENexvxUHtDpX65fk5AMCROxqgNX0sSAHstcw==
 
 is-arguments@^1.0.4:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9"
-  integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
+  integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
   dependencies:
-    call-bind "^1.0.0"
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
 
 is-arrayish@^0.2.1:
   version "0.2.1"
@@ -6640,9 +6295,11 @@ is-arrayish@^0.2.1:
   integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
 
 is-bigint@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2"
-  integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+  integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
+  dependencies:
+    has-bigints "^1.0.1"
 
 is-binary-path@~2.1.0:
   version "2.1.0"
@@ -6651,51 +6308,47 @@ is-binary-path@~2.1.0:
   dependencies:
     binary-extensions "^2.0.0"
 
+is-blob@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-blob/-/is-blob-2.1.0.tgz#e36cd82c90653f1e1b930f11baf9c64216a05385"
+  integrity sha512-SZ/fTft5eUhQM6oF/ZaASFDEdbFVe89Imltn9uZr03wdKMcWNVYSMjQPFtg05QuNkt5l5c135ElvXEQG0rk4tw==
+
 is-boolean-object@^1.0.1, is-boolean-object@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0"
-  integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+  integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
   dependencies:
-    call-bind "^1.0.0"
+    call-bind "^1.0.2"
+    has-tostringtag "^1.0.0"
 
 is-buffer@^1.1.0:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
-is-buffer@^2.0.3:
+is-buffer@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
   integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
 
-is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.3:
-  version "1.2.3"
-  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e"
-  integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ==
-
-is-color-stop@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345"
-  integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=
-  dependencies:
-    css-color-names "^0.0.4"
-    hex-color-regex "^1.1.0"
-    hsl-regex "^1.0.0"
-    hsla-regex "^1.0.0"
-    rgb-regex "^1.0.1"
-    rgba-regex "^1.0.0"
+is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
+  integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
 
 is-core-module@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a"
-  integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548"
+  integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==
   dependencies:
     has "^1.0.3"
 
 is-date-object@^1.0.1:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.4.tgz#550cfcc03afada05eea3dd30981c7b09551f73e5"
-  integrity sha512-/b4ZVsG7Z5XVtIxs/h9W8nvfLgSAyKYdtGWQLbqy6jA1icmgjf8WCoTKgeS4wy5tYaPePouzFMANbnj94c2Z+A==
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+  integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
+  dependencies:
+    has-tostringtag "^1.0.0"
 
 is-docker@^2.1.1:
   version "2.2.1"
@@ -6725,14 +6378,16 @@ is-fullwidth-code-point@^3.0.0:
   integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
 
 is-generator-function@^1.0.7:
-  version "1.0.9"
-  resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.9.tgz#e5f82c2323673e7fcad3d12858c83c4039f6399c"
-  integrity sha512-ZJ34p1uvIfptHCN7sFTjGibB9/oBg17sHqzDLfuwhvmN/qLVvIQXRQ8licZQ35WJ8KuEQt/etnnzQFI9C9Ue/A==
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
+  integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
+  dependencies:
+    has-tostringtag "^1.0.0"
 
 is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
-  integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
   dependencies:
     is-extglob "^2.1.1"
 
@@ -6762,9 +6417,11 @@ is-negative-zero@^2.0.1:
   integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
 
 is-number-object@^1.0.4:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.5.tgz#6edfaeed7950cff19afedce9fbfca9ee6dd289eb"
-  integrity sha512-RU0lI/n95pMoUKu9v1BZP5MBcZuNSVJkMkAG2dJqC4z2GlkGUNeH68SuHuBKBD/XFe+LHZ+f9BKkLET60Niedw==
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.6.tgz#6a7aaf838c7f0686a50b4553f7e54a96494e89f0"
+  integrity sha512-bEVOqiRcvo3zO1+G2lVMy+gkkEm9Yh7cDMRusKKu5ZJKPUYSJwICTKZrNKHA2EbSP0Tu0+6B/emsYNHZyn6K8g==
+  dependencies:
+    has-tostringtag "^1.0.0"
 
 is-number@^7.0.0:
   version "7.0.0"
@@ -6793,13 +6450,13 @@ is-png@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-png/-/is-png-2.0.0.tgz#ee8cbc9e9b050425cedeeb4a6fb74a649b0a4a8d"
   integrity sha512-4KPGizaVGj2LK7xwJIz8o5B2ubu1D/vcQsgOGFEDlpcvgZHto4gBnyd0ig7Ws+67ixmwKoNmu0hYnpo6AaKb5g==
 
-is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f"
-  integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ==
+is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+  integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
   dependencies:
     call-bind "^1.0.2"
-    has-symbols "^1.0.2"
+    has-tostringtag "^1.0.0"
 
 is-relative@^1.0.0:
   version "1.0.0"
@@ -6818,20 +6475,27 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
   resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
   integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
 
+is-shared-array-buffer@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
+  integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
+
 is-stream@^1.0.0, is-stream@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
   integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
 
 is-stream@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
-  integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+  integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
 
-is-string@^1.0.5, is-string@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f"
-  integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w==
+is-string@^1.0.5, is-string@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+  integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+  dependencies:
+    has-tostringtag "^1.0.0"
 
 is-subset@^0.1.1:
   version "0.1.1"
@@ -6846,22 +6510,22 @@ is-svg@^4.2.1:
     fast-xml-parser "^3.19.0"
 
 is-symbol@^1.0.2, is-symbol@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
-  integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+  integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
   dependencies:
-    has-symbols "^1.0.1"
+    has-symbols "^1.0.2"
 
-is-typed-array@^1.1.3:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e"
-  integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug==
+is-typed-array@^1.1.3, is-typed-array@^1.1.7:
+  version "1.1.8"
+  resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79"
+  integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA==
   dependencies:
-    available-typed-arrays "^1.0.2"
+    available-typed-arrays "^1.0.5"
     call-bind "^1.0.2"
-    es-abstract "^1.18.0-next.2"
+    es-abstract "^1.18.5"
     foreach "^2.0.5"
-    has-symbols "^1.0.1"
+    has-tostringtag "^1.0.0"
 
 is-unc-path@^1.0.0:
   version "1.0.0"
@@ -6870,6 +6534,13 @@ is-unc-path@^1.0.0:
   dependencies:
     unc-path-regex "^0.1.2"
 
+is-weakref@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.1.tgz#842dba4ec17fa9ac9850df2d6efbc1737274f2a2"
+  integrity sha512-b2jKc2pQZjaeFYWEf7ScFj+Be1I+PXmlu572Q8coTXZ+LD/QQZ7ShPMst8h16riVgyXTQwUsFEl74mDvc/3MHQ==
+  dependencies:
+    call-bind "^1.0.0"
+
 isarray@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -6911,9 +6582,9 @@ istanbul-lib-coverage@^1.2.1:
   integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==
 
 istanbul-lib-coverage@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec"
-  integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
+  integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==
 
 istanbul-lib-instrument@^1.7.3:
   version "1.10.2"
@@ -6948,18 +6619,18 @@ istanbul-lib-report@^3.0.0:
     supports-color "^7.1.0"
 
 istanbul-lib-source-maps@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9"
-  integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
+  integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
   dependencies:
     debug "^4.1.1"
     istanbul-lib-coverage "^3.0.0"
     source-map "^0.6.1"
 
 istanbul-reports@^3.0.0:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b"
-  integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.5.tgz#a2580107e71279ea6d661ddede929ffc6d693384"
+  integrity sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==
   dependencies:
     html-escaper "^2.0.0"
     istanbul-lib-report "^3.0.0"
@@ -6983,9 +6654,9 @@ jake@^10.6.1:
     minimatch "^3.0.4"
 
 jasmine-core@^3.6.0:
-  version "3.7.1"
-  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.7.1.tgz#0401327f6249eac993d47bbfa18d4e8efacfb561"
-  integrity sha512-DH3oYDS/AUvvr22+xUBW62m1Xoy7tUlY1tsxKEJvl5JeJ7q8zd1K5bUwiOxdH+erj6l2vAMM3hV25Xs9/WrmuQ==
+  version "3.10.1"
+  resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.10.1.tgz#7aa6fa2b834a522315c651a128d940eca553989a"
+  integrity sha512-ooZWSDVAdh79Rrj4/nnfklL3NQVra0BcuhcuWoAwwi+znLDoUeH87AFfeX8s+YeYi6xlv5nveRyaA1v7CintfA==
 
 jasmine-enzyme@^7.1.2:
   version "7.1.2"
@@ -6999,14 +6670,14 @@ javascript-natural-sort@^0.7.1:
   resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59"
   integrity sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k=
 
-jest-worker@^26.3.0, jest-worker@^26.6.2:
-  version "26.6.2"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed"
-  integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==
+jest-worker@^27.0.2, jest-worker@^27.0.6:
+  version "27.3.1"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.3.1.tgz#0def7feae5b8042be38479799aeb7b5facac24b2"
+  integrity sha512-ks3WCzsiZaOPJl/oMsDjaf0TRiSv7ctNgs0FqRr2nARsovz6AWWy4oLElwcquGSz692DzgZQrCLScPNs5YlC4g==
   dependencies:
     "@types/node" "*"
     merge-stream "^2.0.0"
-    supports-color "^7.0.0"
+    supports-color "^8.0.0"
 
 jmespath@^0.15.0:
   version "0.15.0"
@@ -7161,7 +6832,7 @@ jsoneditor@^9.5.4:
 jsonfile@^6.0.1:
   version "6.1.0"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
-  integrity "sha1-vFWyY0eTxnnsZAMJTrE2mKbsCq4= sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="
+  integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
   dependencies:
     universalify "^2.0.0"
   optionalDependencies:
@@ -7178,82 +6849,81 @@ jsonrepair@^2.2.1:
   integrity sha512-o9Je8TceILo872uQC9fIBJm957j1Io7z8Ca1iWIqY6S5S65HGE9XN7XEEw7+tUviB9Vq4sygV89MVTxl+rhZyg==
 
 jss-plugin-camel-case@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.5.1.tgz#427b24a9951b4c2eaa7e3d5267acd2e00b0934f9"
-  integrity sha512-9+oymA7wPtswm+zxVti1qiowC5q7bRdCJNORtns2JUj/QHp2QPXYwSNRD8+D2Cy3/CEMtdJzlNnt5aXmpS6NAg==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.8.2.tgz#8d7f915c8115afaff8cbde08faf610ec9892fba6"
+  integrity sha512-2INyxR+1UdNuKf4v9It3tNfPvf7IPrtkiwzofeKuMd5D58/dxDJVUQYRVg/n460rTlHUfsEQx43hDrcxi9dSPA==
   dependencies:
     "@babel/runtime" "^7.3.1"
     hyphenate-style-name "^1.0.3"
-    jss "10.5.1"
+    jss "10.8.2"
 
 jss-plugin-default-unit@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.5.1.tgz#2be385d71d50aee2ee81c2a9ac70e00592ed861b"
-  integrity sha512-D48hJBc9Tj3PusvlillHW8Fz0y/QqA7MNmTYDQaSB/7mTrCZjt7AVRROExoOHEtd2qIYKOYJW3Jc2agnvsXRlQ==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.8.2.tgz#c66f12e02e0815d911b85c02c2a979ee7b4ce69a"
+  integrity sha512-UZ7cwT9NFYSG+SEy7noRU50s4zifulFdjkUNKE+u6mW7vFP960+RglWjTgMfh79G6OENZmaYnjHV/gcKV4nSxg==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.5.1"
+    jss "10.8.2"
 
 jss-plugin-global@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.5.1.tgz#0e1793dea86c298360a7e2004721351653c7e764"
-  integrity sha512-jX4XpNgoaB8yPWw/gA1aPXJEoX0LNpvsROPvxlnYe+SE0JOhuvF7mA6dCkgpXBxfTWKJsno7cDSCgzHTocRjCQ==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.8.2.tgz#1a35632a693cf50113bcc5ffe6b51969df79c4ec"
+  integrity sha512-UaYMSPsYZ7s/ECGoj4KoHC2jwQd5iQ7K+FFGnCAILdQrv7hPmvM2Ydg45ThT/sH46DqktCRV2SqjRuxeBH8nRA==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.5.1"
+    jss "10.8.2"
 
 jss-plugin-nested@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.5.1.tgz#8753a80ad31190fb6ac6fdd39f57352dcf1295bb"
-  integrity sha512-xXkWKOCljuwHNjSYcXrCxBnjd8eJp90KVFW1rlhvKKRXnEKVD6vdKXYezk2a89uKAHckSvBvBoDGsfZrldWqqQ==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.8.2.tgz#79f3c7f75ea6a36ae72fe52e777035bb24d230c7"
+  integrity sha512-acRvuPJOb930fuYmhkJaa994EADpt8TxI63Iyg96C8FJ9T2xRyU5T6R1IYKRwUiqZo+2Sr7fdGzRTDD4uBZaMA==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.5.1"
+    jss "10.8.2"
     tiny-warning "^1.0.2"
 
 jss-plugin-props-sort@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.5.1.tgz#ab1c167fd2d4506fb6a1c1d66c5f3ef545ff1cd8"
-  integrity sha512-t+2vcevNmMg4U/jAuxlfjKt46D/jHzCPEjsjLRj/J56CvP7Iy03scsUP58Iw8mVnaV36xAUZH2CmAmAdo8994g==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.8.2.tgz#e25a7471868652c394562b6dc5433dcaea7dff6f"
+  integrity sha512-wqdcjayKRWBZnNpLUrXvsWqh+5J5YToAQ+8HNBNw0kZxVvCDwzhK2Nx6AKs7p+5/MbAh2PLgNW5Ym/ysbVAuqQ==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.5.1"
+    jss "10.8.2"
 
 jss-plugin-rule-value-function@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.5.1.tgz#37f4030523fb3032c8801fab48c36c373004de7e"
-  integrity sha512-3gjrSxsy4ka/lGQsTDY8oYYtkt2esBvQiceGBB4PykXxHoGRz14tbCK31Zc6DHEnIeqsjMUGbq+wEly5UViStQ==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.8.2.tgz#55354b55f1b2968a15976729968f767f02d64049"
+  integrity sha512-bW0EKAs+0HXpb6BKJhrn94IDdiWb0CnSluTkh0rGEgyzY/nmD1uV/Wf6KGlesGOZ9gmJzQy+9FFdxIUID1c9Ug==
   dependencies:
     "@babel/runtime" "^7.3.1"
-    jss "10.5.1"
+    jss "10.8.2"
     tiny-warning "^1.0.2"
 
 jss-plugin-vendor-prefixer@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.5.1.tgz#45a183a3a0eb097bdfab0986b858d99920c0bbd8"
-  integrity sha512-cLkH6RaPZWHa1TqSfd2vszNNgxT1W0omlSjAd6hCFHp3KIocSrW21gaHjlMU26JpTHwkc+tJTCQOmE/O1A4FKQ==
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.8.2.tgz#ebb4a482642f34091e454901e21176441dd5f475"
+  integrity sha512-DeGv18QsSiYLSVIEB2+l0af6OToUe0JB+trpzUxyqD2QRC/5AzzDrCrYffO5AHZ81QbffYvSN/pkfZaTWpRXlg==
   dependencies:
     "@babel/runtime" "^7.3.1"
     css-vendor "^2.0.8"
-    jss "10.5.1"
+    jss "10.8.2"
 
-jss@10.5.1, jss@^10.5.1:
-  version "10.5.1"
-  resolved "https://registry.yarnpkg.com/jss/-/jss-10.5.1.tgz#93e6b2428c840408372d8b548c3f3c60fa601c40"
-  integrity sha512-hbbO3+FOTqVdd7ZUoTiwpHzKXIo5vGpMNbuXH1a0wubRSWLWSBvwvaq4CiHH/U42CmjOnp6lVNNs/l+Z7ZdDmg==
+jss@10.8.2, jss@^10.5.1:
+  version "10.8.2"
+  resolved "https://registry.yarnpkg.com/jss/-/jss-10.8.2.tgz#4b2a30b094b924629a64928236017a52c7c97505"
+  integrity sha512-FkoUNxI329CKQ9OQC8L72MBF9KPf5q8mIupAJ5twU7G7XREW7ahb+7jFfrjZ4iy1qvhx1HwIWUIvkZBDnKkEdQ==
   dependencies:
     "@babel/runtime" "^7.3.1"
     csstype "^3.0.2"
-    indefinite-observable "^2.0.1"
     is-in-browser "^1.1.3"
     tiny-warning "^1.0.2"
 
 "jsx-ast-utils@^2.4.1 || ^3.0.0":
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
-  integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b"
+  integrity sha512-uP5vu8xfy2F9A6LGC22KO7e2/vGTS1MhP+18f++ZNlf0Ohaxbc9nIEwHAsejlJKyzfZzU5UIhe5ItYkitcZnZA==
   dependencies:
-    array-includes "^3.1.2"
+    array-includes "^3.1.3"
     object.assign "^4.1.2"
 
 junk@^3.1.0:
@@ -7298,9 +6968,9 @@ karma-coverage@^2.0.3:
     minimatch "^3.0.4"
 
 karma-jasmine-html-reporter@^1.4.0:
-  version "1.6.0"
-  resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.6.0.tgz#586e17025a1b4128e9fba55d5f1e8921bfc3bc1e"
-  integrity sha512-ELO9yf0cNqpzaNLsfFgXd/wxZVYkE2+ECUwhMHUD4PZ17kcsPsYsVyjquiRqyMn2jkd2sHt0IeMyAyq1MC23Fw==
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.7.0.tgz#52c489a74d760934a1089bfa5ea4a8fcb84cc28b"
+  integrity sha512-pzum1TL7j90DTE86eFt48/s12hqwQuiD+e5aXx2Dc9wDEn2LfGq6RoAxEZZjFiN0RDSCOnosEKRZWxbQ+iMpQQ==
 
 karma-jasmine@^4.0.1:
   version "4.0.1"
@@ -7380,9 +7050,9 @@ kind-of@^6.0.2:
   integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
 
 klona@^2.0.4:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0"
-  integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
+  integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==
 
 labeled-stream-splicer@^2.0.0:
   version "2.0.2"
@@ -7405,6 +7075,11 @@ levn@^0.4.1:
     prelude-ls "^1.2.1"
     type-check "~0.4.0"
 
+lilconfig@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.3.tgz#68f3005e921dafbd2a2afb48379986aa6d2579fd"
+  integrity sha512-EHKqr/+ZvdKCifpNrJCKxBTgk5XupZA3y/aCPY9mxfgBzmgh93Mt/WqjjQ38oMxXuvDokaKiM3lAgvSH2sjtHg==
+
 lines-and-columns@^1.1.6:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
@@ -7425,9 +7100,9 @@ loader-utils@^1.0.3, loader-utils@^1.1.0, loader-utils@^1.4.0:
     json5 "^1.0.1"
 
 loader-utils@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0"
-  integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
+  integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
   dependencies:
     big.js "^5.2.2"
     emojis-list "^3.0.0"
@@ -7459,11 +7134,6 @@ lodash._getnative@^3.0.0:
   resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
   integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
 
-lodash.clonedeep@^4.5.0:
-  version "4.5.0"
-  resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
-  integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
-
 lodash.debounce@^4.0.8:
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@@ -7526,6 +7196,11 @@ lodash.memoize@~3.0.3:
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-3.0.4.tgz#2dcbd2c287cbc0a55cc42328bd0c736150d53e3f"
   integrity sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=
 
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
 lodash.truncate@^4.4.2:
   version "4.4.2"
   resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@@ -7536,7 +7211,7 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
 
-lodash@4.*, lodash@^4.14.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4:
+lodash@4, lodash@4.*, lodash@^4.14.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4:
   version "4.17.21"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -7626,9 +7301,9 @@ make-fetch-happen@^9.1.0:
     ssri "^8.0.0"
 
 marked@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/marked/-/marked-2.0.1.tgz#5e7ed7009bfa5c95182e4eb696f85e948cefcee3"
-  integrity sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-2.1.3.tgz#bd017cef6431724fd4b27e0657f5ceb14bff3753"
+  integrity sha512-/Q+7MGzaETqifOMWYEA7HVMaZb4XbcRfaOzcSsHZEith83KGlvaSG33u0SKu89Mj5h+T8V2hM+8O45Qc5XTgwA==
 
 matches-selector@0.0.1:
   version "0.0.1"
@@ -7679,7 +7354,7 @@ microbuffer@^1.0.0:
   resolved "https://registry.yarnpkg.com/microbuffer/-/microbuffer-1.0.0.tgz#8b3832ed40c87d51f47bb234913a698a756d19d2"
   integrity sha1-izgy7UDIfVH0e7I0kTppinVtGdI=
 
-micromatch@^4.0.2:
+micromatch@^4.0.4:
   version "4.0.4"
   resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9"
   integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==
@@ -7695,17 +7370,17 @@ miller-rabin@^4.0.0:
     bn.js "^4.0.0"
     brorand "^1.0.1"
 
-mime-db@1.46.0, mime-db@^1.28.0:
-  version "1.46.0"
-  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee"
-  integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==
+mime-db@1.50.0, mime-db@^1.28.0:
+  version "1.50.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"
+  integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==
 
 mime-types@^2.1.27, mime-types@~2.1.24:
-  version "2.1.29"
-  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2"
-  integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==
+  version "2.1.33"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb"
+  integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==
   dependencies:
-    mime-db "1.46.0"
+    mime-db "1.50.0"
 
 mime@^2.0.3, mime@^2.3.1:
   version "2.5.2"
@@ -7728,9 +7403,9 @@ mimic-response@^1.0.0:
   integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
 
 mini-css-extract-plugin@^1.3.5:
-  version "1.3.9"
-  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.3.9.tgz#47a32132b0fd97a119acd530e8421e8f6ab16d5e"
-  integrity sha512-Ac4s+xhVbqlyhXS5J/Vh/QXUz3ycXlCqoCPpg0vdfhsIBH9eg/It/9L1r1XhSCH737M1lqcWnMuWL13zcygn5A==
+  version "1.6.2"
+  resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz#83172b4fd812f8fc4a09d6f6d16f924f53990ca8"
+  integrity sha512-WhDvO3SjGm40oV5y26GjMJYjd2UMqrLAGKy5YS2/3QKJy2F7jgynuHTir/tgUUOiNQu5saXHdc8reo7YuhhT4Q==
   dependencies:
     loader-utils "^2.0.0"
     schema-utils "^3.0.0"
@@ -7844,9 +7519,9 @@ ml-array-min@^1.2.2:
     is-any-array "^1.0.0"
 
 ml-array-rescale@^1.3.5:
-  version "1.3.5"
-  resolved "https://registry.yarnpkg.com/ml-array-rescale/-/ml-array-rescale-1.3.5.tgz#a41a98535e5b3bcdcde2f1ef532f4453feb11104"
-  integrity sha512-czK+faN7kYrF48SgVQeXGkxUjDEas6BA4EzF4jJNh8UEtzpSvHW3RllZCJCCyrAqeFc+Y/LhgYUzuHFpevM3qA==
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/ml-array-rescale/-/ml-array-rescale-1.3.6.tgz#060d1c636fbb5f877265f4fcc4e0e157521d615a"
+  integrity sha512-Lzj45T6hvHNdht924JQhHzInIK+ilC55zn98uraZUvLBkOWOJGOztEkRM0xyzAjWvVuhpszLADOnoVwfBSnj8w==
   dependencies:
     is-any-array "^1.0.0"
     ml-array-max "^1.2.3"
@@ -7925,26 +7600,31 @@ ms@2.0.0:
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
   integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
 
-ms@2.1.2, ms@^2.0.0:
+ms@2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
+ms@^2.0.0:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
 nan@^2.14.2:
   version "2.15.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
   integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
 
-nanocolors@^0.1.12:
-  version "0.1.12"
-  resolved "https://registry.yarnpkg.com/nanocolors/-/nanocolors-0.1.12.tgz#8577482c58cbd7b5bb1681db4cf48f11a87fd5f6"
-  integrity sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==
-
-nanoid@^3.0.0, nanoid@^3.1.23:
+nanoid@^3.0.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c"
   integrity "sha1-YmZ1Itpmc5ccypFqbT7/P0Ff+Aw= sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA=="
 
+nanoid@^3.1.30:
+  version "3.1.30"
+  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.30.tgz#63f93cc548d2a113dc5dfbc63bfa09e2b9b64362"
+  integrity sha512-zJpuPDwOv8D2zq2WRoMe1HsfZthVewpel9CAvTfc/2mBD1uUT/agc5f7GHGWXlYkFvi1mVxe4IjvP2HNrop7nQ==
+
 nanopop@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/nanopop/-/nanopop-2.1.0.tgz#23476513cee2405888afd2e8a4b54066b70b9e60"
@@ -7988,9 +7668,9 @@ nice-try@^1.0.4:
   integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
 
 node-gyp@^8.1.0:
-  version "8.3.0"
-  resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.3.0.tgz#ebc36a146d45095e1c6af6ccb0e47d1c8fc3fe69"
-  integrity sha512-e+vmKyTiybKgrmvs4M2REFKCnOd+NcrAAnn99Yko6NQA+zZdMlRvbIUHojfsHrSQ1CddLgZnHicnEVgDHziJzA==
+  version "8.4.0"
+  resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.0.tgz#6e1112b10617f0f8559c64b3f737e8109e5a8338"
+  integrity sha512-Bi/oCm5bH6F+FmzfUxJpPaxMEyIhszULGR3TprmTeku8/dMFcdTcypk120NeZqEt54r1BrgEKtm2jJiuIKE28Q==
   dependencies:
     env-paths "^2.2.0"
     glob "^7.1.4"
@@ -8003,11 +7683,6 @@ node-gyp@^8.1.0:
     tar "^6.1.2"
     which "^2.0.2"
 
-node-releases@^1.1.71:
-  version "1.1.72"
-  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe"
-  integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw==
-
 node-releases@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
@@ -8039,10 +7714,10 @@ normalize-url@2.0.1:
     query-string "^5.0.1"
     sort-keys "^2.0.0"
 
-normalize-url@^4.5.0:
-  version "4.5.1"
-  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
-  integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
+normalize-url@^6.0.1:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
+  integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
 
 notificar@^1.0.1:
   version "1.0.1"
@@ -8113,10 +7788,10 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
   integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
 
-object-inspect@^1.10.3, object-inspect@^1.7.0, object-inspect@^1.9.0:
-  version "1.10.3"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369"
-  integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==
+object-inspect@^1.11.0, object-inspect@^1.7.0, object-inspect@^1.9.0:
+  version "1.11.0"
+  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
+  integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
 
 object-is@^1.0.2, object-is@^1.1.2:
   version "1.1.5"
@@ -8141,42 +7816,49 @@ object.assign@^4.1.0, object.assign@^4.1.2:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
-object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.3:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd"
-  integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==
+object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.4:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
+  integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.2"
+    es-abstract "^1.19.1"
 
 object.fromentries@^2.0.3, object.fromentries@^2.0.4:
-  version "2.0.4"
-  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8"
-  integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251"
+  integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.2"
-    has "^1.0.3"
+    es-abstract "^1.19.1"
 
 object.getownpropertydescriptors@^2.1.0:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7"
-  integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ==
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e"
+  integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.2"
+    es-abstract "^1.19.1"
 
-object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.3:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30"
-  integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==
+object.hasown@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.0.tgz#7232ed266f34d197d15cac5880232f7a4790afe5"
+  integrity sha512-MhjYRfj3GBlhSkDHo6QmvgjRLXQ2zndabdf3nX0yTyZK9rPfxb6uRpAac8HXNLy1GpqWtZ81Qh4v3uOls2sRAg==
+  dependencies:
+    define-properties "^1.1.3"
+    es-abstract "^1.19.1"
+
+object.values@^1.1.0, object.values@^1.1.1, object.values@^1.1.4:
+  version "1.1.5"
+  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
+  integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.2"
+    es-abstract "^1.19.1"
 
 on-finished@~2.3.0:
   version "2.3.0"
@@ -8598,19 +8280,20 @@ postcss-calc@^8.0.0:
     postcss-selector-parser "^6.0.2"
     postcss-value-parser "^4.0.2"
 
-postcss-colormin@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.1.1.tgz#834d262f6021f832d9085e355f08ade288a92a1d"
-  integrity sha512-SyTmqKKN6PyYNeeKEC0hqIP5CDuprO1hHurdW1aezDyfofDUOn7y7MaxcolbsW3oazPwFiGiY30XRiW1V4iZpA==
+postcss-colormin@^5.2.1:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-5.2.1.tgz#6e444a806fd3c578827dbad022762df19334414d"
+  integrity sha512-VVwMrEYLcHYePUYV99Ymuoi7WhKrMGy/V9/kTS0DkCoJYmmjdOMneyhzYUxcNgteKDVbrewOkSM7Wje/MFwxzA==
   dependencies:
-    browserslist "^4.16.0"
-    colord "^2.0.0"
+    browserslist "^4.16.6"
+    caniuse-api "^3.0.0"
+    colord "^2.9.1"
     postcss-value-parser "^4.1.0"
 
-postcss-convert-values@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.1.tgz#4ec19d6016534e30e3102fdf414e753398645232"
-  integrity sha512-C3zR1Do2BkKkCgC0g3sF8TS0koF2G+mN8xxayZx3f10cIRmTaAnpgpRQZjNekTZxM2ciSPoh2IWJm0VZx8NoQg==
+postcss-convert-values@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-5.0.2.tgz#879b849dc3677c7d6bc94b6a2c1a3f0808798059"
+  integrity sha512-KQ04E2yadmfa1LqXm7UIDwW1ftxU/QWZmz6NKnHnUvJ3LEYbbcX6i329f/ig+WnEByHegulocXrECaZGLpL8Zg==
   dependencies:
     postcss-value-parser "^4.1.0"
 
@@ -8635,9 +8318,9 @@ postcss-discard-overridden@^5.0.1:
   integrity sha512-Y28H7y93L2BpJhrdUR2SR2fnSsT+3TVx1NmVQLbcnZWwIUpJ7mfcTC6Za9M2PG6w8j7UQRfzxqn8jU2VwFxo3Q==
 
 postcss-loader@^5.0.0:
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.1.0.tgz#8a36f18b8989bee94172626b25f2b9cc6160fb43"
-  integrity sha512-tGgKZF6Ntn16zIWXt7yKV19L0rISaozHPCfdPt+aHOnTZrreeqVR6hCkFhZYfJ6KgpyIFRkKuW8ygHtUid4GlA==
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-5.3.0.tgz#1657f869e48d4fdb018a40771c235e499ee26244"
+  integrity sha512-/+Z1RAmssdiSLgIZwnJHwBMnlABPgF7giYzTN2NOfr9D21IJZ4mQC1R2miwp80zno9M4zMD/umGI8cR+2EL5zw==
   dependencies:
     cosmiconfig "^7.0.0"
     klona "^2.0.4"
@@ -8652,12 +8335,12 @@ postcss-merge-longhand@^5.0.2:
     postcss-value-parser "^4.1.0"
     stylehacks "^5.0.1"
 
-postcss-merge-rules@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.1.tgz#4ff61c5089d86845184a0f149e88d687028bef7e"
-  integrity sha512-UR6R5Ph0c96QB9TMBH3ml8/kvPCThPHepdhRqAbvMRDRHQACPC8iM5NpfIC03+VRMZTGXy4L/BvFzcDFCgb+fA==
+postcss-merge-rules@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-5.0.2.tgz#d6e4d65018badbdb7dcc789c4f39b941305d410a"
+  integrity sha512-5K+Md7S3GwBewfB4rjDeol6V/RZ8S+v4B66Zk2gChRqLTCC8yjnHQ601omj9TKftS19OPGqZ/XzoqpzNQQLwbg==
   dependencies:
-    browserslist "^4.16.0"
+    browserslist "^4.16.6"
     caniuse-api "^3.0.0"
     cssnano-utils "^2.0.1"
     postcss-selector-parser "^6.0.5"
@@ -8670,13 +8353,13 @@ postcss-minify-font-values@^5.0.1:
   dependencies:
     postcss-value-parser "^4.1.0"
 
-postcss-minify-gradients@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.1.tgz#2dc79fd1a1afcb72a9e727bc549ce860f93565d2"
-  integrity sha512-odOwBFAIn2wIv+XYRpoN2hUV3pPQlgbJ10XeXPq8UY2N+9ZG42xu45lTn/g9zZ+d70NKSQD6EOi6UiCMu3FN7g==
+postcss-minify-gradients@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-5.0.3.tgz#f970a11cc71e08e9095e78ec3a6b34b91c19550e"
+  integrity sha512-Z91Ol22nB6XJW+5oe31+YxRsYooxOdFKcbOqY/V8Fxse1Y3vqlNRpi1cxCqoACZTQEhl+xvt4hsbWiV5R+XI9Q==
   dependencies:
+    colord "^2.9.1"
     cssnano-utils "^2.0.1"
-    is-color-stop "^1.1.0"
     postcss-value-parser "^4.1.0"
 
 postcss-minify-params@^5.0.1:
@@ -8777,13 +8460,13 @@ postcss-normalize-unicode@^5.0.1:
     browserslist "^4.16.0"
     postcss-value-parser "^4.1.0"
 
-postcss-normalize-url@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.1.tgz#ffa9fe545935d8b57becbbb7934dd5e245513183"
-  integrity sha512-hkbG0j58Z1M830/CJ73VsP7gvlG1yF+4y7Fd1w4tD2c7CaA2Psll+pQ6eQhth9y9EaqZSLzamff/D0MZBMbYSg==
+postcss-normalize-url@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-5.0.2.tgz#ddcdfb7cede1270740cf3e4dfc6008bd96abc763"
+  integrity sha512-k4jLTPUxREQ5bpajFQZpx8bCF2UrlqOTzP9kEqcEnOfwsRshWs2+oAFIHfDQB8GO2PaUaSE0NlTAYtbluZTlHQ==
   dependencies:
     is-absolute-url "^3.0.3"
-    normalize-url "^4.5.0"
+    normalize-url "^6.0.1"
     postcss-value-parser "^4.1.0"
 
 postcss-normalize-whitespace@^5.0.1:
@@ -8793,10 +8476,10 @@ postcss-normalize-whitespace@^5.0.1:
   dependencies:
     postcss-value-parser "^4.1.0"
 
-postcss-ordered-values@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.1.tgz#79ef6e2bd267ccad3fc0c4f4a586dfd01c131f64"
-  integrity sha512-6mkCF5BQ25HvEcDfrMHCLLFHlraBSlOXFnQMHYhSpDO/5jSR1k8LdEXOkv+7+uzW6o6tBYea1Km0wQSRkPJkwA==
+postcss-ordered-values@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-5.0.2.tgz#1f351426977be00e0f765b3164ad753dac8ed044"
+  integrity sha512-8AFYDSOYWebJYLyJi3fyjl6CqMEG/UVworjiyK1r573I56kb3e879sCJLGvR3merj+fAdPpVplXKQZv+ey6CgQ==
   dependencies:
     cssnano-utils "^2.0.1"
     postcss-value-parser "^4.1.0"
@@ -8825,13 +8508,13 @@ postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4, postcss-selector
     cssesc "^3.0.0"
     util-deprecate "^1.0.2"
 
-postcss-svgo@^5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.1.tgz#6ed5e01e164e59204978994d844c653a331a8100"
-  integrity sha512-cD7DFo6tF9i5eWvwtI4irKOHCpmASFS0xvZ5EQIgEdA1AWfM/XiHHY/iss0gcKHhkqwgYmuo2M0KhJLd5Us6mg==
+postcss-svgo@^5.0.3:
+  version "5.0.3"
+  resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-5.0.3.tgz#d945185756e5dfaae07f9edb0d3cae7ff79f9b30"
+  integrity sha512-41XZUA1wNDAZrQ3XgWREL/M2zSw8LJPvb5ZWivljBsUQAGoEKMYm6okHsTjJxKYI4M75RQEH4KYlEM52VwdXVA==
   dependencies:
     postcss-value-parser "^4.1.0"
-    svgo "^2.3.0"
+    svgo "^2.7.0"
 
 postcss-unique-selectors@^5.0.1:
   version "5.0.1"
@@ -8847,14 +8530,14 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0:
   resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb"
   integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
 
-postcss@^8.2.15, postcss@^8.2.6, postcss@^8.2.9:
-  version "8.2.15"
-  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.15.tgz#9e66ccf07292817d226fc315cbbf9bc148fbca65"
-  integrity sha512-2zO3b26eJD/8rb106Qu2o7Qgg52ND5HPjcyQiK2B98O388h43A448LCslC0dI2P97wCAQRJsFvwTRcXxTKds+Q==
+postcss@^8.2.15, postcss@^8.3.5:
+  version "8.3.11"
+  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858"
+  integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA==
   dependencies:
-    colorette "^1.2.2"
-    nanoid "^3.1.23"
-    source-map "^0.6.1"
+    nanoid "^3.1.30"
+    picocolors "^1.0.0"
+    source-map-js "^0.6.2"
 
 precond@^0.2.3:
   version "0.2.3"
@@ -9201,6 +8884,13 @@ react-checkbox-tree@^1.7.2:
     nanoid "^3.0.0"
     prop-types "^15.5.8"
 
+react-data-grid@^7.0.0-beta.11:
+  version "7.0.0-beta.11"
+  resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-7.0.0-beta.11.tgz#16e87f87ac2d1f2c33816837f1be3c4210f1e4b2"
+  integrity sha512-IjJf3GZ7HxH7uSoDaQhKXV9+L8I64xRKgLVQNCblSgvEY20mg2XlMmEjiV9KqROTUM2MqI+IlEpeBLCZRB3mEw==
+  dependencies:
+    clsx "^1.1.1"
+
 react-dom@^16.6.3:
   version "16.14.0"
   resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89"
@@ -9253,10 +8943,17 @@ react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.0, react-
   resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
   integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
 
-react-property@1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/react-property/-/react-property-1.0.1.tgz#4ae4211557d0a0ae050a71aa8ad288c074bea4e6"
-  integrity sha512-1tKOwxFn3dXVomH6pM9IkLkq2Y8oh+fh/lYW3MJ/B03URswUTqttgckOlbxY2XHF3vPG6uanSc4dVsLW/wk3wQ==
+react-leaflet@^3.2.2:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/react-leaflet/-/react-leaflet-3.2.2.tgz#3d779662c32a1d8729e73d24e8ad96ee5b38beef"
+  integrity sha512-5W7iWjI9+CdTGVAICe8RUyK0n+uLshr+eLQa8eBCbmstPNpCHZJTUSbju4Ws5dkS/PUCr9t5VmoIE9CXuSBEhw==
+  dependencies:
+    "@react-leaflet/core" "^1.1.1"
+
+react-property@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/react-property/-/react-property-2.0.0.tgz#2156ba9d85fa4741faf1918b38efc1eae3c6a136"
+  integrity sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==
 
 react-rnd@^10.3.5:
   version "10.3.5"
@@ -9323,17 +9020,7 @@ react-timer-hook@^3.0.5:
   resolved "https://registry.yarnpkg.com/react-timer-hook/-/react-timer-hook-3.0.5.tgz#a8d930f99b180cd88da245965a26a17df3e7457b"
   integrity sha512-n+98SdmYvui2ne3KyWb3Ldu4k0NYQa3g/VzW6VEIfZJ8GAk/jJsIY700M8Nd2vNSTj05c7wKyQfJBqZ0x7zfiA==
 
-react-transition-group@^4.0.0, react-transition-group@^4.3.0, react-transition-group@^4.4.0:
-  version "4.4.1"
-  resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
-  integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
-  dependencies:
-    "@babel/runtime" "^7.5.5"
-    dom-helpers "^5.0.1"
-    loose-envify "^1.4.0"
-    prop-types "^15.6.2"
-
-react-transition-group@^4.4.2:
+react-transition-group@^4.0.0, react-transition-group@^4.3.0, react-transition-group@^4.4.0, react-transition-group@^4.4.2:
   version "4.4.2"
   resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
   integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
@@ -9417,13 +9104,6 @@ readable-stream@^3.4.0, readable-stream@^3.5.0, readable-stream@^3.6.0:
     string_decoder "^1.1.1"
     util-deprecate "^1.0.1"
 
-readdirp@~3.5.0:
-  version "3.5.0"
-  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"
-  integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==
-  dependencies:
-    picomatch "^2.2.1"
-
 readdirp@~3.6.0:
   version "3.6.0"
   resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -9432,9 +9112,9 @@ readdirp@~3.6.0:
     picomatch "^2.2.1"
 
 rechoir@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.0.tgz#32650fd52c21ab252aa5d65b19310441c7e03aca"
-  integrity sha512-ADsDEH2bvbjltXEP+hTIAmeFekTFK0V2BTxMkok6qILyAJEXV0AFfoWcAq4yfll5VdIMd/RVXq0lR+wQi5ZU3Q==
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
+  integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==
   dependencies:
     resolve "^1.9.0"
 
@@ -9468,9 +9148,9 @@ regenerator-runtime@^0.11.0:
   integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
 
 regenerator-runtime@^0.13.4:
-  version "0.13.7"
-  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
-  integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+  version "0.13.9"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
+  integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
 
 regenerator-transform@^0.14.2:
   version "0.14.5"
@@ -9488,9 +9168,9 @@ regexp.prototype.flags@^1.3.1:
     define-properties "^1.1.3"
 
 regexpp@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
-  integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+  integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
 
 regexpu-core@^4.7.1:
   version "4.7.1"
@@ -9632,16 +9312,6 @@ rfdc@^1.3.0:
   resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
   integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
 
-rgb-regex@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1"
-  integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE=
-
-rgba-regex@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
-  integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
-
 rifm@^0.7.0:
   version "0.7.0"
   resolved "https://registry.yarnpkg.com/rifm/-/rifm-0.7.0.tgz#debe951a9c83549ca6b33e5919f716044c2230be"
@@ -9710,9 +9380,9 @@ sass-loader@^11.0.0:
     neo-async "^2.6.2"
 
 sass-resources-loader@^2.2.1:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/sass-resources-loader/-/sass-resources-loader-2.2.1.tgz#d7dbc36ccb25b2d8ffa508b1b8630b987df1e5c3"
-  integrity sha512-WlofxbWOVnxad874IHZdWbP9eW1pbyqsTJKBsMbeB+YELvLSrZQNDTpH70s6F19BwtanR3NEFnyGwJ9WyotJTQ==
+  version "2.2.4"
+  resolved "https://registry.yarnpkg.com/sass-resources-loader/-/sass-resources-loader-2.2.4.tgz#1a86fba499e74a88cb7ce95f0c98449f348d360e"
+  integrity sha512-hIQhBygYky+rLf+4cuoGYONZ623CEH4Swo1fs1WRJkukbqdvN1VIu2KCL59du6vX92bNELzNkGPLx+NorN73xA==
   dependencies:
     async "^3.2.0"
     chalk "^4.1.0"
@@ -9720,11 +9390,11 @@ sass-resources-loader@^2.2.1:
     loader-utils "^2.0.0"
 
 sass@^1.24.4:
-  version "1.32.8"
-  resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.8.tgz#f16a9abd8dc530add8834e506878a2808c037bdc"
-  integrity sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==
+  version "1.43.4"
+  resolved "https://registry.yarnpkg.com/sass/-/sass-1.43.4.tgz#68c7d6a1b004bef49af0d9caf750e9b252105d1f"
+  integrity sha512-/ptG7KE9lxpGSYiXn7Ar+lKOv37xfWsZRtFYal2QHNigyVQDx685VFT/h7ejVr+R8w7H4tmUgtulsKl5YpveOg==
   dependencies:
-    chokidar ">=2.0.0 <4.0.0"
+    chokidar ">=3.0.0 <4.0.0"
 
 sax@^1.2.4, sax@~1.2.4:
   version "1.2.4"
@@ -9772,12 +9442,12 @@ schema-utils@^2.6.5:
     ajv "^6.12.4"
     ajv-keywords "^3.5.2"
 
-schema-utils@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef"
-  integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA==
+schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281"
+  integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==
   dependencies:
-    "@types/json-schema" "^7.0.6"
+    "@types/json-schema" "^7.0.8"
     ajv "^6.12.5"
     ajv-keywords "^3.5.2"
 
@@ -9820,14 +9490,7 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
 
-semver@^7.2.1, semver@^7.3.4:
-  version "7.3.4"
-  resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
-  integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
-  dependencies:
-    lru-cache "^6.0.0"
-
-semver@^7.3.5:
+semver@^7.2.1, semver@^7.3.4, semver@^7.3.5:
   version "7.3.5"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
   integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
@@ -9841,6 +9504,13 @@ serialize-javascript@^5.0.1:
   dependencies:
     randombytes "^2.1.0"
 
+serialize-javascript@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+  integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+  dependencies:
+    randombytes "^2.1.0"
+
 set-blocking@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
@@ -9903,9 +9573,9 @@ shebang-regex@^3.0.0:
   integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
 
 shell-quote@^1.6.1:
-  version "1.7.2"
-  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"
-  integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
+  version "1.7.3"
+  resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123"
+  integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==
 
 shim-loader@^1.0.1:
   version "1.0.1"
@@ -9927,9 +9597,9 @@ side-channel@^1.0.4:
     object-inspect "^1.9.0"
 
 signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
-  integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f"
+  integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ==
 
 simple-concat@^1.0.0:
   version "1.0.1"
@@ -9937,11 +9607,11 @@ simple-concat@^1.0.0:
   integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
 
 sirv@^1.0.7:
-  version "1.0.12"
-  resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.12.tgz#d816c882b35489b3c63290e2f455ae3eccd5f652"
-  integrity sha512-+jQoCxndz7L2tqQL4ZyzfDhky0W/4ZJip3XoOuxyQWnAwMxindLl3Xv1qT4x1YX/re0leShvTm8Uk0kQspGhBg==
+  version "1.0.18"
+  resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.18.tgz#105fab52fb656ce8a2bebbf36b11052005952899"
+  integrity sha512-f2AOPogZmXgJ9Ma2M22ZEhc1dNtRIzcEkiflMFeVTRq+OViOZMvH1IPMVOwrKaxpSaHioBJiDR0SluRqGa7atA==
   dependencies:
-    "@polka/url" "^1.0.0-next.15"
+    "@polka/url" "^1.0.0-next.20"
     mime "^2.3.1"
     totalist "^1.0.0"
 
@@ -9991,17 +9661,16 @@ socket.io-adapter@~2.3.3:
   integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ==
 
 socket.io-client@^4.0.0:
-  version "4.1.2"
-  resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.1.2.tgz#95ad7113318ea01fba0860237b96d71e1b1fd2eb"
-  integrity sha512-RDpWJP4DQT1XeexmeDyDkm0vrFc0+bUsHDKiVGaNISJvJonhQQOMqV9Vwfg0ZpPJ27LCdan7iqTI92FRSOkFWQ==
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.3.2.tgz#9cfdb8fecac8a24d5723daf8c8749e70c8fdeb25"
+  integrity sha512-2B9LqSunN60yV8F7S84CCEEcgbYNfrn7ejIInZtLZ7ppWtiX8rGZAjvdCvbnC8bqo/9RlCNOUsORLyskxSFP1g==
   dependencies:
-    "@types/component-emitter" "^1.2.10"
+    "@socket.io/component-emitter" "~3.0.0"
     backo2 "~1.0.2"
-    component-emitter "~1.3.0"
-    debug "~4.3.1"
-    engine.io-client "~5.1.1"
+    debug "~4.3.2"
+    engine.io-client "~6.0.1"
     parseuri "0.0.6"
-    socket.io-parser "~4.0.4"
+    socket.io-parser "~4.1.1"
 
 socket.io-parser@~4.0.4:
   version "4.0.4"
@@ -10012,6 +9681,14 @@ socket.io-parser@~4.0.4:
     component-emitter "~1.3.0"
     debug "~4.3.1"
 
+socket.io-parser@~4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.1.1.tgz#0ad53d980781cab1eabe320417d8480c0133e62d"
+  integrity sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==
+  dependencies:
+    "@socket.io/component-emitter" "~3.0.0"
+    debug "~4.3.1"
+
 socket.io@^4.2.0:
   version "4.4.1"
   resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.1.tgz#cd6de29e277a161d176832bb24f64ee045c56ab8"
@@ -10067,15 +9744,20 @@ source-list-map@^1.1.1:
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1"
   integrity sha1-mIkBnRAkzOVc3AaUmDN+9hhqEaE=
 
-source-list-map@^2.0.0, source-list-map@^2.0.1:
+source-list-map@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
   integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
 
-source-map-support@^0.5.5, source-map-support@~0.5.19:
-  version "0.5.19"
-  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
-  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
+source-map-js@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e"
+  integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug==
+
+source-map-support@^0.5.5, source-map-support@~0.5.20:
+  version "0.5.20"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9"
+  integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==
   dependencies:
     buffer-from "^1.0.0"
     source-map "^0.6.0"
@@ -10169,7 +9851,7 @@ stream-splicer@^2.0.0:
 streamroller@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.0.2.tgz#30418d0eee3d6c93ec897f892ed098e3a81e68b7"
-  integrity "sha1-MEGNDu49bJPsiX+JLtCY46geaLc= sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA=="
+  integrity sha512-ur6y5S5dopOaRXBuRIZ1u6GC5bcEXHRZKgfBjfCglMhmIf+roVCECjvkEYzNQOXIN2/JPnkMPW/8B3CZoKaEPA==
   dependencies:
     date-format "^4.0.3"
     debug "^4.1.1"
@@ -10189,14 +9871,14 @@ string-width@^1.0.1:
     is-fullwidth-code-point "^1.0.0"
     strip-ansi "^3.0.0"
 
-"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0:
-  version "4.2.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5"
-  integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==
+"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
   dependencies:
     emoji-regex "^8.0.0"
     is-fullwidth-code-point "^3.0.0"
-    strip-ansi "^6.0.0"
+    strip-ansi "^6.0.1"
 
 string.fromcodepoint@^0.2.1:
   version "0.2.1"
@@ -10208,14 +9890,14 @@ string.prototype.codepointat@^0.2.1:
   resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc"
   integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg==
 
-string.prototype.matchall@^4.0.4:
-  version "4.0.5"
-  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da"
-  integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==
+string.prototype.matchall@^4.0.5:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa"
+  integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.2"
+    es-abstract "^1.19.1"
     get-intrinsic "^1.1.1"
     has-symbols "^1.0.2"
     internal-slot "^1.0.3"
@@ -10223,13 +9905,13 @@ string.prototype.matchall@^4.0.4:
     side-channel "^1.0.4"
 
 string.prototype.trim@^1.2.1:
-  version "1.2.4"
-  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.4.tgz#6014689baf5efaf106ad031a5fa45157666ed1bd"
-  integrity sha512-hWCk/iqf7lp0/AgTF7/ddO1IWtSNPASjlzCicV5irAVdE1grjsneK26YG6xACMBEdCvO8fUST0UzDMh/2Qy+9Q==
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c"
+  integrity sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg==
   dependencies:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
-    es-abstract "^1.18.0-next.2"
+    es-abstract "^1.19.1"
 
 string.prototype.trimend@^1.0.4:
   version "1.0.4"
@@ -10273,12 +9955,12 @@ strip-ansi@^3.0.0, strip-ansi@^3.0.1:
   dependencies:
     ansi-regex "^2.0.0"
 
-strip-ansi@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
-  integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
   dependencies:
-    ansi-regex "^5.0.0"
+    ansi-regex "^5.0.1"
 
 strip-comments@^2.0.1:
   version "2.0.1"
@@ -10314,6 +9996,11 @@ strip-outer@^1.0.0:
   dependencies:
     escape-string-regexp "^1.0.2"
 
+strnum@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.4.tgz#e97e36a7d6ba9f93d0d6b496b2ed0678d422832b"
+  integrity sha512-lMzNMfDpaQOLt4B2mEbfzYS0+T7dvCXeojnlGf6f1AygvWDMcWyXYaLbyICfjVu29sErR8fnRagQfBW/N/hGgw==
+
 style-loader@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c"
@@ -10337,16 +10024,16 @@ style-to-object@0.3.0:
     inline-style-parser "0.1.1"
 
 styled-components@^5.2.1:
-  version "5.2.1"
-  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.2.1.tgz#6ed7fad2dc233825f64c719ffbdedd84ad79101a"
-  integrity sha512-sBdgLWrCFTKtmZm/9x7jkIabjFNVzCUeKfoQsM6R3saImkUnjx0QYdLwJHBjY9ifEcmjDamJDVfknWm1yxZPxQ==
+  version "5.3.3"
+  resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.3.tgz#312a3d9a549f4708f0fb0edc829eb34bde032743"
+  integrity sha512-++4iHwBM7ZN+x6DtPPWkCI4vdtwumQ+inA/DdAsqYd4SVgUKJie5vXyzotA00ttcFdQkCng7zc6grwlfIfw+lw==
   dependencies:
     "@babel/helper-module-imports" "^7.0.0"
     "@babel/traverse" "^7.4.5"
     "@emotion/is-prop-valid" "^0.8.8"
     "@emotion/stylis" "^0.8.4"
     "@emotion/unitless" "^0.7.4"
-    babel-plugin-styled-components ">= 1"
+    babel-plugin-styled-components ">= 1.12.0"
     css-to-react-native "^3.0.0"
     hoist-non-react-statics "^3.0.0"
     shallowequal "^1.1.0"
@@ -10365,7 +10052,7 @@ stylis@4.0.13:
   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
   integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
 
-stylis@^4.0.3, stylis@^4.0.7:
+stylis@^4.0.10, stylis@^4.0.7:
   version "4.0.10"
   resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.10.tgz#446512d1097197ab3f02fb3c258358c3f7a14240"
   integrity sha512-m3k+dk7QeJw660eIKRRn3xPF6uuvHs/FFzjX3HQ5ove0qYsiygoAhwn5a3IYKaZPo5LrYD0rfVmtv1gNY1uYwg==
@@ -10389,13 +10076,20 @@ supports-color@^5.3.0, supports-color@^5.5.0:
   dependencies:
     has-flag "^3.0.0"
 
-supports-color@^7.0.0, supports-color@^7.1.0:
+supports-color@^7.1.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
   integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
   dependencies:
     has-flag "^4.0.0"
 
+supports-color@^8.0.0:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
 svg-parser@^2.0.2:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5"
@@ -10461,19 +10155,6 @@ svgo@^1.2.2, svgo@^1.3.2:
     unquote "~1.1.1"
     util.promisify "~1.0.0"
 
-svgo@^2.3.0:
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.7.0.tgz#e164cded22f4408fe4978f082be80159caea1e2d"
-  integrity sha512-aDLsGkre4fTDCWvolyW+fs8ZJFABpzLXbtdK1y71CKnHzAnpDxKXPj2mNKj+pyOXUCzFHzuxRJ94XOFygOWV3w==
-  dependencies:
-    "@trysound/sax" "0.2.0"
-    commander "^7.2.0"
-    css-select "^4.1.3"
-    css-tree "^1.1.3"
-    csso "^4.2.0"
-    nanocolors "^0.1.12"
-    stable "^0.1.8"
-
 svgo@^2.7.0:
   version "2.8.0"
   resolved "https://registry.yarnpkg.com/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24"
@@ -10492,11 +10173,6 @@ svgpath@^2.1.5:
   resolved "https://registry.yarnpkg.com/svgpath/-/svgpath-2.3.1.tgz#b102334bebd2244b4818460ba2ebad52716a0d43"
   integrity sha512-wNz6lCoj+99GMoyU7SozTfPqiLHz6WcJYZ30Z+F4lF/gPtxWHBCpZ4DhoDI0+oZ0dObKyYsJdSPGbL2mJq/qCg==
 
-symbol-observable@1.2.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
-  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
-
 syntax-error@^1.1.1:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c"
@@ -10504,17 +10180,16 @@ syntax-error@^1.1.1:
   dependencies:
     acorn-node "^1.2.0"
 
-table@^6.0.4:
-  version "6.7.1"
-  resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2"
-  integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==
+table@^6.0.9:
+  version "6.7.3"
+  resolved "https://registry.yarnpkg.com/table/-/table-6.7.3.tgz#255388439715a738391bd2ee4cbca89a4d05a9b7"
+  integrity sha512-5DkIxeA7XERBqMwJq0aHZOdMadBx4e6eDoFRuyT5VR82J0Ycg2DwM6GfA/EQAhJ+toRTaS1lIdSQCqgrmhPnlw==
   dependencies:
     ajv "^8.0.1"
-    lodash.clonedeep "^4.5.0"
     lodash.truncate "^4.4.2"
     slice-ansi "^4.0.0"
-    string-width "^4.2.0"
-    strip-ansi "^6.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
 
 tablesorter@^2.31.2:
   version "2.31.3"
@@ -10524,9 +10199,9 @@ tablesorter@^2.31.2:
     jquery ">=1.2.6"
 
 tapable@^2.1.1, tapable@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
-  integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
+  integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
 
 tar-stream@^1.5.2:
   version "1.6.2"
@@ -10586,26 +10261,33 @@ tempusdominus-core@^5.0.3:
     moment "~2.24.0"
     moment-timezone "^0.5.28"
 
-terser-webpack-plugin@^5.1.1:
-  version "5.1.2"
-  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.1.2.tgz#51d295eb7cc56785a67a372575fdc46e42d5c20c"
-  integrity sha512-6QhDaAiVHIQr5Ab3XUWZyDmrIPCHMiqJVljMF91YKyqwKkL5QHnYMkrMBy96v9Z7ev1hGhSEw1HQZc2p/s5Z8Q==
+terser-webpack-plugin@^5.1.1, terser-webpack-plugin@^5.1.3:
+  version "5.2.4"
+  resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz#ad1be7639b1cbe3ea49fab995cbe7224b31747a1"
+  integrity sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA==
   dependencies:
-    jest-worker "^26.6.2"
+    jest-worker "^27.0.6"
     p-limit "^3.1.0"
-    schema-utils "^3.0.0"
-    serialize-javascript "^5.0.1"
+    schema-utils "^3.1.1"
+    serialize-javascript "^6.0.0"
     source-map "^0.6.1"
-    terser "^5.7.0"
+    terser "^5.7.2"
 
-terser@^5.7.0:
-  version "5.7.0"
-  resolved "https://registry.yarnpkg.com/terser/-/terser-5.7.0.tgz#a761eeec206bc87b605ab13029876ead938ae693"
-  integrity sha512-HP5/9hp2UaZt5fYkuhNBR8YyRcT8juw8+uFbAme53iN9hblvKnLUTKkmwJG6ocWpIKf8UK4DoeWG4ty0J6S6/g==
+terser@^5.7.2:
+  version "5.9.0"
+  resolved "https://registry.yarnpkg.com/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351"
+  integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ==
   dependencies:
     commander "^2.20.0"
     source-map "~0.7.2"
-    source-map-support "~0.5.19"
+    source-map-support "~0.5.20"
+
+text-segmentation@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/text-segmentation/-/text-segmentation-1.0.2.tgz#1f828fa14aa101c114ded1bda35ba7dcc17c9858"
+  integrity sha512-uTqvLxdBrVnx/CFQOtnf8tfzSXFm+1Qxau7Xi54j4OPTZokuDOX8qncQzrg2G8ZicAMOM8TgzFAYTb+AqNO4Cw==
+  dependencies:
+    utrie "^1.0.1"
 
 text-table@^0.2.0:
   version "0.2.0"
@@ -10653,11 +10335,11 @@ tiny-warning@^1.0.2:
   integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
 
 tippy.js@^6.3.1:
-  version "6.3.1"
-  resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.1.tgz#3788a007be7015eee0fd589a66b98fb3f8f10181"
-  integrity sha512-JnFncCq+rF1dTURupoJ4yPie5Cof978inW6/4S6kmWV7LL9YOSEVMifED3KdrVPEG+Z/TFH2CDNJcQEfaeuQww==
+  version "6.3.5"
+  resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.5.tgz#cbc99d34f87ccc127e6460032b86c8d47971d38f"
+  integrity sha512-B9hAQ5KNF+jDJRg6cRysV6Y3J+5fiNfD60GuXR5TP0sfrcltpgdzVc7f1wMtjQ3W0+Xsy80CDvk0Z+Vr0cM4sQ==
   dependencies:
-    "@popperjs/core" "^2.8.3"
+    "@popperjs/core" "^2.9.0"
 
 tmp@^0.2.1:
   version "0.2.1"
@@ -10688,11 +10370,6 @@ to-regex-range@^5.0.1:
   dependencies:
     is-number "^7.0.0"
 
-toggle-selection@^1.0.6:
-  version "1.0.6"
-  resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
-  integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI=
-
 toidentifier@1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
@@ -10721,9 +10398,9 @@ tslib@2.3.0:
   integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
 
 tslib@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
-  integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
+  integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
 
 ttf2eot@^2.0.0:
   version "2.0.0"
@@ -10776,10 +10453,10 @@ type-fest@^0.11.0:
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
   integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
 
-type-fest@^0.8.1:
-  version "0.8.1"
-  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
-  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
+type-fest@^0.20.2:
+  version "0.20.2"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+  integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
 
 type-is@~1.6.17:
   version "1.6.18"
@@ -10805,9 +10482,9 @@ ua-parser-js@^0.7.30:
   integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
 
 uglify-js@^3.1.4:
-  version "3.14.2"
-  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.2.tgz#d7dd6a46ca57214f54a2d0a43cad0f35db82ac99"
-  integrity sha512-rtPMlmcO4agTUfz10CbgJ1k6UAoXM2gWb3GoMPPZB/+/Ackf8lNWk11K4rYi2D0apgoFRLtQOZhb+/iGNJq26A==
+  version "3.14.3"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.14.3.tgz#c0f25dfea1e8e5323eccf59610be08b6043c15cf"
+  integrity sha512-mic3aOdiq01DuSVx0TseaEzMIVqebMZ0Z3vaeDhFEh9bsc24hV1TFvN74reA2vs08D0ZWfNjAcJ3UbVLaBss+g==
 
 umd@^3.0.0:
   version "3.0.3"
@@ -10921,7 +10598,7 @@ unique-slug@^2.0.0:
 universalify@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
-  integrity "sha1-daSYTv7cSwiXXFrrc/Uw0C3yVxc= sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="
+  integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
 
 unpipe@1.0.0, unpipe@~1.0.0:
   version "1.0.0"
@@ -11004,9 +10681,9 @@ util@0.10.3:
     inherits "2.0.1"
 
 util@~0.12.0:
-  version "0.12.3"
-  resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888"
-  integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog==
+  version "0.12.4"
+  resolved "https://registry.yarnpkg.com/util/-/util-0.12.4.tgz#66121a31420df8f01ca0c464be15dfa1d1850253"
+  integrity sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==
   dependencies:
     inherits "^2.0.3"
     is-arguments "^1.0.4"
@@ -11020,12 +10697,19 @@ utils-merge@1.0.1:
   resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
   integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
 
+utrie@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/utrie/-/utrie-1.0.1.tgz#e155235ebcbddc89ae09261ab6e773ce61401b2f"
+  integrity sha512-JPaDXF3vzgZxfeEwutdGzlrNoVFL5UvZcbO6Qo9D4GoahrieUPoMU8GCpVpR7MQqcKhmShIh8VlbEN3PLM3EBg==
+  dependencies:
+    base64-arraybuffer "^1.0.1"
+
 uuid@^3.0.1:
   version "3.4.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
   integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
 
-v8-compile-cache@^2.0.3, v8-compile-cache@^2.2.0:
+v8-compile-cache@^2.0.3:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
   integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
@@ -11038,9 +10722,9 @@ valid-filename@^2.0.1:
     filename-reserved-regex "^2.0.0"
 
 vanilla-picker@^2.11.2:
-  version "2.11.2"
-  resolved "https://registry.yarnpkg.com/vanilla-picker/-/vanilla-picker-2.11.2.tgz#eaa24efa68c27e7ee9e0776df55d6913b855f133"
-  integrity sha512-2cP7LlUnxHxwOf06ReUVtd2RFJMnJGaxN2s0p8wzBH3In5b00Le7fFZ9VrIoBE0svZkSq/BC/Pwq/k/9n+AA2g==
+  version "2.12.1"
+  resolved "https://registry.yarnpkg.com/vanilla-picker/-/vanilla-picker-2.12.1.tgz#6e619eecf553891b8d2d042b745a23c91f19f34c"
+  integrity sha512-2qrEP9VYylKXbyzXKsbu2dferBTvqnlsr29XjHwFE+/MEp0VNj6oEUESLDtKZ7DWzGdSv1x/+ujqFZF+KsO3cg==
   dependencies:
     "@sphinxxxx/color-conversion" "^2.2.2"
 
@@ -11071,7 +10755,7 @@ void-elements@^2.0.0:
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
   integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
 
-watchpack@^2.0.0:
+watchpack@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.2.0.tgz#47d78f5415fe550ecd740f99fe2882323a58b1ce"
   integrity sha512-up4YAn/XHgZHIxFBVCdlMiWDj6WaLKpwVeGQk2I5thdYxF/KmF0aaz6TfJZ/hfl1h/XlcDr7k1KH7ThDagpFaA==
@@ -11098,14 +10782,14 @@ webfonts-loader@^7.3.0:
     loader-utils "^2.0.0"
 
 webpack-bundle-analyzer@^4.4.0:
-  version "4.4.2"
-  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.2.tgz#39898cf6200178240910d629705f0f3493f7d666"
-  integrity sha512-PIagMYhlEzFfhMYOzs5gFT55DkUdkyrJi/SxJp8EF3YMWhS+T9vvs2EoTetpk5qb6VsCq02eXTlRDOydRhDFAQ==
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
+  integrity sha512-GUMZlM3SKwS8Z+CKeIFx7CVoHn3dXFcUAjT/dcZQQmfSZGvitPfMob2ipjai7ovFFqPvTqkEZ/leL4O0YOdAYQ==
   dependencies:
     acorn "^8.0.4"
     acorn-walk "^8.0.0"
     chalk "^4.1.0"
-    commander "^6.2.0"
+    commander "^7.2.0"
     gzip-size "^6.0.0"
     lodash "^4.17.20"
     opener "^1.5.2"
@@ -11113,22 +10797,21 @@ webpack-bundle-analyzer@^4.4.0:
     ws "^7.3.1"
 
 webpack-cli@^4.5.0:
-  version "4.7.0"
-  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.7.0.tgz#3195a777f1f802ecda732f6c95d24c0004bc5a35"
-  integrity sha512-7bKr9182/sGfjFm+xdZSwgQuFjgEcy0iCTIBxRUeteJ2Kr8/Wz0qNJX+jw60LU36jApt4nmMkep6+W5AKhok6g==
+  version "4.9.1"
+  resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.1.tgz#b64be825e2d1b130f285c314caa3b1ba9a4632b3"
+  integrity sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==
   dependencies:
     "@discoveryjs/json-ext" "^0.5.0"
-    "@webpack-cli/configtest" "^1.0.3"
-    "@webpack-cli/info" "^1.2.4"
-    "@webpack-cli/serve" "^1.4.0"
-    colorette "^1.2.1"
+    "@webpack-cli/configtest" "^1.1.0"
+    "@webpack-cli/info" "^1.4.0"
+    "@webpack-cli/serve" "^1.6.0"
+    colorette "^2.0.14"
     commander "^7.0.0"
     execa "^5.0.0"
     fastest-levenshtein "^1.0.12"
     import-local "^3.0.2"
     interpret "^2.2.0"
     rechoir "^0.7.0"
-    v8-compile-cache "^2.2.0"
     webpack-merge "^5.7.3"
 
 webpack-merge@^4.1.5:
@@ -11139,9 +10822,9 @@ webpack-merge@^4.1.5:
     lodash "^4.17.15"
 
 webpack-merge@^5.7.3:
-  version "5.7.3"
-  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.7.3.tgz#2a0754e1877a25a8bbab3d2475ca70a052708213"
-  integrity sha512-6/JUQv0ELQ1igjGDzHkXbVDRxkfA57Zw7PfiupdLFJYrgFqY5ZP8xxbpp2lU3EPwYx89ht5Z/aDkD40hFCm5AA==
+  version "5.8.0"
+  resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
+  integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
   dependencies:
     clone-deep "^4.0.1"
     wildcard "^2.0.0"
@@ -11162,30 +10845,28 @@ webpack-sources@^1.1.0:
     source-list-map "^2.0.0"
     source-map "~0.6.1"
 
-webpack-sources@^2.1.1:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.3.0.tgz#9ed2de69b25143a4c18847586ad9eccb19278cfa"
-  integrity sha512-WyOdtwSvOML1kbgtXbTDnEW0jkJ7hZr/bDByIwszhWd/4XX1A3XMkrbFMsuH4+/MfLlZCUzlAdg4r7jaGKEIgQ==
-  dependencies:
-    source-list-map "^2.0.1"
-    source-map "^0.6.1"
+webpack-sources@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.1.tgz#251a7d9720d75ada1469ca07dbb62f3641a05b6d"
+  integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==
 
 webpack@^5.21.2:
-  version "5.24.3"
-  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.24.3.tgz#6ec0f5059f8d7c7961075fa553cfce7b7928acb3"
-  integrity sha512-x7lrWZ7wlWAdyKdML6YPvfVZkhD1ICuIZGODE5SzKJjqI9A4SpqGTjGJTc6CwaHqn19gGaoOR3ONJ46nYsn9rw==
+  version "5.61.0"
+  resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.61.0.tgz#fa827f0ee9bdfd141dd73c3e891e955ebd52fe7f"
+  integrity sha512-fPdTuaYZ/GMGFm4WrPi2KRCqS1vDp773kj9S0iI5Uc//5cszsFEDgHNaX4Rj1vobUiU1dFIV3mA9k1eHeluFpw==
   dependencies:
     "@types/eslint-scope" "^3.7.0"
-    "@types/estree" "^0.0.46"
-    "@webassemblyjs/ast" "1.11.0"
-    "@webassemblyjs/wasm-edit" "1.11.0"
-    "@webassemblyjs/wasm-parser" "1.11.0"
-    acorn "^8.0.4"
+    "@types/estree" "^0.0.50"
+    "@webassemblyjs/ast" "1.11.1"
+    "@webassemblyjs/wasm-edit" "1.11.1"
+    "@webassemblyjs/wasm-parser" "1.11.1"
+    acorn "^8.4.1"
+    acorn-import-assertions "^1.7.6"
     browserslist "^4.14.5"
     chrome-trace-event "^1.0.2"
-    enhanced-resolve "^5.7.0"
-    es-module-lexer "^0.4.0"
-    eslint-scope "^5.1.1"
+    enhanced-resolve "^5.8.3"
+    es-module-lexer "^0.9.0"
+    eslint-scope "5.1.1"
     events "^3.2.0"
     glob-to-regexp "^0.4.1"
     graceful-fs "^4.2.4"
@@ -11193,11 +10874,11 @@ webpack@^5.21.2:
     loader-runner "^4.2.0"
     mime-types "^2.1.27"
     neo-async "^2.6.2"
-    schema-utils "^3.0.0"
+    schema-utils "^3.1.0"
     tapable "^2.1.1"
-    terser-webpack-plugin "^5.1.1"
-    watchpack "^2.0.0"
-    webpack-sources "^2.1.1"
+    terser-webpack-plugin "^5.1.3"
+    watchpack "^2.2.0"
+    webpack-sources "^3.2.0"
 
 which-boxed-primitive@^1.0.2:
   version "1.0.2"
@@ -11211,17 +10892,16 @@ which-boxed-primitive@^1.0.2:
     is-symbol "^1.0.3"
 
 which-typed-array@^1.1.2:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff"
-  integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793"
+  integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw==
   dependencies:
-    available-typed-arrays "^1.0.2"
-    call-bind "^1.0.0"
-    es-abstract "^1.18.0-next.1"
+    available-typed-arrays "^1.0.5"
+    call-bind "^1.0.2"
+    es-abstract "^1.18.5"
     foreach "^2.0.5"
-    function-bind "^1.1.1"
-    has-symbols "^1.0.1"
-    is-typed-array "^1.1.3"
+    has-tostringtag "^1.0.0"
+    is-typed-array "^1.1.7"
 
 which@^1.2.1, which@^1.2.9:
   version "1.3.1"
@@ -11280,10 +10960,10 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
 
-ws@^7.3.1, ws@~7.4.2:
-  version "7.4.6"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
-  integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==
+ws@^7.3.1:
+  version "7.5.5"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
+  integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
 
 ws@~8.2.3:
   version "8.2.3"
@@ -11295,6 +10975,11 @@ xmldom@~0.5.0:
   resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.5.0.tgz#193cb96b84aa3486127ea6272c4596354cb4962e"
   integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==
 
+xmlhttprequest-ssl@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
+  integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
+
 xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
@@ -11306,9 +10991,9 @@ xterm-addon-fit@^0.5.0:
   integrity sha512-DsS9fqhXHacEmsPxBJZvfj2la30Iz9xk+UKjhQgnYNkrUIN5CYLbw7WEfz117c7+S86S/tpHPfvNxJsF5/G8wQ==
 
 xterm-addon-search@^0.8.0:
-  version "0.8.0"
-  resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.0.tgz#e33eab918df7eac7e7baf95dd2b3d14133754881"
-  integrity sha512-MPJGPVPpHRUw9cLIuqQbrVepmENMOybVUSxIALz5h1ryyQBrVqVujq2hL5aroX5/dZJoHx9lGHQTVLQ07SKgKA==
+  version "0.8.1"
+  resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.8.1.tgz#dfc557e9bcf5fd8ed96292c0d271aa865bc545d5"
+  integrity sha512-OtOaC9gxD2Q4ZnjZrCSRZmKLwwUjXX3gP7mIzq8Rs50317DGRDqgTLuHTYv/Nx/LvI5ceVFRYCxK36Ixs1nXNw==
 
 xterm-addon-web-links@^0.4.0:
   version "0.4.0"
@@ -11316,9 +11001,9 @@ xterm-addon-web-links@^0.4.0:
   integrity sha512-xv8GeiINmx0zENO9hf5k+5bnkaE8mRzF+OBAr9WeFq2eLaQSudioQSiT34M1ofKbzcdjSsKiZm19Rw3i4eXamg==
 
 xterm@^4.11.0:
-  version "4.12.0"
-  resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.12.0.tgz#db09b425b4dcae5b96f8cbbaaa93b3bc60997ca9"
-  integrity sha512-K5mF/p3txUV18mjiZFlElagoHFpqXrm5OYHeoymeXSu8GG/nMaOO/+NRcNCwfdjzAbdQ5VLF32hEHiWWKKm0bw==
+  version "4.14.1"
+  resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.14.1.tgz#6884cb8fb3b83353b1a98139ea23daedf8e35796"
+  integrity sha512-jgzNg5BuGPwq5/M4dGnmbghZvHx2jaj+9crSEt15bV34Za49VziBmCu7zIy88zUKKiGTxeo7aVzirFSJArIMFw==
 
 y18n@^5.0.5:
   version "5.0.8"
@@ -11335,15 +11020,15 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yaml@^1.10.0, yaml@^1.7.2:
+yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2:
   version "1.10.2"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
 
 yargs-parser@^20.2.2:
-  version "20.2.6"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.6.tgz#69f920addf61aafc0b8b89002f5d66e28f2d8b20"
-  integrity sha512-AP1+fQIWSM/sMiET8fyayjx/J+JmTPt2Mr0FkrgqB4todtfa53sOsrSAcIrJRD5XS20bKUwaDIuMkWKCEiQLKA==
+  version "20.2.9"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+  integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
 
 yargs@^16.1.1:
   version "16.2.0"
