@luminati-io/luminati-proxy
Version:
A configurable local proxy for luminati.io
323 lines (309 loc) • 12.2 kB
JavaScript
// LICENSE_CODE ZON ISC
; /*jslint react:true, es6:true*/
import React from 'react';
import Pure_component from '/www/util/pub/pure_component.js';
import classnames from 'classnames';
import _ from 'lodash';
import Tooltip from './common/tooltip.js';
import {Checkbox} from './common.js';
import './css/chrome_widgets.less';
import {T} from './common/i18n.js';
export const Toolbar_button = ({id, tooltip, active, href, placement,
...props})=>
<Tooltip title={tooltip} placement={placement||'bottom'}>
<a className={classnames('toolbar_item toolbar_button', id, {active})}
onClick={props.on_click||(()=>null)} href={href}>
<span className={classnames(id, 't_button', 'chrome_icon')}/>
{props.children}
</a>
</Tooltip>;
export const Devider = ()=><div className="devider"/>;
export const with_resizable_cols = (cols, Table)=>{
class Resizable extends Pure_component {
state = {};
cols = _.cloneDeep(cols);
min_width = 22;
moving_col = null;
style = {position: 'relative', display: 'flex', flex: 'auto',
width: '100%'};
componentDidMount(){
this.resize_columns();
window.document.addEventListener('mousemove', this.on_mouse_move);
window.document.addEventListener('mouseup', this.on_mouse_up);
}
willUnmount(){
window.document.removeEventListener('mousemove',
this.on_mouse_move);
window.document.removeEventListener('mouseup', this.on_mouse_up);
}
set_ref = ref=>{ this.ref = ref; };
show_column = idx=>{
this.cols[idx].hidden = false;
this.resize_columns();
};
hide_column = idx=>{
this.cols[idx].hidden = true;
this.resize_columns();
};
resize_columns = ()=>{
const total_width = this.ref.offsetWidth;
const resizable_cols = this.cols.filter(c=>!c.hidden&&!c.fixed);
const total_fixed = this.cols.reduce((acc, c)=>
acc+(!c.hidden&&c.fixed||0), 0);
const width = (total_width-total_fixed)/resizable_cols.length;
const next_cols = this.cols.reduce((acc, c, idx)=>{
const w = !c.fixed&&width||!c.hidden&&c.fixed||0;
return {
cols: [...acc.cols, {
...c,
width: w,
offset: acc.offset,
border: acc.border,
}],
offset: acc.offset+w,
border: !!w,
};
}, {cols: [], offset: 0, border: true});
this.setState({cols: next_cols.cols});
};
start_moving = (e, idx)=>{
if (e.nativeEvent.which!=1)
return;
this.start_offset = e.pageX;
this.start_width = this.state.cols[idx].width;
this.start_width_last = this.state.cols.slice(-1)[0].width;
this.moving_col = idx;
this.setState({moving: true});
};
on_mouse_move = e=>{
if (this.moving_col===null)
return;
this.setState(prev=>{
let offset = e.pageX-this.start_offset;
if (this.start_width_last-offset<this.min_width)
offset = this.start_width_last-this.min_width;
if (this.start_width+offset<this.min_width)
offset = this.min_width-this.start_width;
let total_width = 0;
const next_cols = prev.cols.map((c, idx)=>{
if (idx<this.moving_col)
{
total_width = total_width+c.width;
return c;
}
else if (idx==this.moving_col)
{
const width = this.start_width+offset;
total_width = total_width+width;
return {...c, width, offset: total_width-width};
}
else if (idx==this.state.cols.length-1)
{
const width = this.start_width_last-offset;
return {...c, width, offset: total_width};
}
total_width = total_width+c.width;
return {...c, offset: total_width-c.width};
});
return {cols: next_cols};
});
};
on_mouse_up = ()=>{
this.moving_col = null;
this.setState({moving: false});
};
render(){
const style = Object.assign({}, this.style, this.props.style||{});
return <div style={style} ref={this.set_ref}
className={classnames({moving: this.state.moving})}>
<Table {...this.props} cols={this.state.cols}
resize_columns={this.resize_columns}
show_column={this.show_column}
hide_column={this.hide_column}/>
<Grid_resizers show={!this.props.cur_preview}
start_moving={this.start_moving}
cols={this.state.cols}/>
</div>;
}
}
return Resizable;
};
const Grid_resizers = ({cols, start_moving, show})=>{
if (!show||!cols)
return null;
return <div>
{cols.slice(0, -1).map((c, idx)=>
!c.fixed &&
<div key={c.title||idx} style={{left: c.width+c.offset-2}}
onMouseDown={e=>start_moving(e, idx)}
className="data_grid_resizer"/>
)}
</div>;
};
export const Sort_icon = ({show, dir})=>{
if (!show)
return null;
const classes = classnames('small_icon_mask', {sort_asc: dir==-1,
sort_desc: dir==1});
return <div className="sort_icon"><span className={classes}/></div>;
};
import {AutoSizer, Table, Column} from 'react-virtualized';
export class Infinite_chrome_table extends Pure_component {
state = {};
cell_renderer = function Cell_renderer(props){
if (props.rowData=='filler')
return <div className="chrome_td"></div>;
return <span key={props.rowData}>{props.rowData}</span>;
};
select_renderer = function Select_renderer(props){
if (props.rowData=='filler')
return <div className="chrome_td"></div>;
return <Checkbox
checked={this.props.selected_list.includes(props.rowData)}
on_change={()=>null}/>;
};
toggle_all = ()=>{
if (this.props.selected_all)
this.props.unselect_all();
else
this.props.select_all();
};
render(){
const rows = this.props.rows||[];
const {class_name, toolbar} = this.props;
return <div className="chrome">
<div className="main_panel vbox">
<Toolbar_container>
<Toolbar_row>
<div className="title_wrapper">{this.props.title}</div>
</Toolbar_row>
{toolbar && <Toolbar_row>{toolbar}</Toolbar_row>}
</Toolbar_container>
<React.Fragment>
<div className={classnames('chrome_table vbox', class_name)}>
<div className="tables_container header_container hack">
<div className="chrome_table">
<AutoSizer>
{({height, width})=>
<Table width={width}
height={height}
onRowClick={this.props.toggle}
onHeaderClick={({dataKey})=>dataKey=='select' &&
this.toggle_all()}
gridClassName="chrome_grid"
headerHeight={27}
headerClassName="chrome_th"
rowClassName="chrome_tr"
rowHeight={22}
rowCount={rows.length+1}
rowGetter={({index})=>rows[index]||'filler'}>
<Column key="select"
cellRenderer={this.select_renderer.bind(this)}
label={
<Checkbox checked={this.props.selected_all}
on_change={()=>null}/>
}
dataKey="select"
className="chrome_td"
flexGrow={0}
flexShrink={1}
width={20}/>
{this.props.cols.map(col=>
<Column key={col.id}
cellRenderer={this.cell_renderer}
label={col.title}
className="chrome_td"
dataKey={col.id}
flexGrow={1}
width={100}/>
)}
</Table>
}
</AutoSizer>
</div>
</div>
</div>
</React.Fragment>
</div>
</div>;
}
}
export class Chrome_table extends Pure_component {
render(){
const {selectable, cols, title, children, class_name,
selected_all, toggle_all} = this.props;
const classes = classnames('chrome', 'chrome_table', class_name);
return <div className={classes}>
<div className="main_panel vbox">
<Toolbar_container>
<Toolbar_row>
<div className="title_wrapper">{title}</div>
</Toolbar_row>
</Toolbar_container>
<div className="tables_container vbox">
<Header_container selectable={selectable}
selected_all={selected_all} toggle_all={toggle_all}
cols={cols}/>
<Data_container selectable={selectable} cols={cols}>
{children}
</Data_container>
</div>
</div>
</div>;
}
}
const Data_container = ({cols=[], children, selectable})=>{
return <div className="data_container">
<table>
<colgroup>
{selectable &&
<col key="select_all" style={{width: 20}}/>
}
{cols.map((c, idx)=>
<col key={idx} style={{width: c.width}}/>
)}
</colgroup>
<tbody>
{children}
<tr className="filler">
{selectable && <td key="select_filler"/>}
{cols.map(c=><td key={c.id}></td>)}
</tr>
</tbody>
</table>
</div>;
};
const Header_container = ({cols, selectable, selected_all, toggle_all})=>
<div className="header_container">
<table>
<colgroup>
{selectable && <col key="select_all" style={{width: 20}}/>}
{(cols||[]).map((c, idx)=><col key={idx} style={{width: c.width}}/>)}
</colgroup>
<tbody>
<tr>
{selectable &&
<th key="select_all" onClick={toggle_all}>
<Checkbox checked={selected_all} readonly/>
</th>
}
{(cols||[]).map((c, idx)=><th key={idx}><T>{c.title}</T></th>)}
</tr>
</tbody>
</table>
</div>;
export const Toolbar_container = ({children})=>
<div className="toolbar_container">
{children}
</div>;
export const Toolbar_row = ({children})=>
<div className="toolbar">
{children}
</div>;
export const Search_box = ({val, on_change})=>
<div className="search_box">
<input value={val}
onChange={on_change}
type="text"
placeholder="Filter"/>
</div>;