mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fixed following issues for Query Tool (after React Porting):
1) Find/Replace both opens the same dialogue box.(by clicking menu option) 2) Add New Server Connection > Server options keep loading(For multiple Server groups & should have some server) 3) Fixed CSS issues of slickgrid at various places. 4) C should be captial in ’<New connection…>' 5) In pop title for New Connection, all words should be capital.(Add new connection) 6) Explain > Analaysis tab > Column heading missing ROWS PLAN with cost & In explain only. 7) Explain > Analaysis tab > with cost enabled > Upward arrow size does not match with font of number. Arrow is little bigger than number. 8) Boolean default is not considered while ading new row.(try table from feature test defaults) 9) In query history , when not query history present, warning icon size big. Match it to warning message - No history found 10) Select table/db object > Open query tool from Tools menu > NOT FOUND error is shown. Existing issue, fixed. 11) Any cell just open by clicking it > Do NOT change any thing > Click Ok > Cell is shown as edited. refs #6131
This commit is contained in:
parent
9470c68c18
commit
e5ef6a7b21
@ -151,7 +151,7 @@
|
|||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-aspen": "^1.1.0",
|
"react-aspen": "^1.1.0",
|
||||||
"react-checkbox-tree": "^1.7.2",
|
"react-checkbox-tree": "^1.7.2",
|
||||||
"react-data-grid": "^7.0.0-beta.11",
|
"react-data-grid": "^7.0.0-beta.12",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-draggable": "^4.4.4",
|
"react-draggable": "^4.4.4",
|
||||||
"react-leaflet": "^3.2.2",
|
"react-leaflet": "^3.2.2",
|
||||||
|
@ -15,6 +15,7 @@ import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
|
|||||||
import HTMLReactParse from 'html-react-parser';
|
import HTMLReactParse from 'html-react-parser';
|
||||||
import { commonTableStyles } from '../Theme';
|
import { commonTableStyles } from '../Theme';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import gettext from 'sources/gettext';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme)=>({
|
const useStyles = makeStyles((theme)=>({
|
||||||
collapsible: {
|
collapsible: {
|
||||||
@ -172,26 +173,26 @@ export default function Analysis({explainTable}) {
|
|||||||
<th rowSpan="2"><button disabled="">#</button></th>
|
<th rowSpan="2"><button disabled="">#</button></th>
|
||||||
<th rowSpan="2"><button disabled="">Node</button></th>
|
<th rowSpan="2"><button disabled="">Node</button></th>
|
||||||
<th colSpan="2" style={explainTable.show_timings ? {} : {display: 'none'}}>
|
<th colSpan="2" style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||||
<button disabled="">Timings</button>
|
<button disabled="">{gettext('Timings')}</button>
|
||||||
</th>
|
</th>
|
||||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}}
|
<th style={(explainTable.show_rowsx || explainTable.show_rows || explainTable.show_plan_rows) ? {} : {display: 'none'}}
|
||||||
colSpan={(explainTable.show_rowsx) ? '3' : '1'}>
|
colSpan={(explainTable.show_rowsx) ? '3' : '1'}>
|
||||||
<button disabled="">Rows</button>
|
<button disabled="">{gettext('Rows')}</button>
|
||||||
</th>
|
</th>
|
||||||
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} rowSpan="2">
|
<th style={(explainTable.show_rowsx || explainTable.show_rows) ? {} : {display: 'none'}} rowSpan="2">
|
||||||
<button disabled="">Loops</button>
|
<button disabled="">{gettext('Loops')}</button>
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||||
<button disabled="">Exclusive</button>
|
<button disabled="">{gettext('Exclusive')}</button>
|
||||||
</th>
|
</th>
|
||||||
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
<th style={explainTable.show_timings ? {} : {display: 'none'}}>
|
||||||
<button disabled="">Inclusive</button>
|
<button disabled="">{gettext('Inclusive')}</button>
|
||||||
</th>
|
</th>
|
||||||
<th style={explainTable.show_rowsx ? {} : {display: 'none'}}><button disabled="">Rows X</button></th>
|
<th style={explainTable.show_rowsx ? {} : {display: 'none'}}><button disabled="">{gettext('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.show_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Actual')}</button></th>
|
||||||
<th style={(explainTable.show_rowsx || explainTable.plan_rows) ? {} : {display: 'none'}}><button disabled="">Plan</button></th>
|
<th style={(explainTable.show_rowsx || explainTable.show_plan_rows) ? {} : {display: 'none'}}><button disabled="">{gettext('Plan')}</button></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -92,7 +92,7 @@ export default function ExplainStatistics({explainTable}) {
|
|||||||
<tbody>
|
<tbody>
|
||||||
{_.sortBy(Object.keys(explainTable.statistics.tables)).map((key, i)=>{
|
{_.sortBy(Object.keys(explainTable.statistics.tables)).map((key, i)=>{
|
||||||
let table = explainTable.statistics.tables[key];
|
let table = explainTable.statistics.tables[key];
|
||||||
table.sum_of_times = _.sumBy(table.nodes, 'sum_of_times');
|
table.sum_of_times = _.sumBy(Object.values(table.nodes), 'sum_of_times');
|
||||||
return <React.Fragment key={i}>
|
return <React.Fragment key={i}>
|
||||||
<tr className={classes.tableRow}>
|
<tr className={classes.tableRow}>
|
||||||
<td className={classes.tableName}>{table.name}</td>
|
<td className={classes.tableName}>{table.name}</td>
|
||||||
|
@ -419,7 +419,7 @@ export default function Graphical({planData, ctx}) {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>} />
|
</Box>} />
|
||||||
<CardContent className={classes.explainContent}>
|
<CardContent className={classes.explainContent}>
|
||||||
<table className={clsx(tableStyles.table, tableStyles.borderBottom)}>
|
<table className={clsx(tableStyles.table, tableStyles.borderBottom, tableStyles.wrapTd)}>
|
||||||
<tbody>
|
<tbody>
|
||||||
<NodeDetails download={false} plan={explainPlanDetails} />
|
<NodeDetails download={false} plan={explainPlanDetails} />
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -237,6 +237,14 @@ function nodeExplainTableData(_planData, _ctx) {
|
|||||||
info.statistics.nodes[node_info] = node;
|
info.statistics.nodes[node_info] = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseExplainTableData(plan, ctx) {
|
||||||
|
nodeExplainTableData(plan, ctx);
|
||||||
|
|
||||||
|
plan['Plans']?.map((p)=>{
|
||||||
|
parseExplainTableData(p, ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function parsePlan(data, ctx) {
|
function parsePlan(data, ctx) {
|
||||||
var idx = 1,
|
var idx = 1,
|
||||||
lvl = data.level = data.level || [idx],
|
lvl = data.level = data.level || [idx],
|
||||||
@ -408,7 +416,6 @@ function parsePlan(data, ctx) {
|
|||||||
// Final Width and Height of current node
|
// Final Width and Height of current node
|
||||||
data['width'] += maxChildWidth;
|
data['width'] += maxChildWidth;
|
||||||
data['Plans'] = plans;
|
data['Plans'] = plans;
|
||||||
nodeExplainTableData(data, ctx);
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@ -456,6 +463,8 @@ function parsePlanData(data, ctx) {
|
|||||||
if (data && 'Settings' in data) {
|
if (data && 'Settings' in data) {
|
||||||
retPlan['Statistics']['Settings'] = data['Settings'];
|
retPlan['Statistics']['Settings'] = data['Settings'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parseExplainTableData(retPlan['Plan'], ctx);
|
||||||
}
|
}
|
||||||
return retPlan;
|
return retPlan;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,18 @@ basicSettings = createMuiTheme(basicSettings, {
|
|||||||
typography: {
|
typography: {
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
htmlFontSize: 14,
|
htmlFontSize: 14,
|
||||||
|
fontFamily: [
|
||||||
|
'Roboto',
|
||||||
|
'"Helvetica Neue"',
|
||||||
|
'-apple-system',
|
||||||
|
'BlinkMacSystemFont',
|
||||||
|
'"Segoe UI"',
|
||||||
|
'Arial',
|
||||||
|
'sans-serif',
|
||||||
|
'"Apple Color Emoji"',
|
||||||
|
'"Segoe UI Emoji"',
|
||||||
|
'"Segoe UI Symbol"',
|
||||||
|
].join(','),
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
@ -281,6 +293,9 @@ function getFinalTheme(baseTheme) {
|
|||||||
overrides: {
|
overrides: {
|
||||||
MuiCssBaseline: {
|
MuiCssBaseline: {
|
||||||
'@global': {
|
'@global': {
|
||||||
|
body: {
|
||||||
|
fontFamily: baseTheme.typography.fontFamily,
|
||||||
|
},
|
||||||
ul: {
|
ul: {
|
||||||
margin: 0,
|
margin: 0,
|
||||||
padding: 0,
|
padding: 0,
|
||||||
@ -618,6 +633,11 @@ export const commonTableStyles = makeStyles((theme)=>({
|
|||||||
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
borderBottom: '1px solid '+theme.otherVars.borderColor,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
wrapTd: {
|
||||||
|
'& tbody td': {
|
||||||
|
whiteSpace: 'pre-wrap',
|
||||||
|
}
|
||||||
|
},
|
||||||
noHover: {
|
noHover: {
|
||||||
'& tbody > tr': {
|
'& tbody > tr': {
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
|
@ -462,16 +462,12 @@ export default function CodeMirror({currEditor, name, value, options, events, re
|
|||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if(editor.current) {
|
if(editor.current) {
|
||||||
if(disabled) {
|
if(readonly || disabled) {
|
||||||
editor.current.setOption('readOnly', true);
|
|
||||||
cmWrapper.current.classList.add(classes.hideCursor);
|
|
||||||
} else if(readonly) {
|
|
||||||
editor.current.setOption('readOnly', true);
|
editor.current.setOption('readOnly', true);
|
||||||
editor.current.addKeyMap({'Tab': false});
|
editor.current.addKeyMap({'Tab': false});
|
||||||
editor.current.addKeyMap({'Shift-Tab': false});
|
editor.current.addKeyMap({'Shift-Tab': false});
|
||||||
cmWrapper.current.classList.add(classes.hideCursor);
|
cmWrapper.current.classList.add(classes.hideCursor);
|
||||||
} else {
|
} else {
|
||||||
cmWrapper.current.classList.remove('cm_disabled');
|
|
||||||
editor.current.setOption('readOnly', false);
|
editor.current.setOption('readOnly', false);
|
||||||
editor.current.removeKeyMap('Tab');
|
editor.current.removeKeyMap('Tab');
|
||||||
editor.current.removeKeyMap('Shift-Tab');
|
editor.current.removeKeyMap('Shift-Tab');
|
||||||
|
@ -9,7 +9,9 @@ const useStyles = makeStyles((theme)=>({
|
|||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
marginTop: '24px',
|
marginTop: '24px',
|
||||||
fontSize: '0.9em',
|
fontSize: '0.8rem',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -17,8 +19,8 @@ export default function EmptyPanelMessage({text}) {
|
|||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
return (
|
return (
|
||||||
<Box className={classes.root}>
|
<Box className={classes.root}>
|
||||||
<InfoRoundedIcon />
|
<InfoRoundedIcon style={{height: '1.2rem'}}/>
|
||||||
<span marginLeft='4px'>{text}</span>
|
<span style={{marginLeft: '4px'}}>{text}</span>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -81,6 +81,9 @@ const useStyles = makeStyles((theme)=>({
|
|||||||
},
|
},
|
||||||
'& > div': {
|
'& > div': {
|
||||||
padding: '4px 10px',
|
padding: '4px 10px',
|
||||||
|
'&:focus': {
|
||||||
|
outline: '2px solid '+theme.otherVars.activeBorder,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'& .drag-initiator': {
|
'& .drag-initiator': {
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
@ -190,6 +193,11 @@ export class LayoutHelper {
|
|||||||
return Boolean(docker.find(panelId));
|
return Boolean(docker.find(panelId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static isTabVisible(docker, panelId) {
|
||||||
|
let panelData = docker.find(panelId);
|
||||||
|
return panelData?.parent?.activeId == panelData.id;
|
||||||
|
}
|
||||||
|
|
||||||
static openTab(docker, panelData, refTabId, direction, forceRerender=false) {
|
static openTab(docker, panelData, refTabId, direction, forceRerender=false) {
|
||||||
let panel = docker.find(panelData.id);
|
let panel = docker.find(panelData.id);
|
||||||
if(panel) {
|
if(panel) {
|
||||||
@ -203,6 +211,43 @@ export class LayoutHelper {
|
|||||||
docker.dockMove(LayoutHelper.getPanel(panelData), tgtPanel, direction);
|
docker.dockMove(LayoutHelper.getPanel(panelData), tgtPanel, direction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static moveTo(direction) {
|
||||||
|
let dockBar = document.activeElement.closest('.dock')?.querySelector('.dock-bar.drag-initiator');
|
||||||
|
if(dockBar) {
|
||||||
|
let key = {
|
||||||
|
key: 'ArrowRight', keyCode: 39, which: 39, code: 'ArrowRight',
|
||||||
|
metaKey: false, ctrlKey: false, shiftKey: false, altKey: false,
|
||||||
|
bubbles: true,
|
||||||
|
};
|
||||||
|
if(direction == 'right') {
|
||||||
|
key = {
|
||||||
|
...key,
|
||||||
|
key: 'ArrowRight', keyCode: 39, which: 39, code: 'ArrowRight'
|
||||||
|
};
|
||||||
|
} else if(direction == 'left') {
|
||||||
|
key = {
|
||||||
|
...key,
|
||||||
|
key: 'ArrowLeft', keyCode: 37, which: 37, code: 'ArrowLeft',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
dockBar.dispatchEvent(new KeyboardEvent('keydown', key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static switchPanel() {
|
||||||
|
let currDockPanel = document.activeElement.closest('.dock-panel.dock-style-default');
|
||||||
|
let dockLayoutPanels = currDockPanel?.closest('.dock-layout').querySelectorAll('.dock-panel.dock-style-default');
|
||||||
|
if(dockLayoutPanels?.length > 1) {
|
||||||
|
for(let i=0; i<dockLayoutPanels.length; i++) {
|
||||||
|
if(dockLayoutPanels[i] == currDockPanel) {
|
||||||
|
let newPanelIdx = (i+1)%dockLayoutPanels.length;
|
||||||
|
dockLayoutPanels[newPanelIdx]?.querySelector('.dock-tab.dock-tab-active .dock-tab-btn')?.focus();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveLayout(layoutObj, layoutId) {
|
function saveLayout(layoutObj, layoutId) {
|
||||||
|
83
web/pgadmin/static/scss/_slickgrid.overrides.scss
Normal file
83
web/pgadmin/static/scss/_slickgrid.overrides.scss
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
.slick-row .cell-actions {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-row.selected .cell-selection {
|
||||||
|
background-color: transparent; /* show default selected row background */
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $color-gray-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-selection {
|
||||||
|
border-right-color: $border-color;
|
||||||
|
border-right-style: solid;
|
||||||
|
background: $color-gray-lighter;
|
||||||
|
color: $color-gray;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-row.selected .cell-move-handle {
|
||||||
|
background: $color-warning-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-row.complete {
|
||||||
|
background-color: $color-success-light;
|
||||||
|
color: $color-gray-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-row:hover .slick-cell{
|
||||||
|
border-top: $table-hover-border;
|
||||||
|
border-bottom: $table-hover-border;
|
||||||
|
background-color: $table-hover-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-row .slick-cell {
|
||||||
|
border-bottom: $panel-border;
|
||||||
|
border-right: $panel-border;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slick-cell.active {
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-right: 1px solid $color-gray-light;
|
||||||
|
border-bottom-color: $color-gray-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui-widget-content.slick-row {
|
||||||
|
&.even, &.odd {
|
||||||
|
background: none;
|
||||||
|
background-color: $table-bg;
|
||||||
|
}
|
||||||
|
}
|
@ -35,5 +35,6 @@ $theme-colors: (
|
|||||||
@import 'jsoneditor.overrides';
|
@import 'jsoneditor.overrides';
|
||||||
@import 'pgadmin4-tree.overrides';
|
@import 'pgadmin4-tree.overrides';
|
||||||
@import 'pgadmin4-tree/src/css/styles';
|
@import 'pgadmin4-tree/src/css/styles';
|
||||||
|
@import 'slickgrid.overrides';
|
||||||
@import 'rc-dock/dist/rc-dock.css';
|
@import 'rc-dock/dist/rc-dock.css';
|
||||||
@import '@szhsin/react-menu/dist/index.css';
|
@import '@szhsin/react-menu/dist/index.css';
|
||||||
|
@ -60,6 +60,13 @@
|
|||||||
.slick-header-sortable {
|
.slick-header-sortable {
|
||||||
cursor: pointer !important;
|
cursor: pointer !important;
|
||||||
|
|
||||||
|
.slick-sort-indicator {
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
.slick-sort-indicator-asc {
|
.slick-sort-indicator-asc {
|
||||||
background: none;
|
background: none;
|
||||||
border-top: none;
|
border-top: none;
|
||||||
|
@ -348,7 +348,7 @@ export default class SQLEditor {
|
|||||||
<input name="close_url" value="${closeUrl}" hidden />`;
|
<input name="close_url" value="${closeUrl}" hidden />`;
|
||||||
|
|
||||||
|
|
||||||
if(sURL){
|
if(sURL && typeof(sURL) === 'string'){
|
||||||
queryToolForm +=`<input name="query_url" value="${sURL}" hidden />`;
|
queryToolForm +=`<input name="query_url" value="${sURL}" hidden />`;
|
||||||
}
|
}
|
||||||
if(sql_filter) {
|
if(sql_filter) {
|
||||||
|
@ -487,7 +487,7 @@ export default function QueryToolComponent({params, pgWindow, pgAdmin, selectedN
|
|||||||
const onClose = ()=>LayoutHelper.close(docker.current, 'new-conn');
|
const onClose = ()=>LayoutHelper.close(docker.current, 'new-conn');
|
||||||
LayoutHelper.openDialog(docker.current, {
|
LayoutHelper.openDialog(docker.current, {
|
||||||
id: 'new-conn',
|
id: 'new-conn',
|
||||||
title: gettext('Add new connection'),
|
title: gettext('Add New Connection'),
|
||||||
content: <NewConnectionDialog onSave={(_isNew, data)=>{
|
content: <NewConnectionDialog onSave={(_isNew, data)=>{
|
||||||
let connectionData = {
|
let connectionData = {
|
||||||
sgid: 0,
|
sgid: 0,
|
||||||
|
@ -173,6 +173,10 @@ export function TextEditor({row, column, onRowChange, onClose}) {
|
|||||||
if(column.is_array && !isValidArray(localVal)) {
|
if(column.is_array && !isValidArray(localVal)) {
|
||||||
Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
|
Notifier.error(gettext('Arrays must start with "{" and end with "}"'));
|
||||||
} else {
|
} else {
|
||||||
|
if(value == localVal) {
|
||||||
|
onClose(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let columnVal = textColumnFinalVal(localVal, column);
|
let columnVal = textColumnFinalVal(localVal, column);
|
||||||
onRowChange({ ...row, [column.key]: columnVal}, true);
|
onRowChange({ ...row, [column.key]: columnVal}, true);
|
||||||
onClose();
|
onClose();
|
||||||
@ -320,6 +324,10 @@ export function JsonTextEditor({row, column, onRowChange, onClose}) {
|
|||||||
setLocalVal(newVal);
|
setLocalVal(newVal);
|
||||||
}, []);
|
}, []);
|
||||||
const onOK = ()=>{
|
const onOK = ()=>{
|
||||||
|
if(value == localVal) {
|
||||||
|
onClose(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
onRowChange({ ...row, [column.key]: localVal}, true);
|
onRowChange({ ...row, [column.key]: localVal}, true);
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
|
@ -105,7 +105,7 @@ function CustomRow(props) {
|
|||||||
const rowInfoValue = {
|
const rowInfoValue = {
|
||||||
rowIdx: props.rowIdx,
|
rowIdx: props.rowIdx,
|
||||||
getCellElement: (colIdx)=>{
|
getCellElement: (colIdx)=>{
|
||||||
return rowRef.current.querySelector(`.rdg-cell[aria-colindex="${colIdx+1}"]`);
|
return rowRef.current?.querySelector(`.rdg-cell[aria-colindex="${colIdx+1}"]`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
|
@ -67,7 +67,8 @@ class NewConnectionSchema extends BaseUISchema {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* initial selection */
|
/* initial selection */
|
||||||
_.find(v, (o)=>o.value==obj.params.sid).selected = true;
|
let foundServer = _.find(v, (o)=>o.value==obj.params.sid);
|
||||||
|
foundServer && (foundServer.selected = true);
|
||||||
groupedOptions.push({
|
groupedOptions.push({
|
||||||
label: k,
|
label: k,
|
||||||
options: v,
|
options: v,
|
||||||
|
@ -129,7 +129,7 @@ export function ConnectionBar({connected, connecting, connectionStatus, connecti
|
|||||||
onClick={onConnItemClick}>{conn.conn_title}</PgMenuItem>
|
onClick={onConnItemClick}>{conn.conn_title}</PgMenuItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
<PgMenuItem onClick={onNewConnClick}>{`< ${gettext('New connection...')} >`}</PgMenuItem>
|
<PgMenuItem onClick={onNewConnClick}>{`< ${gettext('New Connection...')} >`}</PgMenuItem>
|
||||||
</PgMenu>
|
</PgMenu>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,10 @@ import gettext from 'sources/gettext';
|
|||||||
import Theme from 'sources/Theme';
|
import Theme from 'sources/Theme';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
import { Box } from '@material-ui/core';
|
||||||
|
import { LayoutHelper } from '../../../../../../static/js/helpers/Layout';
|
||||||
|
import { PANELS } from '../QueryToolConstants';
|
||||||
|
import { QueryToolContext } from '../QueryToolComponent';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme)=>({
|
const useStyles = makeStyles((theme)=>({
|
||||||
mapContainer: {
|
mapContainer: {
|
||||||
@ -233,7 +237,7 @@ function GeoJsonLayer({data}) {
|
|||||||
} else {
|
} else {
|
||||||
mapObj.setView(bounds.getCenter(), mapObj.getZoom());
|
mapObj.setView(bounds.getCenter(), mapObj.getZoom());
|
||||||
}
|
}
|
||||||
});
|
}, [data]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GeoJSON
|
<GeoJSON
|
||||||
@ -278,6 +282,7 @@ GeoJsonLayer.propTypes = {
|
|||||||
function TheMap({data}) {
|
function TheMap({data}) {
|
||||||
const mapObj = useMap();
|
const mapObj = useMap();
|
||||||
const infoControl = useRef(null);
|
const infoControl = useRef(null);
|
||||||
|
const resetLayersKey = useRef(0);
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
infoControl.current = Leaflet.control({position: 'topright'});
|
infoControl.current = Leaflet.control({position: 'topright'});
|
||||||
infoControl.current.onAdd = function () {
|
infoControl.current.onAdd = function () {
|
||||||
@ -288,70 +293,71 @@ function TheMap({data}) {
|
|||||||
if(data.infoList.length > 0) {
|
if(data.infoList.length > 0) {
|
||||||
infoControl.current.addTo(mapObj);
|
infoControl.current.addTo(mapObj);
|
||||||
}
|
}
|
||||||
|
resetLayersKey.current++;
|
||||||
return ()=>{infoControl.current && infoControl.current.remove();};
|
return ()=>{infoControl.current && infoControl.current.remove();};
|
||||||
}, [data]);
|
}, [data]);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{data.selectedSRID === 4326 &&
|
{data.selectedSRID === 4326 &&
|
||||||
<LayersControl position="topright">
|
<LayersControl position="topright">
|
||||||
<LayersControl.BaseLayer checked name={gettext('Empty')}>
|
<LayersControl.BaseLayer checked name={gettext('Empty')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url=""
|
url=""
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
<LayersControl.BaseLayer checked name={gettext('Street')}>
|
<LayersControl.BaseLayer checked name={gettext('Street')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||||
maxZoom={19}
|
maxZoom={19}
|
||||||
attribution='© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
|
attribution='© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>'
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
<LayersControl.BaseLayer name={gettext('Topography')}>
|
<LayersControl.BaseLayer name={gettext('Topography')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
|
url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
|
||||||
maxZoom={17}
|
maxZoom={17}
|
||||||
attribution={
|
attribution={
|
||||||
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
||||||
+ ' © <a href="http://viewfinderpanoramas.org" target="_blank">SRTM</a>,'
|
+ ' © <a href="http://viewfinderpanoramas.org" target="_blank">SRTM</a>,'
|
||||||
+ ' © <a href="https://opentopomap.org" target="_blank">OpenTopoMap</a>'
|
+ ' © <a href="https://opentopomap.org" target="_blank">OpenTopoMap</a>'
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
<LayersControl.BaseLayer name={gettext('Gray Style')}>
|
<LayersControl.BaseLayer name={gettext('Gray Style')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png"
|
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png"
|
||||||
maxZoom={19}
|
maxZoom={19}
|
||||||
attribution={
|
attribution={
|
||||||
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
||||||
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
||||||
}
|
}
|
||||||
subdomains='abcd'
|
subdomains='abcd'
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
<LayersControl.BaseLayer name={gettext('Light Color')}>
|
<LayersControl.BaseLayer name={gettext('Light Color')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager/{z}/{x}/{y}{r}.pn"
|
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/rastertiles/voyager/{z}/{x}/{y}{r}.pn"
|
||||||
maxZoom={19}
|
maxZoom={19}
|
||||||
attribution={
|
attribution={
|
||||||
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
||||||
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
||||||
}
|
}
|
||||||
subdomains='abcd'
|
subdomains='abcd'
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
<LayersControl.BaseLayer name={gettext('Dark Matter')}>
|
<LayersControl.BaseLayer name={gettext('Dark Matter')}>
|
||||||
<TileLayer
|
<TileLayer
|
||||||
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png"
|
url="https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png"
|
||||||
maxZoom={19}
|
maxZoom={19}
|
||||||
attribution={
|
attribution={
|
||||||
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
'© <a href="http://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a>,'
|
||||||
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
+ ' © <a href="http://cartodb.com/attributions" target="_blank">CartoDB</a>'
|
||||||
}
|
}
|
||||||
subdomains='abcd'
|
subdomains='abcd'
|
||||||
/>
|
/>
|
||||||
</LayersControl.BaseLayer>
|
</LayersControl.BaseLayer>
|
||||||
</LayersControl>}
|
</LayersControl>}
|
||||||
<GeoJsonLayer key={data.geoJSONs.length} data={data}/>
|
<GeoJsonLayer key={resetLayersKey.current} data={data} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -367,18 +373,40 @@ TheMap.propTypes = {
|
|||||||
|
|
||||||
export function GeometryViewer({rows, columns, column}) {
|
export function GeometryViewer({rows, columns, column}) {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const mapRef = React.useRef();
|
||||||
|
const contentRef = React.useRef();
|
||||||
const data = parseData(rows, columns, column);
|
const data = parseData(rows, columns, column);
|
||||||
|
const queryToolCtx = React.useContext(QueryToolContext);
|
||||||
const crs = data.selectedSRID === 4326 ? CRS.EPSG3857 : CRS.Simple;
|
const crs = data.selectedSRID === 4326 ? CRS.EPSG3857 : CRS.Simple;
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
let timeoutId;
|
||||||
|
const contentResizeObserver = new ResizeObserver(()=>{
|
||||||
|
clearTimeout(timeoutId);
|
||||||
|
if(LayoutHelper.isTabVisible(queryToolCtx.docker, PANELS.GEOMETRY)) {
|
||||||
|
timeoutId = setTimeout(function () {
|
||||||
|
mapRef.current?.invalidateSize();
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
contentResizeObserver.observe(contentRef.current);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MapContainer
|
<Box ref={contentRef} width="100%" height="100%">
|
||||||
crs={crs}
|
<MapContainer
|
||||||
zoom={2} center={[20, 100]}
|
crs={crs}
|
||||||
preferCanvas={true}
|
zoom={2} center={[20, 100]}
|
||||||
scrollWheelZoom={false}
|
preferCanvas={true}
|
||||||
className={classes.mapContainer}
|
scrollWheelZoom={false}
|
||||||
>
|
className={classes.mapContainer}
|
||||||
<TheMap data={data} />
|
whenCreated={(map)=>{
|
||||||
</MapContainer>
|
mapRef.current = map;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<TheMap data={data}/>
|
||||||
|
</MapContainer>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import PropTypes from 'prop-types';
|
|||||||
import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
|
import CustomPropTypes from '../../../../../../static/js/custom_prop_types';
|
||||||
import ConfirmTransactionContent from '../dialogs/ConfirmTransactionContent';
|
import ConfirmTransactionContent from '../dialogs/ConfirmTransactionContent';
|
||||||
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
|
import { isMac } from '../../../../../../static/js/keyboard_shortcuts';
|
||||||
|
import { LayoutHelper } from '../../../../../../static/js/helpers/Layout';
|
||||||
|
|
||||||
const useStyles = makeStyles((theme)=>({
|
const useStyles = makeStyles((theme)=>({
|
||||||
root: {
|
root: {
|
||||||
@ -435,6 +436,34 @@ export function MainToolBar({containerRef, onFilterClick, onManageMacros}) {
|
|||||||
containerRef
|
containerRef
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/* Panel shortcuts */
|
||||||
|
useKeyboardShortcuts([
|
||||||
|
{
|
||||||
|
shortcut: queryToolPref.move_previous,
|
||||||
|
options: {
|
||||||
|
callback: ()=>{
|
||||||
|
LayoutHelper.moveTo('left');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
shortcut: queryToolPref.move_next,
|
||||||
|
options: {
|
||||||
|
callback: ()=>{
|
||||||
|
LayoutHelper.moveTo('right');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
shortcut: queryToolPref.switch_panel,
|
||||||
|
options: {
|
||||||
|
callback: ()=>{
|
||||||
|
LayoutHelper.switchPanel(queryToolCtx.docker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
], containerRef);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Box className={classes.root}>
|
<Box className={classes.root}>
|
||||||
|
@ -311,7 +311,7 @@ export default function Query() {
|
|||||||
key.metaKey = true;
|
key.metaKey = true;
|
||||||
key.ctrlKey = false;
|
key.ctrlKey = false;
|
||||||
key.shiftKey = false;
|
key.shiftKey = false;
|
||||||
key.altKey = true;
|
key.altKey = replace;
|
||||||
}
|
}
|
||||||
editor.current?.triggerOnKeyDown(
|
editor.current?.triggerOnKeyDown(
|
||||||
new KeyboardEvent('keydown', key)
|
new KeyboardEvent('keydown', key)
|
||||||
|
@ -373,7 +373,7 @@ export function QueryHistory() {
|
|||||||
|
|
||||||
React.useEffect(async ()=>{
|
React.useEffect(async ()=>{
|
||||||
layoutEvenBus.registerListener(LAYOUT_EVENTS.ACTIVE, (currentTabId)=>{
|
layoutEvenBus.registerListener(LAYOUT_EVENTS.ACTIVE, (currentTabId)=>{
|
||||||
currentTabId == PANELS.HISTORY && listRef.current.focus();
|
currentTabId == PANELS.HISTORY && listRef.current?.focus();
|
||||||
});
|
});
|
||||||
|
|
||||||
setLoaderText(gettext('Fetching history...'));
|
setLoaderText(gettext('Fetching history...'));
|
||||||
@ -399,7 +399,7 @@ export function QueryHistory() {
|
|||||||
refresh({});
|
refresh({});
|
||||||
};
|
};
|
||||||
|
|
||||||
listRef.current.focus();
|
listRef.current?.focus();
|
||||||
eventBus.registerListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
|
eventBus.registerListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
|
||||||
return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
|
return ()=>eventBus.deregisterListener(QUERY_TOOL_EVENTS.PUSH_HISTORY, pushHistory);
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -1116,8 +1116,8 @@ export function ResultSet() {
|
|||||||
}, [selectedRows, selectedColumns, queryData, dataChangeStore, selectedCell.current]);
|
}, [selectedRows, selectedColumns, queryData, dataChangeStore, selectedCell.current]);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
const triggerAddRows = (_rows)=>{
|
const triggerAddRows = (_rows, fromClipboard)=>{
|
||||||
let newRows = rsu.current.processRows(_rows, columns, true);
|
let newRows = rsu.current.processRows(_rows, columns, fromClipboard);
|
||||||
setRows((prev)=>[...newRows, ...prev]);
|
setRows((prev)=>[...newRows, ...prev]);
|
||||||
let add = {};
|
let add = {};
|
||||||
newRows.forEach((row)=>{
|
newRows.forEach((row)=>{
|
||||||
@ -1191,7 +1191,7 @@ export function ResultSet() {
|
|||||||
|
|
||||||
const rowKeyGetter = React.useCallback((row)=>row[rsu.current.clientPK]);
|
const rowKeyGetter = React.useCallback((row)=>row[rsu.current.clientPK]);
|
||||||
return (
|
return (
|
||||||
<Box className={classes.root} ref={containerRef}>
|
<Box className={classes.root} ref={containerRef} tabIndex="0">
|
||||||
<Loader message={loaderText} />
|
<Loader message={loaderText} />
|
||||||
<Loader message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
|
<Loader message={isLoadingMore ? gettext('Loading more rows...') : null} style={{top: 'unset', right: 'unset', padding: '0.5rem 1rem'}}/>
|
||||||
{(columns.length == 0 && rows.length == 0) &&
|
{(columns.length == 0 && rows.length == 0) &&
|
||||||
|
@ -70,7 +70,7 @@ export function ResultSetToolbar({containerRef, canEdit}) {
|
|||||||
field_separator: queryToolPref.results_grid_field_separator,
|
field_separator: queryToolPref.results_grid_field_separator,
|
||||||
});
|
});
|
||||||
let copiedRows = copyUtils.getCopiedRows();
|
let copiedRows = copyUtils.getCopiedRows();
|
||||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows);
|
eventBus.fireEvent(QUERY_TOOL_EVENTS.TRIGGER_ADD_ROWS, copiedRows, true);
|
||||||
}, [queryToolPref]);
|
}, [queryToolPref]);
|
||||||
const copyData = ()=>{
|
const copyData = ()=>{
|
||||||
eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_DATA, checkedMenuItems['copy_with_headers']);
|
eventBus.fireEvent(QUERY_TOOL_EVENTS.COPY_DATA, checkedMenuItems['copy_with_headers']);
|
||||||
|
@ -8880,10 +8880,10 @@ react-checkbox-tree@^1.7.2:
|
|||||||
nanoid "^3.0.0"
|
nanoid "^3.0.0"
|
||||||
prop-types "^15.5.8"
|
prop-types "^15.5.8"
|
||||||
|
|
||||||
react-data-grid@^7.0.0-beta.11:
|
react-data-grid@^7.0.0-beta.12:
|
||||||
version "7.0.0-beta.11"
|
version "7.0.0-beta.12"
|
||||||
resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-7.0.0-beta.11.tgz#16e87f87ac2d1f2c33816837f1be3c4210f1e4b2"
|
resolved "https://registry.yarnpkg.com/react-data-grid/-/react-data-grid-7.0.0-beta.12.tgz#a6310a83a7ad4913a595a8b2a667e4951a95dc58"
|
||||||
integrity sha512-IjJf3GZ7HxH7uSoDaQhKXV9+L8I64xRKgLVQNCblSgvEY20mg2XlMmEjiV9KqROTUM2MqI+IlEpeBLCZRB3mEw==
|
integrity sha512-cgKE4fl/glKllpfY444H1ZF4mNDUfIU7kyrSYVUy8W1npTvGk9CL++ASs1pTSSi2Eg2Sx7vqnC1gEx6C92Kqjw==
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx "^1.1.1"
|
clsx "^1.1.1"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user