"max_score",
];
+const basicTables = [
+ "race",
+ "class",
+ "personality",
+];
+
+const realmTables = [
+ "realm1",
+ "realm2",
+];
+
+const allTables = basicTables.concat(realmTables);
+
/**
* 表示テーブル選択ボタンコンポーネントプロパティ
*/
}
public render() {
- if (this.props.selected) {
- return (
+ const { selected, name, onClick } = this.props;
+
+ const button = (selected) ?
+ (
<b>
- {name2j[this.props.name]}
+ {name2j[name]}
</b>
- );
- } else {
- return (
- <a href="javascript:void(0)" onClick={() => this.props.onClick()}>
- {name2j[this.props.name]}
+ )
+ :
+ (
+ <a href="javascript:void(0)" onClick={onClick}>
+ {name2j[name]}
</a>
);
- }
+
+ return <span className="table-select-button">{button}</span>;
}
}
* 選択テーブル切替時に呼び出されるコールバック関数
* @param name 新たに選択されたテーブル名称
*/
- onSelectChange: (name: string) => void;
+ onSelectedTableChange: (name: string) => void;
}
/**
*/
// tslint:disable-next-line:max-classes-per-file
class TableSelector extends React.Component<ITableSelectProps> {
+ private onSelectedTableChange: { [key: string]: () => void } = {};
+
+ constructor(props: ITableSelectProps) {
+ super(props);
+
+ allTables.forEach((t) =>
+ this.onSelectedTableChange[t] = () => this.props.onSelectedTableChange(t),
+ );
+ }
+
public render() {
return (
<div>
- [ {this.renderSelectButton("race")} | {this.renderSelectButton("class")} |
- {this.renderSelectButton("personality")} ]
- [ {this.renderSelectButton("realm1")} | {this.renderSelectButton("realm2")} ]
+ [{this.renderSelectBussons(basicTables)}]
+ [{this.renderSelectBussons(realmTables)}]
</div>
);
}
+ private renderSelectBussons(tables: string[]) {
+ return (
+ tables.map((t) => this.renderSelectButton(t)).
+ map((t, i) => <span key={i}>{t}{i === tables.length - 1 ? "" : "|"}</span>)
+ );
+ }
+
private renderSelectButton(name: string) {
- return <TableSelectButton
- selected={this.props.selectedTableName === name}
- name={name}
- onClick={() => this.props.onSelectChange(name)} />;
+ return (
+ <TableSelectButton
+ selected={this.props.selectedTableName === name}
+ name={name}
+ onClick={this.onSelectedTableChange[name]}
+ />
+ );
}
}
* テーブルヘッダコンポーネント
*/
function TableHeader(props: ITableHeaderProps) {
+ const tableHeaderColumns = columnOrder.map((col) => {
+ let className = "sort";
+ if (props.sortKeyColumn === col) {
+ className += " ";
+ className += (props.sortOrder === SortOrder.Ascend) ? "ascend" : "descend";
+ }
+ const onClick = () => props.onClick(col);
+ return (
+ <th className={className} onClick={onClick} key={col}>
+ {column2j[col]}
+ </th>
+ );
+ });
+
return (
<thead>
<tr>
<th>{props.tableName}</th>
- {columnOrder.map((col) => {
- let className = "sort";
- if (props.sortKeyColumn === col) {
- className += " ";
- className += (props.sortOrder === SortOrder.Ascend) ? "ascend" : "descend";
- }
- return (
- <th className={className} onClick={() => props.onClick(col)}>
- {column2j[col]}
- </th>
- );
- })}
+ {tableHeaderColumns}
</tr>
</thead>
);
* テーブル行データコンポーネント
*/
function TableDataRow(props: ITableDataRowProps) {
+ const dataColumns = columnOrder.map((col) =>
+ <td className="number" key={col}>{props.row_data[col]}</td>);
+
return (
- <tr key={props.row_data.id}>
+ <tr>
<td>
<a href={`score_ranking.php?${props.linkParam}`}>
{props.rowName}
</a>
</td>
- {columnOrder.map((col) =>
- <td className="number">{props.row_data[col]}</td>)}
+ {dataColumns}
</tr>
);
}
sortKeyColumn: columnOrder[0],
sortOrder: SortOrder.Descend,
};
+
+ this.selectSortColumn = this.selectSortColumn.bind(this);
+ }
+
+ public shouldComponentUpdate(nextProps: IPopuralityTableProps, nextState: IPopuralityTableState) {
+ const { visible } = this.props;
+ const { sortKeyColumn, sortOrder } = this.state;
+ return (visible !== nextProps.visible) ||
+ (sortKeyColumn !== nextState.sortKeyColumn) ||
+ (sortOrder !== nextState.sortOrder);
}
public render() {
return "";
}
- const data = this.get_sorted_data();
const tableName = this.props.name;
+
+ const dataRows = this.get_sorted_data().map((row) => (
+ <TableDataRow
+ linkParam={`${tableName}_id=${row.id}`}
+ rowName={row.name}
+ row_data={row}
+ key={row.id}
+ />
+ ));
return (
<table className="score statistics_table one_row">
<TableHeader
tableName={name2j[tableName]}
sortKeyColumn={this.state.sortKeyColumn}
sortOrder={this.state.sortOrder}
- onClick={this.selectSortColumn.bind(this)}
+ onClick={this.selectSortColumn}
/>
- <tbody>
- {data.map((row) =>
- <TableDataRow
- linkParam={`${tableName}_id=${row.id}`}
- rowName={row.name}
- row_data={row}
- />)}
- </tbody>
+ <tbody>{dataRows}</tbody>
</table>
);
}
/**
* ソートするカラムを選択する
- * @param column ソートするカラムの名称
+ * @param sortKeyColumn ソートするカラムの名称
*/
- protected selectSortColumn(column: string) {
- let newSortOrder = SortOrder.Descend;
- if (this.state.sortKeyColumn === column) {
- newSortOrder =
+ protected selectSortColumn(sortKeyColumn: string) {
+ let sortOrder = SortOrder.Descend;
+
+ if (this.state.sortKeyColumn === sortKeyColumn) {
+ sortOrder =
(this.state.sortOrder === SortOrder.Ascend) ?
SortOrder.Descend : SortOrder.Ascend;
}
- this.setState({
- sortKeyColumn: column,
- sortOrder: newSortOrder,
- });
+
+ this.setState({ sortKeyColumn, sortOrder });
}
}
return "";
}
- const data = this.get_sorted_data();
const realm = this.props.name;
+ const dataRows = this.get_sorted_data().map((row) => (
+ <TableDataRow
+ linkParam={`class_id=${row.class_id}&${realm}_id=${row.realm_id}`}
+ rowName={row.realm_name}
+ row_data={row}
+ key={row.realm_id}
+ />
+ ));
+
return (
<table className="score statistics_table one_row">
<TableHeader
tableName={this.props.data[0].class_name}
sortKeyColumn={this.state.sortKeyColumn}
sortOrder={this.state.sortOrder}
- onClick={this.selectSortColumn.bind(this)}
+ onClick={this.selectSortColumn}
/>
- <tbody>
- {data.map((row) =>
- <TableDataRow
- linkParam={`class_id=${row.class_id}&${realm}_id=${row.realm_id}`}
- rowName={row.realm_name}
- row_data={row}
- />)}
- </tbody>
+ <tbody>{dataRows}</tbody>
</table>
);
}
// tslint:disable-next-line:max-classes-per-file
class RankingTables extends React.Component<IRankingTablesProps> {
public shouldComponentUpdate(nextProps: IRankingTablesProps) {
- return (this.props.data === null && nextProps.data !== null) ||
+ return (Object.keys(this.props.data).length === 0 && Object.keys(nextProps.data).length > 0) ||
(this.props.selectedTableName !== nextProps.selectedTableName);
}
public render() {
- const tables = Object.keys(name2j).map((name) => {
- if (!name.startsWith("realm")) {
- return (
- <div id={name} key={name}>
- <PopuralityTable
- data={this.props.data[name]}
- visible={this.props.selectedTableName === name}
- name={name} />
- </div>
- );
- } else {
- const realmTables = this.props.data[name].map((i) => {
- return <PopuralityRealmTable
- data={i}
- name={name}
- visible={this.props.selectedTableName === name}
- key={i[0].class_id} />;
- });
- return <div id={name} key={name}>{realmTables}</div>;
- }
+ const { data, selectedTableName } = this.props;
+
+ const tableElements = basicTables.map((name) => (
+ <div id={name} key={name}>
+ <PopuralityTable
+ data={data[name]}
+ visible={selectedTableName === name}
+ name={name}
+ />
+ </div>),
+ );
+
+ const realmTableElements = realmTables.map((name) => {
+ const classTables = data[name].map((i) => (
+ <PopuralityRealmTable
+ data={i}
+ name={name}
+ visible={selectedTableName === name}
+ key={i[0].class_id}
+ />),
+ );
+
+ return <div id={name} key={name}>{classTables}</div>;
});
- return <div>{tables}</div>;
+
+ return <div>{tableElements}{realmTableElements}</div>;
}
}
* 人気データ表示コンポーネントプロパティ
*/
interface IPopuralityRankingProps {
- /** 人気データ取得URL */
- dataUrl: string;
+ /** データ取得URL */
+ url: string;
}
/**
interface IPopuralityRankingState {
/** 表示選択中テーブル名称 */
selectedTableName: string;
+ /** データ取得中かどうか */
+ isLoading: boolean;
/** データ内容 */
data: { [col: string]: any[] };
/** データ表示状態を示すメッセージ */
- stateMessage: string;
+ error: string;
}
/**
this.state = {
data: {},
+ error: "",
+ isLoading: true,
selectedTableName: selectedTableName !== null ? selectedTableName : "race",
- stateMessage: "Loading...",
};
+
+ this.changeTable = this.changeTable.bind(this);
}
public componentDidMount() {
- fetch(this.props.dataUrl).then((response) => {
- if (!response.ok) {
- throw Error(`${response.status} ${response.statusText}`);
- }
- return response.json();
- }).then((data) => {
- data.realm1 = this.group_by_class(data.realm1);
- data.realm2 = this.group_by_class(data.realm2);
- this.setState({
- data,
- stateMessage: "Success",
- });
- }).catch((err) => {
- this.setState({ stateMessage: err.message });
- });
+ this.setState({ isLoading: true });
+ fetch(this.props.url)
+ .then((response) => {
+ if (!response.ok) {
+ throw Error(`${response.status} ${response.statusText}`);
+ }
+ return response.json();
+ })
+ .then((data) => {
+ // 魔法領域のデータを職業毎にグループ化する
+ data.realm1 = this.group_by_class(data.realm1);
+ data.realm2 = this.group_by_class(data.realm2);
+ this.setState({ data, isLoading: false });
+ })
+ .catch((err) => this.setState({ error: err.message, isLoading: false }));
}
public componentDidUpdate() {
}
public render() {
- if (Object.keys(this.state.data).length === 0) {
- return <div>{this.state.stateMessage}</div>;
+ const { data, error, isLoading, selectedTableName } = this.state;
+ if (error !== "") {
+ return <p>{error}</p>;
+ }
+ if (isLoading) {
+ return <p>Loading...</p>;
+ }
+ if (Object.keys(data).length === 0) {
+ return <p />;
}
return (
<div>
<TableSelector
- onSelectChange={this.selectTable.bind(this)}
- selectedTableName={this.state.selectedTableName}
+ onSelectedTableChange={this.changeTable}
+ selectedTableName={selectedTableName}
/>
<RankingTables
- data={this.state.data}
- selectedTableName={this.state.selectedTableName}
+ data={data}
+ selectedTableName={selectedTableName}
/>
</div>
);
}
}
- private selectTable(tableName: string) {
+ private changeTable(tableName: string) {
this.setState({ selectedTableName: tableName });
}
+ /**
+ * 魔法領域の統計データを職業IDごとにグループ化する
+ * グループ化した結果、職業IDに1種しか領域がない物は省く
+ *
+ * @param data 魔法領域の統計データ
+ * @return グループ化したデータ
+ */
private group_by_class(data: any[]) {
const result: any[] = [];
data.forEach((i) => {
}
ReactDOM.render(
- <PopuralityRanking dataUrl="get_popularity_ranking.php" />,
+ <PopuralityRanking url="get_popularity_ranking.php" />,
document.getElementById("content"),
);