Finished core functionality.
This commit is contained in:
199
imports/ui/util/GridTable.jsx
Normal file
199
imports/ui/util/GridTable.jsx
Normal file
@@ -0,0 +1,199 @@
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import React, { useState } from 'react';
|
||||
import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
||||
|
||||
//{columns.map((column) => <td>{column.value(row)}</td>)}
|
||||
const WholeRow = ({row, columns}) => {
|
||||
return <tr><td>Test</td></tr>
|
||||
}
|
||||
|
||||
const ColumnHeader = ({column}) => {
|
||||
// console.log("Rendering column header")
|
||||
return <td>
|
||||
{column.isActions ?
|
||||
"TODO: Add widgets"
|
||||
:
|
||||
column.title
|
||||
}
|
||||
</td>
|
||||
}
|
||||
|
||||
const Row = ({columns, row, getRowKey, editedRowKey, editor, setEdited}) => {
|
||||
return <tr><td>Test</td></tr>
|
||||
}
|
||||
/*
|
||||
<tr>
|
||||
{!editedRowKey || getRowKey(row) !== editedRowKey ?
|
||||
columns.map((column, i) => <Cell column={column} row={row}/>)
|
||||
:
|
||||
editor
|
||||
}
|
||||
</tr>
|
||||
*/
|
||||
// onDoubleClick={(e) => {(!editedRowKey || getRowKey(row) !== editedRowKey) && setEdited(row)}}
|
||||
const Cell = ({row, column}) => {
|
||||
return <td>test</td>
|
||||
}
|
||||
/*
|
||||
<td>
|
||||
{column.isActions ?
|
||||
"TODO"
|
||||
:
|
||||
column.value(row)
|
||||
}
|
||||
</td>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Example:
|
||||
* import {useState} from 'react';
|
||||
* const[edited, setEdited] = useState(undefined);
|
||||
* <GridTable setEdited={setEdited} edited={edited} rows={rows} columns={columnns} actions={actions}>
|
||||
* <!-- Editor JSX here. -->
|
||||
* </GridTable>
|
||||
*
|
||||
* export let rows;
|
||||
export let columns;
|
||||
export let rowKey; //Must only be null/undefined if the row is a new object (not associated with a row in the table). Should not change.
|
||||
export let edited;
|
||||
export let actions;
|
||||
export let selection;
|
||||
* @param props
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export default ({columns, rows, actions, getRowKey, edited, setEdited, children}) => {
|
||||
// Setup a width for each column.
|
||||
columns.forEach(column => {
|
||||
let min = column.minWidth ? Math.max(10, column.minWidth) : 10;
|
||||
let weight = column.weight ? Math.max(1, column.weight) : 1;
|
||||
column.width = 'minmax(' + min + 'px, ' + weight + 'fr)';
|
||||
column.isActions = false;
|
||||
});
|
||||
|
||||
// Add the actions column to the end.
|
||||
// TODO: Allow it to be positioned.
|
||||
if(actions) {
|
||||
// TODO: make this fixed width based on the possible widget widths.
|
||||
actions.width = 'minmax(10px, 1fr)';
|
||||
actions.isActions = true;
|
||||
columns[columns.length] = actions;
|
||||
}
|
||||
|
||||
// Collect the column widths for the layout.
|
||||
let gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||
|
||||
// Resize column code.
|
||||
let headerBeingResized = null;
|
||||
let horizontalScrollOffset = 0;
|
||||
const initResize = ({target}) => {
|
||||
headerBeingResized = target.parentNode;
|
||||
window.addEventListener('mousemove', onMouseMove);
|
||||
window.addEventListener('mouseup', completeResize);
|
||||
headerBeingResized.classList.add('header--being-resized');
|
||||
};
|
||||
const completeResize = () => {
|
||||
window.removeEventListener('mousemove', onMouseMove);
|
||||
window.removeEventListener('mouseup', completeResize);
|
||||
headerBeingResized.classList.remove('header--being-resized');
|
||||
headerBeingResized = null;
|
||||
};
|
||||
const onMouseMove = e => {
|
||||
try {
|
||||
// Calculate the desired width.
|
||||
horizontalScrollOffset = document.documentElement.scrollLeft;
|
||||
let parentX = Math.round(headerBeingResized.getBoundingClientRect().x);
|
||||
const width = horizontalScrollOffset + (e.clientX - parentX);
|
||||
// Update the column object with the new size value.
|
||||
const column = columns.find(({element}) => element === headerBeingResized);
|
||||
column.width = Math.max(column.minWidth, width) + "px";
|
||||
// Ensure all the column widths are converted to fixed sizes.
|
||||
columns.forEach((column, index) => {
|
||||
if((index < columns.length - 1) && (column.width.startsWith('minmax'))) {
|
||||
column.width = parseInt(column.element.clientWidth, 10) + 'px';
|
||||
}
|
||||
});
|
||||
// Render the new column sizes.
|
||||
gridTemplateColumns = columns.map(({width}) => width).join(' ');
|
||||
} catch(e) {console.log(e);}
|
||||
}
|
||||
|
||||
// Select row code.
|
||||
let selectedRowElement = null;
|
||||
const selectRow = (e, row) => {
|
||||
let element = e.target;
|
||||
|
||||
while(element && element.nodeName !== "TR") element = element.parentNode;
|
||||
|
||||
if(selectedRowElement) {
|
||||
selectedRowElement.classList.remove('selected');
|
||||
}
|
||||
|
||||
selectedRowElement = element;
|
||||
element.classList.add('selected');
|
||||
dispatch('selection', selectedRowElement.dataset.key);
|
||||
}
|
||||
|
||||
// Edit row code.
|
||||
let editorContainer;
|
||||
let editorTable;
|
||||
// Listen for changes to the edited variable. Move the editor row and display it when there is a value.
|
||||
// Relies on the table row being hidden when the edited value matches the row's value.
|
||||
useTracker(() => {
|
||||
if(editorContainer) {
|
||||
if(edited) {
|
||||
let id = rowKey($edited);
|
||||
let hiddenRow = editorTable.querySelector('tbody tr[data-key="' + id + '"]');
|
||||
|
||||
if(!hiddenRow) {
|
||||
let body = editorTable.querySelector('tbody');
|
||||
body.firstChild ? body.insertBefore(editorContainer, body.firstChild) : body.appendChild(editorContainer);
|
||||
editorContainer.classList.remove('hidden');
|
||||
}
|
||||
else {
|
||||
//let editor = hiddenRow.querySelector('.editor');
|
||||
let body = editorTable.querySelector('tbody');
|
||||
let next = hiddenRow.nextSibling;
|
||||
//editor.appendChild(editorContainer);
|
||||
next ? body.insertBefore(editorContainer, next) : body.appendChild(editorContainer);
|
||||
editorContainer.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Edited cleared");
|
||||
editorContainer.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let editedRowKey = edited ? getRowKey(edited) : undefined;
|
||||
console.log("Rendering grid table")
|
||||
|
||||
// let contents = "";
|
||||
//
|
||||
// for(let row of rows) {
|
||||
// contents += <WholeRow row={row} columns={columns}/>
|
||||
// // contents += <tr>
|
||||
// // for(let column of columns) {
|
||||
// // contents += <CellValue row={row} column={column}></CellValue>
|
||||
// // }
|
||||
// // contents += </tr>
|
||||
// }
|
||||
|
||||
return <div className={'grid-table-container'}>
|
||||
<table style={{gridTemplateColumns}}>
|
||||
<thead>
|
||||
<tr>
|
||||
{columns.map((column, i) => <ColumnHeader key={column.key} column={column}/>)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/*{rows.map((row, i) => <Row key={getRowKey(row)} row={row} columns={columns} getRowKey={getRowKey} editedRowKey={editedRowKey} editor={children} setEdited={setEdited}/>)}*/}
|
||||
{rows.map((row)=><WholeRow row={row} columns={columns}/>)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
||||
56
imports/ui/util/GridTable2.jsx
Normal file
56
imports/ui/util/GridTable2.jsx
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import React, { useState } from 'react';
|
||||
import { useTracker } from 'meteor/react-meteor-data';
|
||||
import _ from 'lodash';
|
||||
|
||||
export default class GridTable2 extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
rows: [{_id: 1234, name: "Fred"}, {_id: 1235, }],
|
||||
columns: [{id: "first", value: row => {return row._id}}, {id: "second", value: row => {return row.name}}]
|
||||
}
|
||||
}
|
||||
render() {
|
||||
console.log("Rendering GridTable2")
|
||||
return <table><tbody>
|
||||
<tr><td>Test</td><td>Test2</td></tr>
|
||||
{/*<tr>*/}
|
||||
{/*<GridTableCell row={this.state.row} column={this.state.column}></GridTableCell>*/}
|
||||
{/*</tr>*/}
|
||||
{/*{this.state.rows.map((row) => <GridTableRow key={row._id} row={row} columns={this.state.columns}></GridTableRow>)}*/}
|
||||
</tbody></table>
|
||||
}
|
||||
}
|
||||
|
||||
class GridTableRow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
columns: props.columns,
|
||||
row: props.row,
|
||||
}
|
||||
}
|
||||
render() {
|
||||
console.log("Rendering GridTableRow")
|
||||
return <tr>
|
||||
{this.state.columns.map((column)=><GridTableCell key={this.state.row._id + "-" + column.id} row={this.state.row} column={column}/>)}
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
|
||||
class GridTableCell extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
console.log(props);
|
||||
this.state = {
|
||||
row: props.row,
|
||||
column: props.column,
|
||||
value: props.column.value(props.row)
|
||||
}
|
||||
}
|
||||
render() {
|
||||
console.log("Rendering GridTableCell")
|
||||
return <td>{this.state.value}</td>
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,15 @@ import Box from "@mui/material/Box";
|
||||
// let options = {
|
||||
// key: (row) => row._id,
|
||||
// editor: (row) => {return (<MyRowEditor value={row}/>)}
|
||||
// add: true,
|
||||
// maxHeight: "40rem",
|
||||
// remove: (row) => { /* show dialog and/or perform remove */ }
|
||||
// }
|
||||
|
||||
const cssTopControls = {
|
||||
display: 'flex',
|
||||
flexDirection: 'row-reverse',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -79,6 +83,10 @@ export default ({columns, rows, options}) => {
|
||||
setEdited({})
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
if(!edited && e.key === 'Delete' && selected && options.delete && _.isFunction(options.delete)) {
|
||||
options.delete(selected)
|
||||
}
|
||||
}
|
||||
|
||||
const sort = (e, column) => {
|
||||
@@ -119,7 +127,10 @@ export default ({columns, rows, options}) => {
|
||||
// console.log(rows)
|
||||
return (
|
||||
<div className='simpleTableContainer'>
|
||||
{options.add && <div style={cssTopControls}><Button variant="text" className="button" onClick={addRow}>Add</Button></div>}
|
||||
<div style={cssTopControls}>
|
||||
{options.add && <Button variant="text" className="button" onClick={addRow}>Add</Button>}
|
||||
{options.remove && _.isFunction(options.remove) && <Button disabled={!selected} variant='text' className='button' onClick={() => {selected && options.remove(selected)}}>Remove</Button>}
|
||||
</div>
|
||||
<TableContainer className="simpleTable" component={Paper} style={containerStyle}>
|
||||
<Table size="small" aria-label="Table" tabIndex="0" onKeyDown={keyHandler}>
|
||||
<TableHead className="sticky">
|
||||
|
||||
Reference in New Issue
Block a user