aboutsummaryrefslogtreecommitdiff
path: root/diplomacy/web/src/gui/core/table.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'diplomacy/web/src/gui/core/table.jsx')
-rw-r--r--diplomacy/web/src/gui/core/table.jsx112
1 files changed, 112 insertions, 0 deletions
diff --git a/diplomacy/web/src/gui/core/table.jsx b/diplomacy/web/src/gui/core/table.jsx
new file mode 100644
index 0000000..cb729e7
--- /dev/null
+++ b/diplomacy/web/src/gui/core/table.jsx
@@ -0,0 +1,112 @@
+// ==============================================================================
+// Copyright (C) 2019 - Philip Paquette, Steven Bocco
+//
+// This program is free software: you can redistribute it and/or modify it under
+// the terms of the GNU Affero General Public License as published by the Free
+// Software Foundation, either version 3 of the License, or (at your option) any
+// later version.
+//
+// This program is distributed in the hope that it will be useful, but WITHOUT
+// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
+// details.
+//
+// You should have received a copy of the GNU Affero General Public License along
+// with this program. If not, see <https://www.gnu.org/licenses/>.
+// ==============================================================================
+//// Tables.
+
+import React from "react";
+import PropTypes from 'prop-types';
+
+class DefaultWrapper {
+ constructor(data) {
+ this.data = data;
+ this.get = this.get.bind(this);
+ }
+
+ get(fieldName) {
+ return this.data[fieldName];
+ }
+}
+
+function defaultWrapper(data) {
+ return new DefaultWrapper(data);
+}
+
+export class Table extends React.Component {
+ // className
+ // caption
+ // columns : {name: [title, order]}
+ // data: [objects with expected column names]
+ // wrapper: (optional) function to use to wrap one data entry into an object before accessing fields.
+ // Must return an instance with a method get(name).
+ // If provided: wrapper(data_entry).get(field_name)
+ // else: data_entry[field_name]
+
+ constructor(props) {
+ super(props);
+ if (!this.props.wrapper)
+ this.props.wrapper = defaultWrapper;
+ }
+
+ static getHeader(columns) {
+ const header = [];
+ for (let entry of Object.entries(columns)) {
+ const name = entry[0];
+ const title = entry[1][0];
+ const order = entry[1][1];
+ header.push([order, name, title]);
+ }
+ header.sort((a, b) => {
+ let t = a[0] - b[0];
+ if (t === 0)
+ t = a[1].localeCompare(b[1]);
+ if (t === 0)
+ t = a[2].localeCompare(b[2]);
+ return t;
+ });
+ return header;
+ }
+
+ static getHeaderLine(header) {
+ return (
+ <thead className={'thead-light'}>
+ <tr>{header.map((column, colIndex) => <th key={colIndex}>{column[2]}</th>)}</tr>
+ </thead>
+ );
+ }
+
+ static getBodyRow(header, row, rowIndex, wrapper) {
+ const wrapped = wrapper(row);
+ return (<tr key={rowIndex}>
+ {header.map((headerColumn, colIndex) => <td className={'align-middle'}
+ key={colIndex}>{wrapped.get(headerColumn[1])}</td>)}
+ </tr>);
+ }
+
+ static getBodyLines(header, data, wrapper) {
+ return (<tbody>{data.map((row, rowIndex) => Table.getBodyRow(header, row, rowIndex, wrapper))}</tbody>);
+ }
+
+ render() {
+ const header = Table.getHeader(this.props.columns);
+ return (
+ <div className={'table-responsive'}>
+ <table className={this.props.className}>
+ <caption>{this.props.caption} ({this.props.data.length})</caption>
+ {Table.getHeaderLine(header)}
+ {Table.getBodyLines(header, this.props.data, this.props.wrapper)}
+ </table>
+ </div>
+ );
+ }
+}
+
+Table.propTypes = {
+ wrapper: PropTypes.func,
+ columns: PropTypes.object,
+ className: PropTypes.string,
+ caption: PropTypes.string,
+ data: PropTypes.array
+};