Files
Tempest/imports/ui/util/GridTable.jsx

199 lines
6.0 KiB
JavaScript

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>
}