mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-26 22:36:00 +00:00
expandable FK columns
This commit is contained in:
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import ColumnLabel from './ColumnLabel';
|
||||
import { filterName } from '@dbgate/datalib';
|
||||
import { FontIcon } from '../icons';
|
||||
|
||||
const Wrapper = styled.div``;
|
||||
|
||||
@@ -28,6 +29,45 @@ const Input = styled.input`
|
||||
width: 80px;
|
||||
`;
|
||||
|
||||
function ExpandIcon({ display, column, isHover, ...other }) {
|
||||
if (column.foreignKey) {
|
||||
return (
|
||||
<FontIcon
|
||||
icon={`far ${display.isExpandedColumn(column.uniqueName) ? 'fa-minus-square' : 'fa-plus-square'} `}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return <FontIcon icon={`fas fa-square ${isHover ? 'lightblue' : 'white'}`} {...other} />;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} props
|
||||
* @param {import('@dbgate/datalib').GridDisplay} props.display
|
||||
* @param {import('@dbgate/datalib').DisplayColumn} props.column
|
||||
*/
|
||||
function ColumnManagerRow(props) {
|
||||
const { display, column } = props;
|
||||
const [isHover, setIsHover] = React.useState(false);
|
||||
return (
|
||||
<Row onMouseEnter={() => setIsHover(true)} onMouseLeave={() => setIsHover(false)}>
|
||||
<ExpandIcon
|
||||
display={display}
|
||||
column={column}
|
||||
isHover={isHover}
|
||||
onClick={() => display.toggleExpandedColumn(column.uniqueName)}
|
||||
/>
|
||||
<input
|
||||
type="checkbox"
|
||||
style={{ marginLeft: `${5 + (column.uniquePath.length - 1) * 10}px` }}
|
||||
checked={column.isChecked}
|
||||
onChange={() => display.setColumnVisibility(column.uniqueName, !column.isChecked)}
|
||||
></input>
|
||||
<ColumnLabel {...column} />
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
/** @param props {import('./types').DataGridProps} */
|
||||
export default function ColumnManager(props) {
|
||||
const { display } = props;
|
||||
@@ -39,17 +79,11 @@ export default function ColumnManager(props) {
|
||||
<Button onClick={() => display.hideAllColumns()}>Hide</Button>
|
||||
<Button onClick={() => display.showAllColumns()}>Show</Button>
|
||||
</SearchBoxWrapper>
|
||||
{display.columns
|
||||
.filter(col => filterName(columnFilter, col.columnName))
|
||||
.map(col => (
|
||||
<Row key={col.columnName}>
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={col.isChecked}
|
||||
onChange={() => display.setColumnVisibility(col.uniqueName, !col.isChecked)}
|
||||
></input>
|
||||
<ColumnLabel {...col} />
|
||||
</Row>
|
||||
{display
|
||||
.getColumns(columnFilter)
|
||||
.filter(column => filterName(columnFilter, column.columnName))
|
||||
.map(column => (
|
||||
<ColumnManagerRow key={column.uniqueName} display={display} column={column} />
|
||||
))}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
@@ -110,7 +110,7 @@ export default function DataGridCore(props) {
|
||||
// nextRows = [];
|
||||
// }
|
||||
const { rows: nextRows } = response.data;
|
||||
console.log('nextRows', nextRows);
|
||||
// console.log('nextRows', nextRows);
|
||||
const loadedInfo = {
|
||||
loadedRows: [...loadedRows, ...nextRows],
|
||||
loadedTime,
|
||||
@@ -142,7 +142,7 @@ export default function DataGridCore(props) {
|
||||
|
||||
const columnSizes = React.useMemo(() => countColumnSizes(), [loadedRows, containerWidth, display]);
|
||||
|
||||
console.log('containerWidth', containerWidth);
|
||||
// console.log('containerWidth', containerWidth);
|
||||
|
||||
const gridScrollAreaHeight = containerHeight - 2 * rowHeight;
|
||||
const gridScrollAreaWidth = containerWidth - columnSizes.frozenSize;
|
||||
@@ -177,13 +177,13 @@ export default function DataGridCore(props) {
|
||||
const columnSizes = new SeriesSizes();
|
||||
if (!loadedRows || !columns) return columnSizes;
|
||||
|
||||
console.log('countColumnSizes', loadedRows.length, containerWidth);
|
||||
// console.log('countColumnSizes', loadedRows.length, containerWidth);
|
||||
|
||||
columnSizes.maxSize = (containerWidth * 2) / 3;
|
||||
columnSizes.count = columns.length;
|
||||
|
||||
// columnSizes.setExtraordinaryIndexes(this.getHiddenColumnIndexes(), this.getFrozenColumnIndexes());
|
||||
console.log('display.hiddenColumnIndexes', display.hiddenColumnIndexes)
|
||||
// console.log('display.hiddenColumnIndexes', display.hiddenColumnIndexes)
|
||||
|
||||
columnSizes.setExtraordinaryIndexes(display.hiddenColumnIndexes, []);
|
||||
|
||||
@@ -240,7 +240,7 @@ export default function DataGridCore(props) {
|
||||
// console.log('containerHeight', containerHeight);
|
||||
|
||||
const visibleColumnCount = columnSizes.getVisibleScrollCount(firstVisibleColumnScrollIndex, gridScrollAreaWidth);
|
||||
console.log('visibleColumnCount', visibleColumnCount);
|
||||
// console.log('visibleColumnCount', visibleColumnCount);
|
||||
|
||||
const visibleRealColumnIndexes = [];
|
||||
const modelIndexes = {};
|
||||
@@ -274,7 +274,7 @@ export default function DataGridCore(props) {
|
||||
});
|
||||
}
|
||||
|
||||
console.log('visibleRealColumnIndexes', visibleRealColumnIndexes);
|
||||
// console.log('visibleRealColumnIndexes', visibleRealColumnIndexes);
|
||||
|
||||
return (
|
||||
<GridContainer ref={containerRef}>
|
||||
|
||||
@@ -15,28 +15,29 @@ export function getIconImage(src, props) {
|
||||
return <img width={size} height={size} src={src} style={style} className={className} title={title} />;
|
||||
}
|
||||
|
||||
export function getFontIcon(fontIconSpec, props = {}) {
|
||||
let iconClass = fontIconSpec;
|
||||
export function FontIcon({ icon, ...props }) {
|
||||
let iconClass = icon;
|
||||
if (!iconClass) return null;
|
||||
var parts = iconClass.split(' ');
|
||||
var name = parts[0];
|
||||
parts = parts.slice(1);
|
||||
let parts = iconClass.split(' ');
|
||||
const type = parts[0];
|
||||
const name = parts[1];
|
||||
parts = parts.slice(2);
|
||||
|
||||
var className = props.className || '';
|
||||
let className = props.className || '';
|
||||
|
||||
// if (_.startsWith(name, 'bs-')) className += ` glyphicon glyphicon-${name.substr(3)}`;
|
||||
if (_.startsWith(name, 'fa-')) className += ` fas fa-${name.substr(3)}`;
|
||||
if (type == 'fas' || type == 'far') className += `${type} ${name}`;
|
||||
|
||||
if (_.includes(parts, 'spin')) className += ' fa-spin';
|
||||
|
||||
var style = props.style || {};
|
||||
const style = props.style || {};
|
||||
|
||||
var last = parts[parts.length - 1];
|
||||
const last = parts[parts.length - 1];
|
||||
if (last && last != 'spin') {
|
||||
style['color'] = last;
|
||||
}
|
||||
|
||||
return <i className={className} style={style} title={props.title} />;
|
||||
return <i {...props} className={className} style={style} title={props.title} />;
|
||||
}
|
||||
|
||||
export const TableIcon = props => getIconImage('table2.svg', props);
|
||||
@@ -85,13 +86,11 @@ export const LinkedServerIcon = props => getIconImage('linkedserver.svg', props)
|
||||
|
||||
export const EmptyIcon = props => getIconImage('data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=', props);
|
||||
|
||||
export const TimesRedIcon = props => getFontIcon('fa-times red', props);
|
||||
export const TimesGreenCircleIcon = props => getFontIcon('fa-times-circle green', props);
|
||||
export const GrayFilterIcon = props => getFontIcon('fa-filter lightgray', props);
|
||||
export const ExclamationTriangleIcon = props => getFontIcon('fa-exclamation-triangle', props);
|
||||
export const HourGlassIcon = props => getFontIcon('fa-hourglass', props);
|
||||
export const InfoBlueCircleIcon = props => getFontIcon('fa-info-circle blue', props);
|
||||
export const TimesRedIcon = props => <FontIcon name='fas fa-times red' {...props} />;
|
||||
export const TimesGreenCircleIcon = props => <FontIcon icon='fas fa-times-circle green' {...props} />;
|
||||
export const GrayFilterIcon = props => <FontIcon icon='fas fa-filter lightgray' {...props} />;
|
||||
export const ExclamationTriangleIcon = props => <FontIcon icon='fas fa-exclamation-triangle' {...props} />;
|
||||
export const HourGlassIcon = props => <FontIcon icon='fas fa-hourglass' {...props} />;
|
||||
export const InfoBlueCircleIcon = props => <FontIcon icon='fas fa-info-circle blue' {...props} />;
|
||||
|
||||
export const SpinnerIcon = props => getFontIcon('fa-spinner spin', props);
|
||||
|
||||
export const FontIcon = ({ name }) => <i className={`fas ${name}`}></i>;
|
||||
export const SpinnerIcon = props => <FontIcon icon='fas fa-spinner spin' {...props} />;
|
||||
|
||||
@@ -3,17 +3,21 @@ import useFetch from '../utility/useFetch';
|
||||
import styled from 'styled-components';
|
||||
import theme from '../theme';
|
||||
import DataGrid from '../datagrid/DataGrid';
|
||||
import { TableGridDisplay } from '@dbgate/datalib';
|
||||
import { TableGridDisplay, createGridConfig, createGridCache } from '@dbgate/datalib';
|
||||
import useTableInfo from '../utility/useTableInfo';
|
||||
import useConnectionInfo from '../utility/useConnectionInfo';
|
||||
import engines from '@dbgate/engines';
|
||||
import getTableInfo from '../utility/getTableInfo';
|
||||
|
||||
export default function TableDataTab({ conid, database, schemaName, pureName }) {
|
||||
const tableInfo = useTableInfo({ conid, database, schemaName, pureName });
|
||||
const [config, setConfig] = React.useState({ hiddenColumns: [] });
|
||||
const [config, setConfig] = React.useState(createGridConfig());
|
||||
const [cache, setCache] = React.useState(createGridCache());
|
||||
const connection = useConnectionInfo(conid);
|
||||
if (!tableInfo || !connection) return null;
|
||||
const display = new TableGridDisplay(tableInfo, engines(connection), config, setConfig);
|
||||
const display = new TableGridDisplay(tableInfo, engines(connection), config, setConfig, cache, setCache, name =>
|
||||
getTableInfo({ conid, database, ...name })
|
||||
);
|
||||
return (
|
||||
<DataGrid
|
||||
// key={`${conid}, ${database}, ${schemaName}, ${pureName}`}
|
||||
|
||||
12
packages/web/src/utility/getTableInfo.js
Normal file
12
packages/web/src/utility/getTableInfo.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import axios from './axios';
|
||||
|
||||
export default async function getTableInfo({ conid, database, schemaName, pureName }) {
|
||||
const resp = await axios.request({
|
||||
method: 'get',
|
||||
url: 'tables/table-info',
|
||||
params: { conid, database, schemaName, pureName },
|
||||
});
|
||||
/** @type {import('@dbgate/types').TableInfo} */
|
||||
const res = resp.data;
|
||||
return res;
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import theme from '../theme';
|
||||
import styled from 'styled-components';
|
||||
import { FontIcon } from '../icons';
|
||||
import { useCurrentWidget, useSetCurrentWidget } from '../utility/globalState';
|
||||
|
||||
const IconWrapper = styled.div`
|
||||
@@ -55,7 +54,7 @@ export default function WidgetIconPanel() {
|
||||
isSelected={name === currentWidget}
|
||||
onClick={() => setCurrentWidget(name === currentWidget ? null : name)}
|
||||
>
|
||||
<FontIcon name={icon} />
|
||||
<i className={`fas ${icon}`}/>
|
||||
</IconWrapper>
|
||||
))}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user