get routing to list details working, misc. component folder cleanup

This commit is contained in:
Collin M. Barrett 2019-08-25 17:08:31 -05:00
parent 15ddb5f32d
commit 70228ee353
15 changed files with 217 additions and 221 deletions

View file

@ -1,13 +1,13 @@
import { Layout, Menu } from 'antd';
import React from 'react';
import { BrowserRouter, Route, RouteComponentProps, Switch } from 'react-router-dom';
import { BrowserRouter as Router, Route, RouteComponentProps, Switch } from 'react-router-dom';
import { AllListsTable } from './components';
import { ListsTable } from './components';
const { Header, Content, Footer } = Layout;
export const App: React.FC = () =>
<BrowserRouter>
<Router>
<Layout>
<Header>
<Logo />
@ -16,7 +16,7 @@ export const App: React.FC = () =>
<Content>
<div style={{ background: '#fff', paddingLeft: 12, paddingTop: 12, paddingRight: 12, minHeight: 280 }}>
<Switch>
<Route path="/" exact component={AllListsTable} />
<Route path="/" component={ListsTable} />
<Route component={NotFound} />
</Switch>
</div>
@ -25,7 +25,7 @@ export const App: React.FC = () =>
<CopyrightAuthor /> | <Community /> | <GitHub /> | <Api /> | <Donate />
</Footer>
</Layout>
</BrowserRouter>;
</Router>;
const Logo = (): JSX.Element =>
<img src={`${process.env.PUBLIC_URL}/logo_filterlists.png`}

View file

@ -0,0 +1,16 @@
import { Button } from 'antd';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { List } from '../interfaces/List';
interface Props {
list: List;
};
export const ListDetailsButton = (props: RouteComponentProps & Props) =>
<Button
type="primary"
onClick={() => props.history.push(`/lists/${props.list.id}`)}>
Details
</Button>;

View file

@ -0,0 +1,19 @@
import { Drawer } from 'antd';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { List } from '../interfaces/List';
import { Description } from './Description';
import { SubscribeButton } from './subscribeButton';
interface Props {
list: List;
};
export const ListDetailsDrawer = (props: RouteComponentProps & Props) =>
<Drawer
visible={true}
onClose={() => props.history.push("/")}>
<Description {...props.list} />
<SubscribeButton {...props.list} />
</Drawer>;

View file

@ -1,159 +0,0 @@
import { Table } from 'antd';
import React from 'react';
import { Description, LanguageCloud, ListDetails, TagCloud } from '..';
import { Language } from '../../interfaces/Language';
import { List } from '../../interfaces/List';
import { Tag } from '../../interfaces/Tag';
import { nameof } from '../../utils';
import styles from './AllListsTable.module.css';
interface State {
lists: List[];
languages: Language[];
tags: Tag[];
pageSize: number;
pageSizeOptions: string[];
isNarrowWindow: boolean;
}
export class AllListsTable extends React.Component<{}, State> {
constructor(props: any) {
super(props);
this.state = {
lists: [],
languages: [],
tags: [],
pageSize: 0,
pageSizeOptions: [],
isNarrowWindow: false
};
this.updatePageSize = this.updatePageSize.bind(this);
}
componentDidMount() {
this.fetchData();
this.updatePageSize();
window.addEventListener('resize', this.updatePageSize);
}
private fetchData() {
fetch("/api/v1/lists")
.then(response => response.json())
.then(json => (json as List[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(lists => { this.setState({ lists: lists }); })
fetch("/api/v1/languages")
.then(response => response.json())
.then(json => (json as Language[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(languages => { this.setState({ languages: languages }); })
fetch("/api/v1/tags")
.then(response => response.json())
.then(json => (json as Tag[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(tags => { this.setState({ tags: tags }); })
}
private updatePageSize() {
const pageSize = Math.floor((window.innerHeight - 211.5) / 56);
this.setState({
pageSize: pageSize,
pageSizeOptions: [pageSize, 200, 2000].sort((a, b) => a - b).map(String),
isNarrowWindow: window.innerWidth < 576 ? true : false
});
}
render() {
return (
<Table<List>
dataSource={this.state.lists}
rowKey={record => record.id.toString()}
loading={this.state.lists.length ? false : true}
size="small"
pagination={{
size: "small",
pageSize: this.state.pageSize,
showSizeChanger: true,
pageSizeOptions: this.state.pageSizeOptions
}}
scroll={{ x: this.state.isNarrowWindow ? undefined : 1200 }}>
<Table.Column<List>
title="Name"
dataIndex={nameof<List>("name")}
sorter={(a, b) => a.name.localeCompare(b.name)}
defaultSortOrder={"ascend"}
width={this.state.isNarrowWindow ? undefined : 200}
className={styles.nogrow}
fixed="left"
render={(text: string) => <div>{text}</div>} />
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Description"
dataIndex={nameof<List>("description")}
className={styles.nogrow}
render={(_text: string, record: List) => <Description {...record} />} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Software"
dataIndex={nameof<List>("syntaxId")}
className={styles.nogrow}
render={(text: string) => <div>{text}</div>} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Languages"
dataIndex={nameof<List>("languageIds")}
sorter={(a, b) => arraySorter(a.languageIds, b.languageIds, this.state.languages)}
width={125}
className={styles.nogrow}
render={(languageIds: number[]) =>
languageIds
? <LanguageCloud languages={this.state.languages.filter((l: Language) => languageIds.includes(l.id))} />
: null} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Tags"
dataIndex={nameof<List>("tagIds")}
sorter={(a, b) => arraySorter(a.tagIds, b.tagIds, this.state.tags)}
width={275}
className={styles.nogrow}
render={(tagIds: number[]) =>
tagIds
? <TagCloud tags={this.state.tags.filter((t: Tag) => tagIds.includes(t.id))} />
: null} />}
<Table.Column<List>
title="Details"
dataIndex={nameof<List>("viewUrl")}
className={styles.nogrow}
fixed="right"
render={(_text: string, record: List) => <ListDetails list={record} />} />
</Table>
);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updatePageSize);
}
}
interface ArraySortableEntity {
id: number;
name: string;
}
const arraySorter = (a: number[], b: number[], entities: ArraySortableEntity[]): number =>
a && a.length
? b && b.length
? a.length === b.length
? entities.filter((e: ArraySortableEntity) => a.indexOf(e.id) > -1).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
> entities.filter((e: ArraySortableEntity) => b.indexOf(e.id) > -1).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
? 1
: -1
: a.length > b.length
? -1
: 1
: -1
: 1;

View file

@ -1,3 +0,0 @@
import { AllListsTable } from './AllListsTable';
export { AllListsTable }

View file

@ -1,3 +0,0 @@
import { SubscribeButton } from './subscribe';
export { SubscribeButton }

View file

@ -1,8 +1,3 @@
import { AllListsTable } from './allListsTable';
import { SubscribeButton } from './buttons';
import { Description } from './Description';
import { LanguageCloud } from './languageCloud';
import { ListDetails } from './listDetails';
import { TagCloud } from './tagCloud';
import { ListsTable } from './listsTable';
export { SubscribeButton, Description, LanguageCloud, TagCloud, AllListsTable, ListDetails };
export { ListsTable };

View file

@ -1,41 +0,0 @@
import { Button, Drawer } from 'antd';
import React from 'react';
import { Description } from '..';
import { List } from '../../interfaces/List';
import { SubscribeButton } from '../buttons';
interface State {
visible: boolean;
}
interface Props {
list: List;
};
export class ListDetails extends React.Component<Props, State> {
constructor(props: any) {
super(props);
this.state = {
visible: false
};
}
render() {
return (
<span>
<Button
type="primary"
onClick={() => this.setState({ visible: true, })}>
Details
</Button>
<Drawer
visible={this.state.visible}
onClose={() => this.setState({ visible: false, })}>
<Description {...this.props.list} />
<SubscribeButton {...this.props.list} />
</Drawer>
</span>
)
}
}

View file

@ -1,3 +0,0 @@
import { ListDetails } from './ListDetails';
export { ListDetails };

View file

@ -0,0 +1,172 @@
import { Table } from 'antd';
import React from 'react';
import { Route, RouteComponentProps } from 'react-router-dom';
import { Language } from '../../interfaces/Language';
import { List } from '../../interfaces/List';
import { Tag } from '../../interfaces/Tag';
import { nameof } from '../../utils';
import { Description } from '../Description';
import { LanguageCloud } from '../languageCloud';
import { ListDetailsButton } from '../ListDetailsButton';
import { ListDetailsDrawer } from '../ListDetailsDrawer';
import { TagCloud } from '../tagCloud';
import styles from './ListsTable.module.css';
interface State {
lists: List[];
languages: Language[];
tags: Tag[];
pageSize: number;
pageSizeOptions: string[];
isNarrowWindow: boolean;
}
export class ListsTable extends React.Component<RouteComponentProps, State> {
constructor(props: any) {
super(props);
this.state = {
lists: [],
languages: [],
tags: [],
pageSize: 0,
pageSizeOptions: [],
isNarrowWindow: false
};
this.updatePageSize = this.updatePageSize.bind(this);
}
componentDidMount() {
this.fetchData();
this.updatePageSize();
window.addEventListener('resize', this.updatePageSize);
}
private fetchData() {
fetch("/api/v1/lists")
.then(response => response.json())
.then(json => (json as List[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(lists => { this.setState({ lists: lists }); })
fetch("/api/v1/languages")
.then(response => response.json())
.then(json => (json as Language[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(languages => { this.setState({ languages: languages }); })
fetch("/api/v1/tags")
.then(response => response.json())
.then(json => (json as Tag[]).sort((a, b) => a.name.localeCompare(b.name)))
.then(tags => { this.setState({ tags: tags }); })
}
private updatePageSize() {
const pageSize = Math.floor((window.innerHeight - 211.5) / 56);
this.setState({
pageSize: pageSize,
pageSizeOptions: [pageSize, 200, 2000].sort((a, b) => a - b).map(String),
isNarrowWindow: window.innerWidth < 576 ? true : false
});
}
render() {
return (
<span>
<Table<List>
dataSource={this.state.lists}
rowKey={record => record.id.toString()}
loading={this.state.lists.length ? false : true}
size="small"
pagination={{
size: "small",
pageSize: this.state.pageSize,
showSizeChanger: true,
pageSizeOptions: this.state.pageSizeOptions
}}
scroll={{ x: this.state.isNarrowWindow ? undefined : 1200 }}>
<Table.Column<List>
title="Name"
dataIndex={nameof<List>("name")}
sorter={(a, b) => a.name.localeCompare(b.name)}
defaultSortOrder={"ascend"}
width={this.state.isNarrowWindow ? undefined : 200}
className={styles.nogrow}
fixed="left"
render={(text: string) => <div>{text}</div>} />
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Description"
dataIndex={nameof<List>("description")}
className={styles.nogrow}
render={(_text: string, record: List) => <Description {...record} />} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Software"
dataIndex={nameof<List>("syntaxId")}
className={styles.nogrow}
render={(text: string) => <div>{text}</div>} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Languages"
dataIndex={nameof<List>("languageIds")}
sorter={(a, b) => arraySorter(a.languageIds, b.languageIds, this.state.languages)}
width={125}
className={styles.nogrow}
render={(languageIds: number[]) =>
languageIds
? <LanguageCloud languages={this.state.languages.filter((l: Language) => languageIds.includes(l.id))} />
: null} />}
{this.state.isNarrowWindow
? null
: <Table.Column<List>
title="Tags"
dataIndex={nameof<List>("tagIds")}
sorter={(a, b) => arraySorter(a.tagIds, b.tagIds, this.state.tags)}
width={275}
className={styles.nogrow}
render={(tagIds: number[]) =>
tagIds
? <TagCloud tags={this.state.tags.filter((t: Tag) => tagIds.includes(t.id))} />
: null} />}
<Table.Column<List>
title="Details"
dataIndex={nameof<List>("viewUrl")}
className={styles.nogrow}
fixed="right"
render={(_text: string, record: List) => <ListDetailsButton list={record} {...this.props} />} />
</Table>
<Route path="/lists/:id" render={props => {
const list = this.state.lists.find(l => l.id === +props.match.params.id);
return list
? <ListDetailsDrawer list={list as List} {...props} />
: null //TODO: redirect or 404 for invalid list id
}} />
</span>
);
}
componentWillUnmount() {
window.removeEventListener('resize', this.updatePageSize);
}
}
interface ArraySortableEntity {
id: number;
name: string;
}
const arraySorter = (a: number[], b: number[], entities: ArraySortableEntity[]): number =>
a && a.length
? b && b.length
? a.length === b.length
? entities.filter((e: ArraySortableEntity) => a.indexOf(e.id) > -1).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
> entities.filter((e: ArraySortableEntity) => b.indexOf(e.id) > -1).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
? 1
: -1
: a.length > b.length
? -1
: 1
: -1
: 1;

View file

@ -0,0 +1,3 @@
import { ListsTable } from './ListsTable';
export { ListsTable }