aboutsummaryrefslogtreecommitdiff
path: root/diplomacy/web/src/gui/core
diff options
context:
space:
mode:
Diffstat (limited to 'diplomacy/web/src/gui/core')
-rw-r--r--diplomacy/web/src/gui/core/action.jsx52
-rw-r--r--diplomacy/web/src/gui/core/button.jsx52
-rw-r--r--diplomacy/web/src/gui/core/delete_button.jsx43
-rw-r--r--diplomacy/web/src/gui/core/fancybox.jsx59
-rw-r--r--diplomacy/web/src/gui/core/forms.jsx116
-rw-r--r--diplomacy/web/src/gui/core/layouts.jsx55
-rw-r--r--diplomacy/web/src/gui/core/page.jsx375
-rw-r--r--diplomacy/web/src/gui/core/tab.jsx29
-rw-r--r--diplomacy/web/src/gui/core/table.jsx112
-rw-r--r--diplomacy/web/src/gui/core/tabs.jsx69
10 files changed, 0 insertions, 962 deletions
diff --git a/diplomacy/web/src/gui/core/action.jsx b/diplomacy/web/src/gui/core/action.jsx
deleted file mode 100644
index 73fe8cb..0000000
--- a/diplomacy/web/src/gui/core/action.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-import React from "react";
-import PropTypes from 'prop-types';
-
-
-export class Action extends React.Component {
- // title
- // isActive
- // onClick
- // See Button parameters.
-
- render() {
- return (
- <div className="action nav-item" onClick={this.props.onClick}>
- <div
- className={'nav-link' + (this.props.isActive ? ' active' : '') + (this.props.highlight !== null ? ' updated' : '')}>
- {this.props.title}
- {this.props.highlight !== null
- && this.props.highlight !== undefined
- && <span className={'update'}>{this.props.highlight}</span>}
- </div>
- </div>
- );
- }
-}
-
-Action.propTypes = {
- title: PropTypes.string.isRequired,
- onClick: PropTypes.func.isRequired,
- highlight: PropTypes.any,
- isActive: PropTypes.bool
-};
-
-Action.defaultProps = {
- highlight: null,
- isActive: false
-};
diff --git a/diplomacy/web/src/gui/core/button.jsx b/diplomacy/web/src/gui/core/button.jsx
deleted file mode 100644
index 0d5dadd..0000000
--- a/diplomacy/web/src/gui/core/button.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-
-export class Button extends React.Component {
- /** Bootstrap button.
- * Bootstrap classes:
- * - btn
- * - btn-primary
- * - mx-1 (margin-left 1px, margin-right 1px)
- * Props: title (str), onClick (function).
- * **/
- // title
- // onClick
- // pickEvent = false
- // large = false
- // small = false
-
- constructor(props) {
- super(props);
- this.onClick = this.onClick.bind(this);
- }
-
- onClick(event) {
- if (this.props.onClick)
- this.props.onClick(this.props.pickEvent ? event : null);
- }
-
- render() {
- return (
- <button
- className={`btn btn-${this.props.color || 'secondary'}` + (this.props.large ? ' btn-block' : '') + (this.props.small ? ' btn-sm' : '')}
- disabled={this.props.disabled}
- onClick={this.onClick}>
- <strong>{this.props.title}</strong>
- </button>
- );
- }
-}
-
-Button.propTypes = {
- title: PropTypes.string.isRequired,
- onClick: PropTypes.func.isRequired,
- color: PropTypes.string,
- large: PropTypes.bool,
- small: PropTypes.bool,
- pickEvent: PropTypes.bool,
- disabled: PropTypes.bool
-};
-
-Button.defaultPropTypes = {
- disabled: false
-};
diff --git a/diplomacy/web/src/gui/core/delete_button.jsx b/diplomacy/web/src/gui/core/delete_button.jsx
deleted file mode 100644
index 59141fd..0000000
--- a/diplomacy/web/src/gui/core/delete_button.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from "react";
-import {Button} from "./button";
-import PropTypes from "prop-types";
-
-export class DeleteButton extends React.Component {
- constructor(props) {
- super(props);
- this.state = {step: 0};
- this.onClick = this.onClick.bind(this);
- }
-
- onClick() {
- this.setState({step: this.state.step + 1}, () => {
- if (this.state.step === 2)
- this.props.onClick();
- });
- }
-
- render() {
- let title = '';
- let color = '';
- if (this.state.step === 0) {
- title = this.props.title;
- color = 'secondary';
- } else if (this.state.step === 1) {
- title = this.props.confirmTitle;
- color = 'danger';
- } else if (this.state.step === 2) {
- title = this.props.waitingTitle;
- color = 'danger';
- }
- return (
- <Button title={title} color={color} onClick={this.onClick} small={true} large={true}/>
- );
- }
-}
-
-DeleteButton.propTypes = {
- title: PropTypes.string.isRequired,
- confirmTitle: PropTypes.string.isRequired,
- waitingTitle: PropTypes.string.isRequired,
- onClick: PropTypes.func.isRequired
-};
diff --git a/diplomacy/web/src/gui/core/fancybox.jsx b/diplomacy/web/src/gui/core/fancybox.jsx
deleted file mode 100644
index 66a1efe..0000000
--- a/diplomacy/web/src/gui/core/fancybox.jsx
+++ /dev/null
@@ -1,59 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-import React from 'react';
-import PropTypes from 'prop-types';
-import {Button} from "./button";
-
-const TIMES = '\u00D7';
-
-export class FancyBox extends React.Component {
- // open-tag (<FancyBox></FancyBox>)
- // PROPERTIES
- // title
- // onClose
- render() {
- return (
- <div className={'fancy-wrapper'} onClick={this.props.onClose}>
- <div className={'fancy-box container'} onClick={(event) => {
- if (!event)
- event = window.event;
- if (event.hasOwnProperty('cancelBubble'))
- event.cancelBubble = true;
- if (event.stopPropagation)
- event.stopPropagation();
- }}>
- <div className={'row fancy-bar'}>
- <div className={'col-11 align-self-center fancy-title'}>{this.props.title}</div>
- <div className={'col-1 fancy-button'}>
- <Button title={TIMES} color={'danger'} onClick={this.props.onClose}/>
- </div>
- </div>
- <div className={'row'}>
- <div className={'col fancy-content'}>{this.props.children}</div>
- </div>
- </div>
- </div>
- );
- }
-}
-
-
-FancyBox.propTypes = {
- title: PropTypes.string.isRequired,
- onClose: PropTypes.func.isRequired,
- children: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
-};
diff --git a/diplomacy/web/src/gui/core/forms.jsx b/diplomacy/web/src/gui/core/forms.jsx
deleted file mode 100644
index da7250d..0000000
--- a/diplomacy/web/src/gui/core/forms.jsx
+++ /dev/null
@@ -1,116 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-import React from "react";
-import {UTILS} from "../../diplomacy/utils/utils";
-import {Button} from "./button";
-
-export class Forms {
- static createOnChangeCallback(component, callback) {
- return (event) => {
- const value = UTILS.html.isCheckBox(event.target) ? event.target.checked : event.target.value;
- const fieldName = UTILS.html.isRadioButton(event.target) ? event.target.name : event.target.id;
- const update = {[fieldName]: value};
- const state = Object.assign({}, component.state, update);
- if (callback)
- callback(state);
- component.setState(state);
- };
- }
-
- static createOnSubmitCallback(component, callback, resetState) {
- return (event) => {
- if (callback)
- callback(Object.assign({}, component.state));
- if (resetState)
- component.setState(resetState);
- event.preventDefault();
- };
- }
-
- static createOnResetCallback(component, onChangeCallback, resetState) {
- return (event) => {
- if (onChangeCallback)
- onChangeCallback(resetState);
- component.setState(resetState);
- if (event && event.preventDefault)
- event.preventDefault();
- };
- }
-
- static getValue(fieldValues, fieldName, defaultValue) {
- return fieldValues.hasOwnProperty(fieldName) ? fieldValues[fieldName] : defaultValue;
- }
-
- static createReset(title, large, onReset) {
- return <Button key={'reset'} title={title || 'reset'} onClick={onReset} pickEvent={true} large={large}/>;
- }
-
- static createSubmit(title, large, onSubmit) {
- return <Button key={'submit'} title={title || 'submit'} onClick={onSubmit} pickEvent={true} large={large}/>;
- }
-
- static createButton(title, fn, color, large) {
- const wrapFn = (event) => {
- fn();
- event.preventDefault();
- };
- return <Button large={large} key={title} color={color} title={title} onClick={wrapFn} pickEvent={true}/>;
- }
-
- static createCheckbox(id, title, value, onChange) {
- const input = <input className={'form-check-input'} key={id} type={'checkbox'} id={id} checked={value}
- onChange={onChange}/>;
- const label = <label className={'form-check-label'} key={`label-${id}`} htmlFor={id}>{title}</label>;
- return [input, label];
- }
-
- static createRadio(name, value, title, currentValue, onChange) {
- const id = `[${name}][${value}]`;
- const input = <input className={'form-check-input'} key={id} type={'radio'}
- name={name} value={value} checked={currentValue === value}
- id={id} onChange={onChange}/>;
- const label = <label className={'form-check-label'} key={`label-${id}`} htmlFor={id}>{title || value}</label>;
- return [input, label];
- }
-
- static createRow(label, input) {
- return (
- <div className={'form-group row'}>
- {label}
- <div className={'col'}>{input}</div>
- </div>
- );
- }
-
- static createLabel(htmFor, title, className) {
- return <label className={className} htmlFor={htmFor}>{title}</label>;
- }
-
- static createColLabel(htmlFor, title) {
- return Forms.createLabel(htmlFor, title, 'col');
- }
-
- static createSelectOptions(values, none) {
- const options = values.slice();
- const components = options.map((option, index) => <option key={index} value={option}>{option}</option>);
- if (none) {
- components.splice(0, 0, [<option key={-1} value={''}>{none === true ? '(none)' : `${none}`}</option>]);
- }
- return components;
- }
-}
-
diff --git a/diplomacy/web/src/gui/core/layouts.jsx b/diplomacy/web/src/gui/core/layouts.jsx
deleted file mode 100644
index 78189e4..0000000
--- a/diplomacy/web/src/gui/core/layouts.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-import React from 'react';
-import PropTypes from 'prop-types';
-
-class Div extends React.Component {
- getClassName() {
- return '';
- }
-
- render() {
- return (
- <div className={this.getClassName() + (this.props.className ? ' ' + this.props.className : '')}>
- {this.props.children}
- </div>
- );
- }
-}
-
-Div.propTypes = {
- className: PropTypes.string,
- children: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
-};
-
-export class Bar extends Div {
- getClassName() {
- return 'bar';
- }
-}
-
-export class Row extends Div {
- getClassName() {
- return 'row';
- }
-}
-
-export class Col extends Div {
- getClassName() {
- return 'col';
- }
-}
diff --git a/diplomacy/web/src/gui/core/page.jsx b/diplomacy/web/src/gui/core/page.jsx
deleted file mode 100644
index 5e7aee2..0000000
--- a/diplomacy/web/src/gui/core/page.jsx
+++ /dev/null
@@ -1,375 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-/** Main class to use to create app GUI. **/
-
-import React from "react";
-import {ContentConnection} from "../diplomacy/contents/content_connection";
-import {UTILS} from "../../diplomacy/utils/utils";
-import {Diplog} from "../../diplomacy/utils/diplog";
-import {FancyBox} from "./fancybox";
-import {DipStorage} from "../diplomacy/utils/dipStorage";
-import {PageContext} from "../diplomacy/widgets/page_context";
-import {ContentGames} from "../diplomacy/contents/content_games";
-import {loadGameFromDisk} from "../diplomacy/utils/load_game_from_disk";
-import {ContentGame} from "../diplomacy/contents/content_game";
-
-export class Page extends React.Component {
-
- constructor(props) {
- super(props);
- this.connection = null;
- this.channel = null;
- this.availableMaps = null;
- this.state = {
- // fancybox,
- fancyTitle: null,
- onFancyBox: null,
- // Page messages
- error: null,
- info: null,
- success: null,
- // Page content parameters
- name: null,
- body: null,
- // Games.
- games: {}, // Games found.
- myGames: {} // Games locally stored.
- };
- this.error = this.error.bind(this);
- this.info = this.info.bind(this);
- this.success = this.success.bind(this);
- this.logout = this.logout.bind(this);
- this.loadGameFromDisk = this.loadGameFromDisk.bind(this);
- this.unloadFancyBox = this.unloadFancyBox.bind(this);
- this._post_remove = this._post_remove.bind(this);
- this._add_to_my_games = this._add_to_my_games.bind(this);
- this._remove_from_my_games = this._remove_from_my_games.bind(this);
- this._remove_from_games = this._remove_from_games.bind(this);
- this.onReconnectionError = this.onReconnectionError.bind(this);
- }
-
- static wrapMessage(message) {
- return message ? `(${UTILS.date()}) ${message}` : '';
- }
-
- static __sort_games(games) {
- // Sort games with not-joined games first, else compare game ID.
- games.sort((a, b) => (((a.role ? 1 : 0) - (b.role ? 1 : 0)) || a.game_id.localeCompare(b.game_id)));
- return games;
- }
-
- static defaultPage() {
- return <ContentConnection/>;
- }
-
- onReconnectionError(error) {
- this.__disconnect(error);
- }
-
- //// Methods to load a global fancybox.
-
- loadFancyBox(title, callback) {
- this.setState({fancyTitle: title, onFancyBox: callback});
- }
-
- unloadFancyBox() {
- this.setState({fancyTitle: null, onFancyBox: null});
- }
-
- //// Methods to load a page.
-
- load(name, body, messages) {
- const newState = {};
- if (messages) {
- for (let key of ['error', 'info', 'success'])
- newState[key] = Page.wrapMessage(messages[key]);
- }
- Diplog.printMessages(newState);
- newState.name = name;
- newState.body = body;
- this.setState(newState);
- }
-
- loadGames(messages) {
- this.load(
- 'games',
- <ContentGames myGames={this.getMyGames()} gamesFound={this.getGamesFound()}/>,
- messages
- );
- }
-
- loadGameFromDisk() {
- loadGameFromDisk(
- (game) => this.load(
- `game: ${game.game_id}`,
- <ContentGame data={game}/>,
- {success: `Game loaded from disk: ${game.game_id}`}
- ),
- this.error
- );
- }
-
- getName() {
- return this.state.name;
- }
-
- //// Methods to sign out channel and go back to connection page.
-
- __disconnect(error) {
- // Clear local data and go back to connection page.
- this.connection.close();
- this.connection = null;
- this.channel = null;
- this.availableMaps = null;
- const message = Page.wrapMessage(error ? `${error.toString()}` : `Disconnected from channel and server.`);
- Diplog.success(message);
- this.setState({
- error: error ? message : null,
- info: null,
- success: error ? null : message,
- name: null,
- body: null,
- // When disconnected, remove all games previously loaded.
- games: {},
- myGames: {}
- });
- }
-
- logout() {
- // Disconnect channel and go back to connection page.
- if (this.channel) {
- this.channel.logout()
- .then(() => this.__disconnect())
- .catch(error => this.error(`Error while disconnecting: ${error.toString()}.`));
- } else {
- this.__disconnect();
- }
- }
-
- //// Methods to be used to set page title and messages.
-
- error(message) {
- message = Page.wrapMessage(message);
- Diplog.error(message);
- this.setState({error: message});
- }
-
- info(message) {
- message = Page.wrapMessage(message);
- Diplog.info(message);
- this.setState({info: message});
- }
-
- success(message) {
- message = Page.wrapMessage(message);
- Diplog.success(message);
- this.setState({success: message});
- }
-
- warn(message) {
- this.info(message);
- }
-
- //// Methods to manage games.
-
- updateMyGames(gamesToAdd) {
- // Update state myGames with given games. This method does not update local storage.
- const myGames = Object.assign({}, this.state.myGames);
- let gamesFound = null;
- for (let gameToAdd of gamesToAdd) {
- myGames[gameToAdd.game_id] = gameToAdd;
- if (this.state.games.hasOwnProperty(gameToAdd.game_id)) {
- if (!gamesFound)
- gamesFound = Object.assign({}, this.state.games);
- gamesFound[gameToAdd.game_id] = gameToAdd;
- }
- }
- if (!gamesFound)
- gamesFound = this.state.games;
- this.setState({myGames: myGames, games: gamesFound});
- }
-
- getGame(gameID) {
- if (this.state.myGames.hasOwnProperty(gameID))
- return this.state.myGames[gameID];
- return this.state.games[gameID];
- }
-
- getMyGames() {
- return Page.__sort_games(Object.values(this.state.myGames));
- }
-
- getGamesFound() {
- return Page.__sort_games(Object.values(this.state.games));
- }
-
- addGamesFound(gamesToAdd) {
- const gamesFound = {};
- for (let game of gamesToAdd) {
- gamesFound[game.game_id] = (
- this.state.myGames.hasOwnProperty(game.game_id) ?
- this.state.myGames[game.game_id] : game
- );
- }
- this.setState({games: gamesFound});
- }
-
- leaveGame(gameID) {
- if (this.state.myGames.hasOwnProperty(gameID)) {
- const game = this.state.myGames[gameID];
- if (game.client) {
- game.client.leave()
- .then(() => {
- this.disconnectGame(gameID).then(() => {
- this.loadGames({info: `Game ${gameID} left.`});
- });
- })
- .catch(error => this.error(`Error when leaving game ${gameID}: ${error.toString()}`));
- }
- } else {
- this.loadGames({info: `No game to left.`});
- }
- }
-
- _post_remove(gameID) {
- this.disconnectGame(gameID)
- .then(() => {
- const myGames = this._remove_from_my_games(gameID);
- const games = this._remove_from_games(gameID);
- this.setState(
- {games, myGames},
- () => this.loadGames({info: `Game ${gameID} deleted.`}));
- });
- }
-
- removeGame(gameID) {
- const game = this.getGame(gameID);
- if (game) {
- if (game.client) {
- game.client.remove()
- .then(() => this._post_remove(gameID))
- .catch(error => this.error(`Error when deleting game ${gameID}: ${error.toString()}`));
- } else {
- this.channel.joinGame({game_id: gameID})
- .then(networkGame => {
- networkGame.remove()
- .then(() => this._post_remove(gameID))
- .catch(error => this.error(`Error when deleting game ${gameID}: ${error.toString()}`));
- })
- .catch(error => this.error(`Error when connecting to game to delete (${gameID}): ${error.toString()}`));
- }
- }
- }
-
-
- disconnectGame(gameID) {
- const game = this.getGame(gameID);
- if (game) {
- if (game.client)
- game.client.clearAllCallbacks();
- return this.channel.getGamesInfo({games: [gameID]})
- .then(gamesInfo => {
- this.updateMyGames(gamesInfo);
- })
- .catch(error => this.error(`Error while leaving game ${gameID}: ${error.toString()}`));
- }
- return null;
- }
-
- _add_to_my_games(game) {
- const myGames = Object.assign({}, this.state.myGames);
- const gamesFound = this.state.games.hasOwnProperty(game.game_id) ? Object.assign({}, this.state.games) : this.state.games;
- myGames[game.game_id] = game;
- if (gamesFound.hasOwnProperty(game.game_id))
- gamesFound[game.game_id] = game;
- return {myGames: myGames, games: gamesFound};
- }
-
- _remove_from_my_games(gameID) {
- if (this.state.myGames.hasOwnProperty(gameID)) {
- const games = Object.assign({}, this.state.myGames);
- delete games[gameID];
- DipStorage.removeUserGame(this.channel.username, gameID);
- return games;
- } else {
- return this.state.myGames;
- }
- }
-
- _remove_from_games(gameID) {
- if (this.state.games.hasOwnProperty(gameID)) {
- const games = Object.assign({}, this.state.games);
- delete games[gameID];
- return games;
- } else {
- return this.state.games;
- }
- }
-
- addToMyGames(game) {
- // Update state myGames with given game **and** update local storage.
- DipStorage.addUserGame(this.channel.username, game.game_id);
- this.setState(this._add_to_my_games(game), () => this.loadGames());
- }
-
- removeFromMyGames(gameID) {
- const myGames = this._remove_from_my_games(gameID);
- if (myGames !== this.state.myGames)
- this.setState({myGames}, () => this.loadGames());
- }
-
- hasMyGame(gameID) {
- return this.state.myGames.hasOwnProperty(gameID);
- }
-
- //// Render method.
-
- render() {
- const successMessage = this.state.success || '-';
- const infoMessage = this.state.info || '-';
- const errorMessage = this.state.error || '-';
- return (
- <PageContext.Provider value={this}>
- <div className="page container-fluid" id={this.state.contentName}>
- <div className={'top-msg row'}>
- <div title={successMessage !== '-' ? successMessage : ''}
- className={'col-sm-4 msg success ' + (this.state.success ? 'with-msg' : 'no-msg')}
- onClick={() => this.success()}>
- {successMessage}
- </div>
- <div title={infoMessage !== '-' ? infoMessage : ''}
- className={'col-sm-4 msg info ' + (this.state.info ? 'with-msg' : 'no-msg')}
- onClick={() => this.info()}>
- {infoMessage}
- </div>
- <div title={errorMessage !== '-' ? errorMessage : ''}
- className={'col-sm-4 msg error ' + (this.state.error ? 'with-msg' : 'no-msg')}
- onClick={() => this.error()}>
- {errorMessage}
- </div>
- </div>
- {this.state.body || Page.defaultPage()}
- {this.state.onFancyBox && (
- <FancyBox title={this.state.fancyTitle} onClose={this.unloadFancyBox}>
- {this.state.onFancyBox()}
- </FancyBox>
- )}
- </div>
- </PageContext.Provider>
- );
- }
-}
diff --git a/diplomacy/web/src/gui/core/tab.jsx b/diplomacy/web/src/gui/core/tab.jsx
deleted file mode 100644
index f1ad4aa..0000000
--- a/diplomacy/web/src/gui/core/tab.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-
-export class Tab extends React.Component {
- render() {
- const style = {
- display: this.props.display ? 'block' : 'none'
- };
- const id = this.props.id ? {id: this.props.id} : {};
- return (
- <div className={'tab mb-4 ' + this.props.className} style={style} {...id}>
- {this.props.children}
- </div>
- );
- }
-}
-
-Tab.propTypes = {
- display: PropTypes.bool,
- className: PropTypes.string,
- id: PropTypes.string,
- children: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
-};
-
-Tab.defaultProps = {
- display: false,
- className: '',
- id: ''
-};
diff --git a/diplomacy/web/src/gui/core/table.jsx b/diplomacy/web/src/gui/core/table.jsx
deleted file mode 100644
index cb729e7..0000000
--- a/diplomacy/web/src/gui/core/table.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-// ==============================================================================
-// 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
-};
diff --git a/diplomacy/web/src/gui/core/tabs.jsx b/diplomacy/web/src/gui/core/tabs.jsx
deleted file mode 100644
index a3f6b9b..0000000
--- a/diplomacy/web/src/gui/core/tabs.jsx
+++ /dev/null
@@ -1,69 +0,0 @@
-// ==============================================================================
-// 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/>.
-// ==============================================================================
-import React from "react";
-import {Action} from "./action";
-import PropTypes from 'prop-types';
-
-export class Tabs extends React.Component {
- /** PROPERTIES
- * active: index of active menu (must be > menu.length).
- * highlights: dictionary mapping a menu indice to a highlight message
- * onChange: callback(index): receive index of menu to display.
- * **/
-
- generateTabAction(tabTitle, tabId, isActive, onChange, highlight) {
- return <Action isActive={isActive}
- title={tabTitle}
- onClick={() => onChange(tabId)}
- highlight={highlight}
- key={tabId}/>;
- }
-
- render() {
- if (!this.props.menu.length)
- throw new Error(`No tab menu given.`);
- if (this.props.menu.length !== this.props.titles.length)
- throw new Error(`Menu length (${this.props.menu.length}) != titles length (${this.props.titles.length})`);
- if (this.props.active && !this.props.menu.includes(this.props.active))
- throw new Error(`Invalid active tab name, got ${this.props.active}, expected one of: ${this.props.menu.join(', ')}`);
- const active = this.props.active || this.props.menu[0];
- return (
- <div className={'tabs mb-3'}>
- <nav className={'tabs-bar nav nav-tabs justify-content-center mb-3'}>
- {this.props.menu.map((tabName, index) => this.generateTabAction(
- this.props.titles[index], tabName, active === tabName, this.props.onChange,
- (this.props.highlights.hasOwnProperty(tabName) && this.props.highlights[tabName]) || null
- ))}
- </nav>
- {this.props.children}
- </div>
- );
- }
-}
-
-Tabs.propTypes = {
- menu: PropTypes.arrayOf(PropTypes.string).isRequired, // tab names
- titles: PropTypes.arrayOf(PropTypes.string).isRequired, // tab titles
- onChange: PropTypes.func.isRequired, // callback(tab name)
- children: PropTypes.array.isRequired,
- active: PropTypes.string, // current active tab name
- highlights: PropTypes.object, // {tab name => highligh message (optional)}
-};
-
-Tabs.defaultProps = {
- highlights: {}
-};