Skip to content

Commit

Permalink
Added column resizers to tables. (#3786)
Browse files Browse the repository at this point in the history
  • Loading branch information
scudette authored Oct 1, 2024
1 parent 0a1dec8 commit 4f1da6e
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 54 deletions.
80 changes: 80 additions & 0 deletions gui/velociraptor/src/components/core/column-resizer.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import _ from 'lodash';
import React, { createRef, Component } from 'react';
import PropTypes from 'prop-types';


export default class ColumnResizer extends Component {
ref = createRef();

static propTypes = {
// Called to set the width of the previous column.
setWidth: PropTypes.func,
width: PropTypes.number,
}

componentDidMount() {
document.addEventListener('mousemove', this.onMouseMove);
document.addEventListener('mouseup', this.endDrag);
this.dragging = false;

// Track the position of the mouse pointer always. It will
// only be used when we start to drag.
this.mouseX = 0;
}

componentWillUnmount() {
document.removeEventListener('mousemove', this.onMouseMove);
document.removeEventListener('mouseup', this.endDrag);
}

// Start dragging locks in the current screen position and the
// width of the previous td.
startDrag = e=>{
this.startWidth = this.props.width;
if (_.isUndefined(this.startWidth)) {
let prev = this.ref.current.previousElementSibling;
if(prev) {
this.startWidth = prev.clientWidth;
}
}

this.dragging = true;
this.startingX = this.mouseX;

// Disable selection
e.preventDefault();
return false;
}

endDrag = ()=>{
this.dragging = false;
}

onMouseMove = e=>{
this.mouseX = e.screenX;

if (!this.dragging) {
return;
}

// Resize the previous element according to the startingX and
// current position.
let diff = this.startingX - this.mouseX;
let newPrevWidth = this.startWidth - diff;
if (newPrevWidth < 0) {
newPrevWidth = 0;
}

this.props.setWidth(newPrevWidth);
}

render() {
return (
<td
ref={this.ref}
className="column-resizer"
onMouseDown={this.startDrag}
/>
);
}
}
18 changes: 18 additions & 0 deletions gui/velociraptor/src/components/core/paged-table.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,21 @@ table.paged-table-header:hover .hidden-edit {
width: 0px;
visibility: hidden;
}

td.column-resizer {
width: 0px;
padding: 5px;
cursor: col-resize;
min-width: 5px;
max-width: 5px;
background-color: var(--color-table-heading-background);
}

.paged-table thead td.column-resizer {
background-color: var(--color-table-heading-background);
}

.paged-table thead td.column-resizer:hover,
.paged-table tbody td.column-resizer:hover {
background-color: var(--color-table-row-selected);
}
155 changes: 101 additions & 54 deletions gui/velociraptor/src/components/core/paged-table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ToolTip from '../widgets/tooltip.jsx';
import Table from 'react-bootstrap/Table';
import Dropdown from 'react-bootstrap/Dropdown';
import StackDialog from './stack.jsx';
import ColumnResizer from "./column-resizer.jsx";

import T from '../i8n/i8n.jsx';
import UserConfig from '../core/user.jsx';
Expand Down Expand Up @@ -543,6 +544,9 @@ class VeloPagedTable extends Component {

// Keep state for individual stacking transforms
stack_transforms: {},

// Map between columns and their widths.
column_widths: {},
}

componentDidMount = () => {
Expand Down Expand Up @@ -860,65 +864,97 @@ class VeloPagedTable extends Component {
column_name = T(column_name);
}

let styles = {};
let col_width = this.state.column_widths[column_name];
if (col_width) {
styles = {
minWidth: col_width,
maxWidth: col_width,
width: col_width,
};
}

// Do not allow this column to be sorted/filtered
if (this.props.prevent_transformations &&
this.props.prevent_transformations[column]) {
return <th key={idx}>
<table className="paged-table-header">
<tbody>
<tr>
<td>{ column_name }</td>
<td className="sort-element">
<ButtonGroup>
<Button
className="hidden-sorter"
size="sm"
></Button>
</ButtonGroup>

</td>
</tr>
</tbody>
</table>
</th>;
return <React.Fragment key={idx}>
<th style={styles}>
<table className="paged-table-header">
<tbody>
<tr>
<td>{ column_name }</td>
<td className="sort-element">
<ButtonGroup>
<Button
className="hidden-sorter"
size="sm"
></Button>
</ButtonGroup>

</td>
</tr>
</tbody>
</table>
</th>
<ColumnResizer
width={this.state.column_widths[column]}
setWidth={x=>{
let column_widths = Object.assign(
{}, this.state.column_widths);
column_widths[column] = x;
this.setState({column_widths: column_widths});
}}
/>
</React.Fragment>;
}

return (
<th key={idx}>
<table className="paged-table-header">
<tbody>
<tr>
<td>{ column_name }</td>
<td className="sort-element">
<ButtonGroup>
{ this.isColumnStacked(column) &&
<ToolTip tooltip={T("Stack")} key={idx}>
<Button variant="default"
target="_blank" rel="noopener noreferrer"
onClick={e=>{
this.setState({showStackDialog: column});
e.preventDefault();
return false;
}}>
<FontAwesomeIcon icon="layer-group"/>
</Button>
</ToolTip>
}
<ColumnSort column={column}
transform={this.state.transform}
setTransform={this.setTransform}
/>

<ColumnFilter column={column}
<React.Fragment key={idx}>
<th style={styles}>
<table className="paged-table-header">
<tbody>
<tr>
<td>{ column_name }</td>
<td className="sort-element">
<ButtonGroup>
{ this.isColumnStacked(column) &&
<ToolTip tooltip={T("Stack")} key={idx}>
<Button variant="default"
target="_blank" rel="noopener noreferrer"
onClick={e=>{
this.setState({showStackDialog: column});
e.preventDefault();
return false;
}}>
<FontAwesomeIcon icon="layer-group"/>
</Button>
</ToolTip>
}
<ColumnSort column={column}
transform={this.state.transform}
setTransform={this.setTransform}
/>
</ButtonGroup>
</td>
</tr>
</tbody>
</table>
</th>
/>

<ColumnFilter column={column}
transform={this.state.transform}
setTransform={this.setTransform}
/>
</ButtonGroup>
</td>
</tr>
</tbody>
</table>
</th>
<ColumnResizer
width={this.state.column_widths[column]}
setWidth={x=>{
let column_widths = Object.assign(
{}, this.state.column_widths);
column_widths[column] = x;
this.setState({column_widths: column_widths});
}}
/>
</React.Fragment>
);
}

Expand All @@ -929,9 +965,20 @@ class VeloPagedTable extends Component {
let cell = row[column];
let renderer = this.getColumnRenderer(column);

return <td key={column}>
{ renderer(cell, row, this.props.env)}
</td>;
return <React.Fragment key={column}>
<td>
{ renderer(cell, row, this.props.env)}
</td>
<ColumnResizer
width={this.state.column_widths[column]}
setWidth={x=>{
let column_widths = Object.assign(
{}, this.state.column_widths);
column_widths[column] = x;
this.setState({column_widths: column_widths});
}}
/>
</React.Fragment>;
};

selectRow = (row, idx)=>{
Expand Down

0 comments on commit 4f1da6e

Please sign in to comment.