mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
get routing to list details working, misc. component folder cleanup
This commit is contained in:
parent
15ddb5f32d
commit
70228ee353
15 changed files with 217 additions and 221 deletions
|
|
@ -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`}
|
||||
|
|
|
|||
16
src/FilterLists.Web.V2/src/components/ListDetailsButton.tsx
Normal file
16
src/FilterLists.Web.V2/src/components/ListDetailsButton.tsx
Normal 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>;
|
||||
19
src/FilterLists.Web.V2/src/components/ListDetailsDrawer.tsx
Normal file
19
src/FilterLists.Web.V2/src/components/ListDetailsDrawer.tsx
Normal 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>;
|
||||
|
|
@ -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;
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import { AllListsTable } from './AllListsTable';
|
||||
|
||||
export { AllListsTable }
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import { SubscribeButton } from './subscribe';
|
||||
|
||||
export { SubscribeButton }
|
||||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
import { ListDetails } from './ListDetails';
|
||||
|
||||
export { ListDetails };
|
||||
172
src/FilterLists.Web.V2/src/components/listsTable/ListsTable.tsx
Normal file
172
src/FilterLists.Web.V2/src/components/listsTable/ListsTable.tsx
Normal 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;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import { ListsTable } from './ListsTable';
|
||||
|
||||
export { ListsTable }
|
||||
Loading…
Reference in a new issue