Skip to content

feat(Database Browser): Change columns order and visibility #1235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';
import { useDrag, useDrop } from 'react-dnd';

import Icon from 'components/Icon/Icon.react';
import styles from 'components/ColumnsConfiguration/ColumnConfigurationItem.scss';

const DND_TYPE = 'ColumnConfigurationItem';

export default ({ name, handleColumnDragDrop, index, onChangeVisible, visible }) => {
const [ { isDragging}, drag ] = useDrag({
item: { type: DND_TYPE, index },
collect: monitor => ({ isDragging: !!monitor.isDragging() })
});

const [ { canDrop, isOver }, drop ] = useDrop({
accept: DND_TYPE,
drop: item => handleColumnDragDrop(item.index, index),
canDrop: item => item.index !== index,
collect: monitor => ({
isOver: !!monitor.isOver(),
canDrop: !!monitor.canDrop()
})
});

return drag(drop(
<section
className={styles.columnConfigItem}
style={{
opacity: isDragging ? 0.5 : 1,
cursor: isDragging ? 'grabbing' : null,
backgroundColor: isOver && canDrop ? '#208aec' : null
}}
onClick={() => onChangeVisible(!visible)}>
<div className={[styles.icon, styles.visibilityIcon].join(' ')}>
<Icon name={visible ? 'visibility' : 'visibility_off'} width={18} height={18} fill={visible ? 'white' : 'rgba(0,0,0,0.4)'} />
</div>
<div className={styles.columnConfigItemName} title={name}>{name}</div>
<div className={styles.icon}>
<Icon name='drag-indicator' width={14} height={14} fill="white" />
</div>
</section>
));
};
25 changes: 25 additions & 0 deletions src/components/ColumnsConfiguration/ColumnConfigurationItem.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.columnConfigItem {
padding: 8px 10px;
display: flex;
justify-content: space-between;
border-radius: 5px;
cursor: grab;
}

.icon {
display: flex;
align-items: center;
height: 24px;
}

.visibilityIcon {
cursor: pointer;
width: 30px;
}

.columnConfigItemName {
width: 110px;
text-overflow: ellipsis;
overflow: hidden;
line-height: 24px;
}
109 changes: 109 additions & 0 deletions src/components/ColumnsConfiguration/ColumnsConfiguration.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import React from 'react';
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import ReactDOM from 'react-dom';

import Button from 'components/Button/Button.react';
import ColumnConfigurationItem from 'components/ColumnsConfiguration/ColumnConfigurationItem.react';
import styles from 'components/ColumnsConfiguration/ColumnsConfiguration.scss';
import Icon from 'components/Icon/Icon.react';
import Popover from 'components/Popover/Popover.react';
import Position from 'lib/Position';

export default class ColumnsConfiguration extends React.Component {
constructor() {
super();

this.state = {
open: false
};
}

componentDidMount() {
this.node = ReactDOM.findDOMNode(this);
}

componentWillReceiveProps(props) {
if (props.schema !== this.props.schema) {
this.setState({
open: false
});
}
}

toggle() {
this.setState({
open: !this.state.open
})
}

showAll() {
this.props.handleColumnsOrder(this.props.order.map(order => ({ ...order, visible: true })));
}

hideAll() {
this.props.handleColumnsOrder(this.props.order.map(order => ({ ...order, visible: false })));
}

render() {
const { handleColumnDragDrop, handleColumnsOrder, order } = this.props;
const [ title, entry ] = [styles.title, styles.entry ].map(className => (
<div className={className} onClick={this.toggle.bind(this)}>
<Icon name='manage-columns' width={14} height={14} />
<span>Manage Columns</span>
</div>
));

let popover = null;
if (this.state.open) {
popover = (
<Popover fixed={true} position={Position.inDocument(this.node)} onExternalClick={this.toggle.bind(this)}>
<div className={styles.popover}>
{title}
<div className={styles.body}>
<div className={styles.columnConfigContainer}>
<DndProvider backend={HTML5Backend}>
{order.map(({ name, visible, ...rest }, index) => {
return <ColumnConfigurationItem
key={index}
index={index}
name={name}
visible={visible}
onChangeVisible={visible => {
const updatedOrder = [...order];
updatedOrder[index] = {
...rest,
name,
visible
};
handleColumnsOrder(updatedOrder);
}}
handleColumnDragDrop={handleColumnDragDrop} />
})}
</DndProvider>
</div>
<div className={styles.footer}>
<Button
color='white'
value='Hide All'
width='85px'
onClick={this.hideAll.bind(this)} />
<Button
color='white'
value='Show all'
width='85px'
onClick={this.showAll.bind(this)} />
</div>
</div>
</div>
</Popover>
);
}
return (
<>
{entry}
{popover}
</>
);
}
}
75 changes: 75 additions & 0 deletions src/components/ColumnsConfiguration/ColumnsConfiguration.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
@import 'stylesheets/globals.scss';

.entry {
display: inline-block;
height: 14px;
padding: 0 8px;

svg {
fill: #66637A;
}

&:hover svg {
fill: white;
}
}

.title {
margin-top: -4px;
background: #797691;
padding: 4px 8px;
border-radius: 5px 5px 0 0;

svg {
fill: white;
}
}

.entry, .title {
@include NotoSansFont;
font-size: 14px;
color: #ffffff;
cursor: pointer;

svg {
vertical-align: middle;
margin-right: 4px;
}

span {
vertical-align: middle;
line-height: 14px;
}
}

.body {
color: white;
position: absolute;
top: 22px;
right: 0;
border-radius: 5px 0 5px 5px;
background: #797691;
width: 220px;
font-size: 14px;

.columnConfigContainer {
max-height: calc(100vh - 180px);
overflow: auto;
margin: 10px;
}
}

.footer {
background: rgba(0,0,0,0.2);
padding: 15px 20px;
display: flex;
justify-content: space-between;

> a {
margin-right: 10px;

&:last-child {
margin-right: 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ export default class DataBrowserHeaderBar extends React.Component {
</div>
];

headers.forEach(({ width, name, type, targetClass, order }, i) => {
headers.forEach(({ width, name, type, targetClass, order, visible }, i) => {
if (!visible) return;
let wrapStyle = { width };
if (i % 2) {
wrapStyle.background = '#726F85';
Expand Down
2 changes: 1 addition & 1 deletion src/components/Popover/Popover.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default class Popover extends React.Component {
}

render() {
return <div></div>;
return null;
}
}

Expand Down
16 changes: 9 additions & 7 deletions src/dashboard/Data/Browser/BrowserTable.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ export default class BrowserTable extends React.Component {
checked={this.props.selection['*'] || this.props.selection[obj.id]}
onChange={(e) => this.props.selectRow(obj.id, e.target.checked)} />
</span>
{this.props.order.map(({ name, width }, j) => {
{this.props.order.map(({ name, width, visible }, j) => {
if (!visible) return null;
let type = this.props.columns[name].type;
let attr = obj;
if (!this.props.isUnique) {
Expand Down Expand Up @@ -147,22 +148,23 @@ export default class BrowserTable extends React.Component {
}
}

let headers = this.props.order.map(({ name, width }) => (
let headers = this.props.order.map(({ name, width, visible }) => (
{
width: width,
name: name,
type: this.props.columns[name].type,
targetClass: this.props.columns[name].targetClass,
order: ordering.col === name ? ordering.direction : null
order: ordering.col === name ? ordering.direction : null,
visible
}
));
let editor = null;
let table = <div ref='table' />;
if (this.props.data) {
let rowWidth = 210;
for (let i = 0; i < this.props.order.length; i++) {
rowWidth += this.props.order[i].width;
}
const rowWidth = this.props.order.reduce(
(rowWidth, { visible, width }) => visible ? rowWidth + width : rowWidth,
210
);
let newRow = null;
if (this.props.newObject && this.state.offset <= 0) {
newRow = (
Expand Down
10 changes: 10 additions & 0 deletions src/dashboard/Data/Browser/BrowserToolbar.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
import BrowserFilter from 'components/BrowserFilter/BrowserFilter.react';
import BrowserMenu from 'components/BrowserMenu/BrowserMenu.react';
import ColumnsConfiguration
from 'components/ColumnsConfiguration/ColumnsConfiguration.react';
import Icon from 'components/Icon/Icon.react';
import MenuItem from 'components/BrowserMenu/MenuItem.react';
import prettyNumber from 'lib/prettyNumber';
Expand Down Expand Up @@ -42,6 +44,9 @@ let BrowserToolbar = ({
onRefresh,
hidePerms,
isUnique,
handleColumnDragDrop,
handleColumnsOrder,
order,

enableDeleteAllRows,
enableExportClass,
Expand Down Expand Up @@ -150,6 +155,11 @@ let BrowserToolbar = ({
<span>Add Row</span>
</a>
<div className={styles.toolbarSeparator} />
<ColumnsConfiguration
handleColumnsOrder={handleColumnsOrder}
handleColumnDragDrop={handleColumnDragDrop}
order={order} />
<div className={styles.toolbarSeparator} />
<a className={styles.toolbarButton} onClick={onRefresh}>
<Icon name='refresh-solid' width={14} height={14} />
<span>Refresh</span>
Expand Down
9 changes: 9 additions & 0 deletions src/dashboard/Data/Browser/DataBrowser.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ export default class DataBrowser extends React.Component {
}
}

handleColumnsOrder(order) {
this.setState({ order }, () => {
this.updatePreferences(order);
});
}

render() {
let { className, ...other } = this.props;
const { preventSchemaEdits } = this.context.currentApp;
Expand All @@ -213,6 +219,9 @@ export default class DataBrowser extends React.Component {
enableSecurityDialog={this.context.currentApp.serverInfo.features.schemas.editClassLevelPermissions && !preventSchemaEdits}
enableColumnManipulation={!preventSchemaEdits}
enableClassManipulation={!preventSchemaEdits}
handleColumnDragDrop={this.handleHeaderDragDrop.bind(this)}
handleColumnsOrder={this.handleColumnsOrder.bind(this)}
order={this.state.order}
{...other}/>
</div>
);
Expand Down
3 changes: 3 additions & 0 deletions src/icons/drag-indicator.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/manage-columns.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/visibility.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/icons/visibility_off.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading