mirror of
https://github.com/collinbarrett/FilterLists.git
synced 2026-03-11 09:04:27 +00:00
parent
01c62e5336
commit
962dcf6ad5
55 changed files with 569 additions and 865 deletions
|
|
@ -1,9 +1,9 @@
|
|||
const { override, fixBabelImports } = require("customize-cra");
|
||||
const { override, fixBabelImports } = require('customize-cra');
|
||||
|
||||
module.exports = override(
|
||||
fixBabelImports("import", {
|
||||
libraryName: "antd",
|
||||
libraryDirectory: "es",
|
||||
style: "css"
|
||||
})
|
||||
);
|
||||
fixBabelImports('import', {
|
||||
libraryName: 'antd',
|
||||
libraryDirectory: 'es',
|
||||
style: 'css',
|
||||
}),
|
||||
);
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { App } from "./App";
|
||||
import { App } from './App';
|
||||
|
||||
it("renders without crashing", () => {
|
||||
const div = document.createElement("div");
|
||||
it('renders without crashing', () => {
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<App />, div);
|
||||
ReactDOM.unmountComponentAtNode(div);
|
||||
});
|
||||
});
|
||||
|
|
@ -1,48 +1,27 @@
|
|||
import { Icon, Layout, Menu, Tag } from "antd";
|
||||
import React from "react";
|
||||
import {
|
||||
BrowserRouter as Router,
|
||||
Link,
|
||||
Route,
|
||||
RouteComponentProps,
|
||||
Switch
|
||||
} from "react-router-dom";
|
||||
import { Icon, Layout, Menu, Tag } from 'antd';
|
||||
import React from 'react';
|
||||
import { BrowserRouter as Router, Link, Route, RouteComponentProps, Switch } from 'react-router-dom';
|
||||
|
||||
import { ListsTable } from "./components";
|
||||
import { ListsTable } from './components';
|
||||
|
||||
const { Header, Content, Footer } = Layout;
|
||||
|
||||
export const App: React.FC = () => (
|
||||
export const App: React.FC = () =>
|
||||
<Router>
|
||||
<Layout>
|
||||
<Header style={{ background: "#fff" }}>
|
||||
<Header style={{ background: '#fff' }}>
|
||||
<Logo />
|
||||
<Menu mode="horizontal" style={{ lineHeight: "64px" }} />
|
||||
<Menu mode="horizontal" style={{ lineHeight: '64px' }} />
|
||||
</Header>
|
||||
<Content>
|
||||
<div
|
||||
style={{
|
||||
background: "#fff",
|
||||
paddingLeft: 12,
|
||||
paddingTop: 12,
|
||||
paddingRight: 12,
|
||||
minHeight: 280
|
||||
}}
|
||||
>
|
||||
<div style={{ background: '#fff', paddingLeft: 12, paddingTop: 12, paddingRight: 12, minHeight: 280 }}>
|
||||
<Switch>
|
||||
<Route path="/" component={ListsTable} />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
</Content>
|
||||
<Footer
|
||||
style={{
|
||||
textAlign: "center",
|
||||
padding: "6px 50px",
|
||||
background: "#fff",
|
||||
lineHeight: "28px"
|
||||
}}
|
||||
>
|
||||
<Footer style={{ textAlign: 'center', padding: '6px 50px', background: '#fff', lineHeight: '28px' }}>
|
||||
<CopyrightAuthor />
|
||||
<Twitter />
|
||||
<Community />
|
||||
|
|
@ -51,101 +30,78 @@ export const App: React.FC = () => (
|
|||
<Donate />
|
||||
</Footer>
|
||||
</Layout>
|
||||
</Router>
|
||||
);
|
||||
</Router>;
|
||||
|
||||
const Logo = () => (
|
||||
const Logo = () =>
|
||||
<Link to="/">
|
||||
<img
|
||||
src={`${process.env.PUBLIC_URL}/logo_filterlists.png`}
|
||||
<img src={`${process.env.PUBLIC_URL}/logo_filterlists.png`}
|
||||
alt="FilterLists logo"
|
||||
height="44px"
|
||||
style={{ background: "#fff" }}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
style={{ background: '#fff' }} />
|
||||
</Link>;
|
||||
|
||||
const NotFound = (props: RouteComponentProps) => (
|
||||
const NotFound = (props: RouteComponentProps) =>
|
||||
<h2>
|
||||
404 Not Found: <code>{props.location.pathname}</code>
|
||||
</h2>
|
||||
);
|
||||
</h2>;
|
||||
|
||||
const CopyrightAuthor = () => (
|
||||
const CopyrightAuthor = () =>
|
||||
<Tag>
|
||||
©{new Date().getFullYear()}
|
||||
<a
|
||||
href="https://collinmbarrett.com"
|
||||
<a href="https://collinmbarrett.com"
|
||||
title="Collin M. Barrett's Homepage"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
Collin M. Barrett
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
||||
const Twitter = () => (
|
||||
const Twitter = () =>
|
||||
<Tag>
|
||||
<a
|
||||
href="https://twitter.com/FilterLists"
|
||||
<a href="https://twitter.com/FilterLists"
|
||||
title="FilterLists on Twitter"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="twitter" /> Twitter
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
||||
const Community = () => (
|
||||
const Community = () =>
|
||||
<Tag>
|
||||
<a
|
||||
href="https://hub.filterlists.com"
|
||||
<a href="https://hub.filterlists.com"
|
||||
title="FilterLists Discourse community forum"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="team" /> Community
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
||||
const GitHub = () => (
|
||||
const GitHub = () =>
|
||||
<Tag>
|
||||
<a
|
||||
href="https://github.com/collinbarrett/FilterLists"
|
||||
<a href="https://github.com/collinbarrett/FilterLists"
|
||||
title="FilterLists git repository on GitHub"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="github" /> GitHub
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
||||
const Api = () => (
|
||||
const Api = () =>
|
||||
<Tag>
|
||||
<a
|
||||
href="/api/v1/lists"
|
||||
<a href="/api/v1/lists"
|
||||
title="FilterLists API lists endpoint"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="api" /> API
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
||||
const Donate = () => (
|
||||
const Donate = () =>
|
||||
<Tag>
|
||||
<a
|
||||
href="https://beerpay.io/collinbarrett/FilterLists"
|
||||
<a href="https://beerpay.io/collinbarrett/FilterLists"
|
||||
title="Donate to FilterLists with Beerpay"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="dollar" /> Donate
|
||||
</a>
|
||||
</Tag>
|
||||
);
|
||||
</Tag>;
|
||||
|
|
@ -1,15 +1,11 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
description: string;
|
||||
descriptionSourceUrl: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const Description = (props: Props) =>
|
||||
props.descriptionSourceUrl ? (
|
||||
<blockquote cite={props.descriptionSourceUrl}>
|
||||
{props.description}
|
||||
</blockquote>
|
||||
) : (
|
||||
<p>{props.description}</p>
|
||||
);
|
||||
props.descriptionSourceUrl
|
||||
? <blockquote cite={props.descriptionSourceUrl}>{props.description}</blockquote>
|
||||
: <p>{props.description}</p>;
|
||||
|
|
@ -1,32 +1,32 @@
|
|||
import { Tag } from "antd";
|
||||
import * as React from "react";
|
||||
import { Tag } from 'antd';
|
||||
import * as React from 'react';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
descriptionUrl?: string;
|
||||
showLabel?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const LicenseTag = (props: Props) =>
|
||||
props.name ? (
|
||||
<span>
|
||||
props.name
|
||||
? <span>
|
||||
{props.showLabel && <h3>License:</h3>}
|
||||
<Tag>
|
||||
<TagContents name={props.name} descriptionUrl={props.descriptionUrl} />
|
||||
<TagContents
|
||||
name={props.name}
|
||||
descriptionUrl={props.descriptionUrl} />
|
||||
</Tag>
|
||||
</span>
|
||||
) : null;
|
||||
: null;
|
||||
|
||||
const TagContents = (props: Props) =>
|
||||
props.descriptionUrl ? (
|
||||
<a
|
||||
href={props.descriptionUrl}
|
||||
props.descriptionUrl
|
||||
? <a href={props.descriptionUrl}
|
||||
title={`View ${props.name}'s homepage.`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
{props.name}
|
||||
</a>
|
||||
) : (
|
||||
<>{props.name}</>
|
||||
);
|
||||
: <>
|
||||
{props.name}
|
||||
</>;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { Button } from "antd";
|
||||
import { ButtonType } from "antd/lib/button";
|
||||
import React from "react";
|
||||
import { Button } from 'antd';
|
||||
import { ButtonType } from 'antd/lib/button';
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
url: string;
|
||||
|
|
@ -8,11 +8,11 @@ interface Props {
|
|||
title?: string;
|
||||
type?: ButtonType;
|
||||
icon?: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const LinkButton = (props: Props) =>
|
||||
props.url && props.text ? (
|
||||
<Button
|
||||
props.url && props.text
|
||||
? <Button
|
||||
href={props.url}
|
||||
title={props.title}
|
||||
type={props.type || "default"}
|
||||
|
|
@ -20,8 +20,7 @@ export const LinkButton = (props: Props) =>
|
|||
style={{ borderLeftColor: "rgb(217, 217, 217)" }} //HACK: override buggy style in antd
|
||||
icon={props.icon}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer" >
|
||||
{props.text}
|
||||
</Button>
|
||||
) : null;
|
||||
</Button >
|
||||
: null;
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import { Button, Icon } from "antd";
|
||||
import React from "react";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import slugify from "slugify";
|
||||
import { Button, Icon } from 'antd';
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import slugify from 'slugify';
|
||||
|
||||
import { SlugifyOptions } from "../constants";
|
||||
import { SlugifyOptions } from '../constants';
|
||||
|
||||
interface Props {
|
||||
listName: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const ListInfoButton = (props: RouteComponentProps & Props) => {
|
||||
const listPath = `/lists/${slugify(props.listName, SlugifyOptions)}`;
|
||||
|
|
@ -15,13 +15,10 @@ export const ListInfoButton = (props: RouteComponentProps & Props) => {
|
|||
<Button
|
||||
type="primary"
|
||||
title={`View more information about ${props.listName}.`}
|
||||
onClick={() =>
|
||||
props.location.pathname === listPath
|
||||
? props.history.push("/")
|
||||
: props.history.push(listPath)
|
||||
}
|
||||
>
|
||||
onClick={() => props.location.pathname === listPath
|
||||
? props.history.push("/")
|
||||
: props.history.push(listPath)} >
|
||||
<Icon type="info-circle" />
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
publishedDate: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const PublishedDate = (props: Props) =>
|
||||
props.publishedDate ? (
|
||||
<>
|
||||
props.publishedDate
|
||||
? <>
|
||||
<h3>First Published Date:</h3>
|
||||
{new Date(props.publishedDate).toDateString()}
|
||||
</>
|
||||
) : null;
|
||||
: null;
|
||||
|
|
@ -1,50 +1,48 @@
|
|||
import { Button } from "antd";
|
||||
import { ButtonType } from "antd/lib/button";
|
||||
import React from "react";
|
||||
import { Button } from 'antd';
|
||||
import { ButtonType } from 'antd/lib/button';
|
||||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
viewUrl: string;
|
||||
viewUrlMirrors: string[];
|
||||
}
|
||||
};
|
||||
|
||||
export const SubscribeButtons = (props: Props) =>
|
||||
props.viewUrl ? (
|
||||
<>
|
||||
props.viewUrl
|
||||
? <>
|
||||
<SubscribeButton
|
||||
name={props.name}
|
||||
viewUrl={props.viewUrl}
|
||||
text="Subscribe"
|
||||
/>
|
||||
<MirrorButtons name={props.name} viewUrlMirrors={props.viewUrlMirrors} />
|
||||
text="Subscribe" />
|
||||
<MirrorButtons
|
||||
name={props.name}
|
||||
viewUrlMirrors={props.viewUrlMirrors} />
|
||||
</>
|
||||
) : null;
|
||||
: null;
|
||||
|
||||
interface MirrorButtonsProps {
|
||||
name: string;
|
||||
viewUrlMirrors: string[];
|
||||
}
|
||||
};
|
||||
|
||||
const MirrorButtons = (props: MirrorButtonsProps) => (
|
||||
const MirrorButtons = (props: MirrorButtonsProps) =>
|
||||
<>
|
||||
{props.viewUrlMirrors && props.viewUrlMirrors.length
|
||||
? props.viewUrlMirrors.map((viewUrlMirror: string, i: number) => (
|
||||
<SubscribeButton
|
||||
key={i}
|
||||
name={props.name}
|
||||
viewUrl={viewUrlMirror}
|
||||
text={`Subscribe (Mirror ${i + 1})`}
|
||||
/>
|
||||
))
|
||||
? props.viewUrlMirrors.map((viewUrlMirror: string, i: number) =>
|
||||
<SubscribeButton
|
||||
key={i}
|
||||
name={props.name}
|
||||
viewUrl={viewUrlMirror}
|
||||
text={`Subscribe (Mirror ${i + 1})`} />)
|
||||
: null}
|
||||
</>
|
||||
);
|
||||
</>;
|
||||
|
||||
interface SubscribeButtonProps {
|
||||
name: string;
|
||||
viewUrl: string;
|
||||
text: string;
|
||||
}
|
||||
};
|
||||
|
||||
const SubscribeButton = (props: SubscribeButtonProps) => {
|
||||
const buttonProps = buildButtonProps(props.name, props.viewUrl);
|
||||
|
|
@ -55,8 +53,7 @@ const SubscribeButton = (props: SubscribeButtonProps) => {
|
|||
icon="import"
|
||||
type={buttonProps.type}
|
||||
href={buttonProps.href}
|
||||
title={buttonProps.title}
|
||||
>
|
||||
title={buttonProps.title}>
|
||||
{props.text}
|
||||
</Button>
|
||||
);
|
||||
|
|
@ -77,27 +74,22 @@ const buildButtonProps = (name: string, viewUrl: string) => {
|
|||
if (viewUrl.includes(".onion/")) {
|
||||
type = "dashed";
|
||||
prefixes.push("TOR");
|
||||
}
|
||||
};
|
||||
if (viewUrl.includes("http://")) {
|
||||
type = "danger";
|
||||
prefixes.push("INSECURE");
|
||||
}
|
||||
};
|
||||
|
||||
// Software protocols
|
||||
if (viewUrl.includes(".tpl")) {
|
||||
disabled = true; // IE not supported by FilterLists
|
||||
}
|
||||
if (
|
||||
viewUrl.includes(".lsrules") ||
|
||||
viewUrl.includes("?hostformat=littlesnitch")
|
||||
) {
|
||||
};
|
||||
if (viewUrl.includes(".lsrules") || viewUrl.includes("?hostformat=littlesnitch")) {
|
||||
href = `x-littlesnitch:subscribe-rules?url=${hrefLocation}`;
|
||||
message = `Subscribe to ${name} with Little Snitch's rule group subscription feature.`;
|
||||
}
|
||||
};
|
||||
|
||||
const title = `${
|
||||
prefixes.length ? prefixes.join(" | ") + " | " : ""
|
||||
}${message}`;
|
||||
const title = `${prefixes.length ? prefixes.join(" | ") + " | " : ""}${message}`;
|
||||
|
||||
return { disabled, type, href, title };
|
||||
};
|
||||
};
|
||||
|
|
@ -1,32 +1,32 @@
|
|||
import { Tag } from "antd";
|
||||
import * as React from "react";
|
||||
import { Tag } from 'antd';
|
||||
import * as React from 'react';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
definitionUrl?: string;
|
||||
showLabel?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const SyntaxTag = (props: Props) =>
|
||||
props.name ? (
|
||||
<span>
|
||||
props.name
|
||||
? <span>
|
||||
{props.showLabel && <h3>Syntax:</h3>}
|
||||
<Tag>
|
||||
<TagContents name={props.name} definitionUrl={props.definitionUrl} />
|
||||
<TagContents
|
||||
name={props.name}
|
||||
definitionUrl={props.definitionUrl} />
|
||||
</Tag>
|
||||
</span>
|
||||
) : null;
|
||||
: null;
|
||||
|
||||
const TagContents = (props: Props) =>
|
||||
props.definitionUrl ? (
|
||||
<a
|
||||
href={props.definitionUrl}
|
||||
props.definitionUrl
|
||||
? <a href={props.definitionUrl}
|
||||
title={`View ${props.name}'s homepage.`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
{props.name}
|
||||
</a>
|
||||
) : (
|
||||
<>{props.name}</>
|
||||
);
|
||||
: <>
|
||||
{props.name}
|
||||
</>;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { ListsTable } from "./listsTable";
|
||||
import { ListsTable } from './listsTable';
|
||||
|
||||
export { ListsTable };
|
||||
export { ListsTable };
|
||||
|
|
@ -1,24 +1,19 @@
|
|||
import { Tag } from "antd";
|
||||
import React from "react";
|
||||
import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { Language } from "../../interfaces/Language";
|
||||
import styles from "./LanguageCloud.module.css";
|
||||
import { Language } from '../../interfaces/Language';
|
||||
import styles from './LanguageCloud.module.css';
|
||||
|
||||
interface Props {
|
||||
languages: Language[];
|
||||
showLabel?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const LanguageCloud = (props: Props) =>
|
||||
props.languages && props.languages.length ? (
|
||||
<div className={styles.grow}>
|
||||
{props.showLabel && (
|
||||
<h3>{`Language${props.languages.length > 1 ? "s" : ""}:`}</h3>
|
||||
)}
|
||||
{props.languages.map((l: Language, i: number) => (
|
||||
<Tag key={i} title={l.name}>
|
||||
{l.iso6391}
|
||||
</Tag>
|
||||
))}
|
||||
props.languages && props.languages.length
|
||||
? <div className={styles.grow}>
|
||||
{props.showLabel && <h3>{`Language${props.languages.length > 1 ? "s" : ""}:`}</h3>}
|
||||
{props.languages.map((l: Language, i: number) =>
|
||||
<Tag key={i} title={l.name}>{l.iso6391}</Tag>)}
|
||||
</div>
|
||||
) : null;
|
||||
: null;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { LanguageCloud } from "./LanguageCloud";
|
||||
import { LanguageCloud } from './LanguageCloud';
|
||||
|
||||
export { LanguageCloud };
|
||||
export { LanguageCloud };
|
||||
|
|
@ -1,27 +1,27 @@
|
|||
import "./listInfoDrawer.css";
|
||||
import './listInfoDrawer.css';
|
||||
|
||||
import { Divider, Drawer } from "antd";
|
||||
import ButtonGroup from "antd/lib/button/button-group";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import { Divider, Drawer } from 'antd';
|
||||
import ButtonGroup from 'antd/lib/button/button-group';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import { Language } from "../../interfaces/Language";
|
||||
import { License } from "../../interfaces/License";
|
||||
import { List } from "../../interfaces/List";
|
||||
import { Maintainer } from "../../interfaces/Maintainer";
|
||||
import { Software } from "../../interfaces/Software";
|
||||
import { Syntax } from "../../interfaces/Syntax";
|
||||
import { Tag } from "../../interfaces/Tag";
|
||||
import { Description } from "../Description";
|
||||
import { LanguageCloud } from "../languageCloud";
|
||||
import { LicenseTag } from "../LicenseTag";
|
||||
import { LinkButton } from "../LinkButton";
|
||||
import { Maintainers } from "../maintainers";
|
||||
import { PublishedDate } from "../PublishedDate";
|
||||
import { SoftwareCloud } from "../softwareCloud";
|
||||
import { SubscribeButtons } from "../SubscribeButtons";
|
||||
import { SyntaxTag } from "../SyntaxTag";
|
||||
import { TagCloud } from "../tagCloud";
|
||||
import { Language } from '../../interfaces/Language';
|
||||
import { License } from '../../interfaces/License';
|
||||
import { List } from '../../interfaces/List';
|
||||
import { Maintainer } from '../../interfaces/Maintainer';
|
||||
import { Software } from '../../interfaces/Software';
|
||||
import { Syntax } from '../../interfaces/Syntax';
|
||||
import { Tag } from '../../interfaces/Tag';
|
||||
import { Description } from '../Description';
|
||||
import { LanguageCloud } from '../languageCloud';
|
||||
import { LicenseTag } from '../LicenseTag';
|
||||
import { LinkButton } from '../LinkButton';
|
||||
import { Maintainers } from '../maintainers';
|
||||
import { PublishedDate } from '../PublishedDate';
|
||||
import { SoftwareCloud } from '../softwareCloud';
|
||||
import { SubscribeButtons } from '../SubscribeButtons';
|
||||
import { SyntaxTag } from '../SyntaxTag';
|
||||
import { TagCloud } from '../tagCloud';
|
||||
|
||||
interface Props {
|
||||
list: List;
|
||||
|
|
@ -31,17 +31,14 @@ interface Props {
|
|||
software: Software[];
|
||||
syntax: Syntax | undefined;
|
||||
tags: Tag[];
|
||||
}
|
||||
};
|
||||
|
||||
export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
|
||||
const [originalTitle] = useState<string>(document.title);
|
||||
useEffect(() => {
|
||||
const updateTitle = () =>
|
||||
(document.title = props.list.name + " | " + originalTitle);
|
||||
const updateTitle = () => document.title = props.list.name + " | " + originalTitle;
|
||||
updateTitle();
|
||||
return () => {
|
||||
document.title = originalTitle;
|
||||
};
|
||||
return () => { document.title = originalTitle; };
|
||||
}, [props.list, originalTitle]);
|
||||
return (
|
||||
<Drawer
|
||||
|
|
@ -51,29 +48,29 @@ export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
|
|||
mask={false}
|
||||
title={props.list.name}
|
||||
destroyOnClose={true}
|
||||
onClose={() => props.history.push("/")}
|
||||
>
|
||||
onClose={() => props.history.push("/")}>
|
||||
<Description
|
||||
description={props.list.description}
|
||||
descriptionSourceUrl={props.list.descriptionSourceUrl}
|
||||
/>
|
||||
<LanguageCloud languages={props.languages} showLabel={true} />
|
||||
<TagCloud tags={props.tags} showLabel={true} />
|
||||
{props.license && (
|
||||
descriptionSourceUrl={props.list.descriptionSourceUrl} />
|
||||
<LanguageCloud
|
||||
languages={props.languages}
|
||||
showLabel={true} />
|
||||
<TagCloud
|
||||
tags={props.tags}
|
||||
showLabel={true} />
|
||||
{props.license &&
|
||||
<LicenseTag
|
||||
name={props.license.name}
|
||||
descriptionUrl={props.license.descriptionUrl}
|
||||
showLabel={true}
|
||||
/>
|
||||
)}
|
||||
{props.syntax && (
|
||||
showLabel={true} />}
|
||||
{props.syntax &&
|
||||
<SyntaxTag
|
||||
name={props.syntax.name}
|
||||
definitionUrl={props.syntax.definitionUrl}
|
||||
showLabel={true}
|
||||
/>
|
||||
)}
|
||||
<SoftwareCloud software={props.software} showLabel={true} />
|
||||
showLabel={true} />}
|
||||
<SoftwareCloud
|
||||
software={props.software}
|
||||
showLabel={true} />
|
||||
<PublishedDate publishedDate={props.list.publishedDate} />
|
||||
<Maintainers maintainers={props.maintainers} />
|
||||
<Divider />
|
||||
|
|
@ -81,65 +78,54 @@ export const ListInfoDrawer = (props: RouteComponentProps & Props) => {
|
|||
<SubscribeButtons
|
||||
name={props.list.name}
|
||||
viewUrl={props.list.viewUrl}
|
||||
viewUrlMirrors={props.list.viewUrlMirrors}
|
||||
/>
|
||||
viewUrlMirrors={props.list.viewUrlMirrors} />
|
||||
<LinkButton
|
||||
url={props.list.viewUrl}
|
||||
text="View"
|
||||
title={`View ${props.list.name} in its raw format`}
|
||||
icon="search"
|
||||
/>
|
||||
icon="search" />
|
||||
<LinkButton
|
||||
url={props.list.homeUrl}
|
||||
text="Home"
|
||||
title={`View ${props.list.name}'s homepage.`}
|
||||
icon="home"
|
||||
/>
|
||||
icon="home" />
|
||||
<LinkButton
|
||||
url={props.list.policyUrl}
|
||||
text="Policy"
|
||||
title={`View the types of rules that ${props.list.name} includes.`}
|
||||
icon="file-exclamation"
|
||||
/>
|
||||
{props.list.emailAddress && (
|
||||
icon="file-exclamation" />
|
||||
{props.list.emailAddress &&
|
||||
<LinkButton
|
||||
url={`mailto:${props.list.emailAddress}`}
|
||||
text="Email"
|
||||
title={`Email ${props.list.name}.`}
|
||||
icon="mail"
|
||||
/>
|
||||
)}
|
||||
icon="mail" />}
|
||||
<LinkButton
|
||||
url={props.list.issuesUrl}
|
||||
text="GitHub Issues"
|
||||
title={`View the GitHub Issues for ${props.list.name}.`}
|
||||
icon="github"
|
||||
/>
|
||||
icon="github" />
|
||||
<LinkButton
|
||||
url={props.list.submissionUrl}
|
||||
text="Submit a Rule"
|
||||
title={`Submit a new rule to be included in ${props.list.name}.`}
|
||||
icon="form"
|
||||
/>
|
||||
icon="form" />
|
||||
<LinkButton
|
||||
url={props.list.forumUrl}
|
||||
text="Forum"
|
||||
title={`View the forum for ${props.list.name}.`}
|
||||
icon="team"
|
||||
/>
|
||||
icon="team" />
|
||||
<LinkButton
|
||||
url={props.list.chatUrl}
|
||||
text="Chat"
|
||||
title={`Enter the chat room for ${props.list.name}.`}
|
||||
icon="message"
|
||||
/>
|
||||
icon="message" />
|
||||
<LinkButton
|
||||
url={props.list.donateUrl}
|
||||
text="Donate"
|
||||
title={`Donate to the maintainer of ${props.list.name}.`}
|
||||
icon="dollar"
|
||||
/>
|
||||
icon="dollar" />
|
||||
</ButtonGroup>
|
||||
</Drawer>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { ListInfoDrawer } from "./ListInfoDrawer";
|
||||
import { ListInfoDrawer } from './ListInfoDrawer';
|
||||
|
||||
export { ListInfoDrawer };
|
||||
export { ListInfoDrawer };
|
||||
|
|
@ -1,19 +1,14 @@
|
|||
import React from "react";
|
||||
import {
|
||||
Redirect,
|
||||
Route,
|
||||
RouteComponentProps,
|
||||
StaticContext
|
||||
} from "react-router";
|
||||
import React from 'react';
|
||||
import { Redirect, Route, RouteComponentProps, StaticContext } from 'react-router';
|
||||
|
||||
import { Language } from "../../interfaces/Language";
|
||||
import { License } from "../../interfaces/License";
|
||||
import { List } from "../../interfaces/List";
|
||||
import { Maintainer } from "../../interfaces/Maintainer";
|
||||
import { Software } from "../../interfaces/Software";
|
||||
import { Syntax } from "../../interfaces/Syntax";
|
||||
import { Tag } from "../../interfaces/Tag";
|
||||
import { ListInfoDrawer } from "../listInfoDrawer";
|
||||
import { Language } from '../../interfaces/Language';
|
||||
import { License } from '../../interfaces/License';
|
||||
import { List } from '../../interfaces/List';
|
||||
import { Maintainer } from '../../interfaces/Maintainer';
|
||||
import { Software } from '../../interfaces/Software';
|
||||
import { Syntax } from '../../interfaces/Syntax';
|
||||
import { Tag } from '../../interfaces/Tag';
|
||||
import { ListInfoDrawer } from '../listInfoDrawer';
|
||||
|
||||
interface Props {
|
||||
lists: List[];
|
||||
|
|
@ -23,52 +18,22 @@ interface Props {
|
|||
software: Software[];
|
||||
syntaxes: Syntax[];
|
||||
tags: Tag[];
|
||||
}
|
||||
};
|
||||
|
||||
export const ListDrawer = (props: Props) => {
|
||||
const renderDrawer = (rp: RouteComponentProps<any, StaticContext, any>) => {
|
||||
const list = props.lists.find(l => l.slug === rp.match.params.listSlug);
|
||||
return list ? (
|
||||
<ListInfoDrawer
|
||||
return list
|
||||
? <ListInfoDrawer
|
||||
list={list as List}
|
||||
languages={
|
||||
list.languageIds &&
|
||||
props.languages.filter((l: Language) =>
|
||||
list.languageIds.includes(l.id)
|
||||
)
|
||||
}
|
||||
license={
|
||||
list.licenseId
|
||||
? props.licenses.find((l: License) => list.licenseId === l.id)
|
||||
: props.licenses.find((l: License) => l.id === 5)
|
||||
}
|
||||
maintainers={
|
||||
list.maintainerIds &&
|
||||
props.maintainers.filter((m: Maintainer) =>
|
||||
list.maintainerIds.includes(m.id)
|
||||
)
|
||||
}
|
||||
software={
|
||||
list.syntaxId
|
||||
? props.software.filter((s: Software) =>
|
||||
s.syntaxIds.includes(list.syntaxId)
|
||||
)
|
||||
: []
|
||||
}
|
||||
syntax={
|
||||
list.syntaxId
|
||||
? props.syntaxes.find(s => s.id === list.syntaxId)
|
||||
: undefined
|
||||
}
|
||||
tags={
|
||||
list.tagIds &&
|
||||
props.tags.filter((t: Tag) => list.tagIds.includes(t.id))
|
||||
}
|
||||
{...rp}
|
||||
/>
|
||||
) : props.lists && props.lists.length ? (
|
||||
<Redirect to={{ pathname: "/" }} />
|
||||
) : null;
|
||||
languages={list.languageIds && props.languages.filter((l: Language) => list.languageIds.includes(l.id))}
|
||||
license={list.licenseId ? props.licenses.find((l: License) => list.licenseId === l.id) : props.licenses.find((l: License) => l.id === 5)}
|
||||
maintainers={list.maintainerIds && props.maintainers.filter((m: Maintainer) => list.maintainerIds.includes(m.id))}
|
||||
software={list.syntaxId ? props.software.filter((s: Software) => s.syntaxIds.includes(list.syntaxId)) : []}
|
||||
syntax={list.syntaxId ? props.syntaxes.find(s => s.id === list.syntaxId) : undefined}
|
||||
tags={list.tagIds && props.tags.filter((t: Tag) => list.tagIds.includes(t.id))}
|
||||
{...rp} />
|
||||
: (props.lists && props.lists.length) ? <Redirect to={{ pathname: "/", }} /> : null;
|
||||
};
|
||||
return <Route path="/lists/:listSlug" render={renderDrawer} />;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,33 +1,29 @@
|
|||
import "./listsTable.css";
|
||||
import './listsTable.css';
|
||||
|
||||
import { Table, Tag } from "antd";
|
||||
import {
|
||||
PaginationConfig,
|
||||
SorterResult,
|
||||
TableCurrentDataSource
|
||||
} from "antd/lib/table";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { RouteComponentProps } from "react-router";
|
||||
import { Table, Tag } from 'antd';
|
||||
import { PaginationConfig, SorterResult, TableCurrentDataSource } from 'antd/lib/table';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { RouteComponentProps } from 'react-router';
|
||||
|
||||
import { useSearchColumnFilter, useTablePageSizer } from "../../hooks";
|
||||
import { Language } from "../../interfaces/Language";
|
||||
import { License } from "../../interfaces/License";
|
||||
import { List } from "../../interfaces/List";
|
||||
import { Maintainer } from "../../interfaces/Maintainer";
|
||||
import { Software } from "../../interfaces/Software";
|
||||
import { Syntax } from "../../interfaces/Syntax";
|
||||
import { Tag as TagInterface } from "../../interfaces/Tag";
|
||||
import { nameof } from "../../utils";
|
||||
import { Description } from "../Description";
|
||||
import { LanguageCloud } from "../languageCloud";
|
||||
import { LicenseTag } from "../LicenseTag";
|
||||
import { ListInfoButton } from "../ListInfoButton";
|
||||
import { MaintainerCloud } from "../maintainerCloud";
|
||||
import { SoftwareCloud, SoftwareIcon } from "../softwareCloud";
|
||||
import { SyntaxTag } from "../SyntaxTag";
|
||||
import { TagCloud } from "../tagCloud";
|
||||
import { arraySorter } from "./arraySorter";
|
||||
import styles from "./ListsTable.module.css";
|
||||
import { useSearchColumnFilter, useTablePageSizer } from '../../hooks';
|
||||
import { Language } from '../../interfaces/Language';
|
||||
import { License } from '../../interfaces/License';
|
||||
import { List } from '../../interfaces/List';
|
||||
import { Maintainer } from '../../interfaces/Maintainer';
|
||||
import { Software } from '../../interfaces/Software';
|
||||
import { Syntax } from '../../interfaces/Syntax';
|
||||
import { Tag as TagInterface } from '../../interfaces/Tag';
|
||||
import { nameof } from '../../utils';
|
||||
import { Description } from '../Description';
|
||||
import { LanguageCloud } from '../languageCloud';
|
||||
import { LicenseTag } from '../LicenseTag';
|
||||
import { ListInfoButton } from '../ListInfoButton';
|
||||
import { MaintainerCloud } from '../maintainerCloud';
|
||||
import { SoftwareCloud, SoftwareIcon } from '../softwareCloud';
|
||||
import { SyntaxTag } from '../SyntaxTag';
|
||||
import { TagCloud } from '../tagCloud';
|
||||
import { arraySorter } from './arraySorter';
|
||||
import styles from './ListsTable.module.css';
|
||||
|
||||
interface Props {
|
||||
lists: List[];
|
||||
|
|
@ -37,24 +33,13 @@ interface Props {
|
|||
software: Software[];
|
||||
syntaxes: Syntax[];
|
||||
tags: TagInterface[];
|
||||
}
|
||||
};
|
||||
|
||||
export const ListsTable = (props: RouteComponentProps & Props) => {
|
||||
const {
|
||||
lists,
|
||||
languages,
|
||||
licenses,
|
||||
maintainers,
|
||||
software,
|
||||
syntaxes,
|
||||
tags,
|
||||
...routeComponentProps
|
||||
} = props;
|
||||
const { lists, languages, licenses, maintainers, software, syntaxes, tags, ...routeComponentProps } = props;
|
||||
const tablePageSize = useTablePageSizer();
|
||||
const searchNameColumn = useSearchColumnFilter<List>(nameof<List>("name"));
|
||||
const searchDescriptionColumn = useSearchColumnFilter<List>(
|
||||
nameof<List>("description")
|
||||
);
|
||||
const searchDescriptionColumn = useSearchColumnFilter<List>(nameof<List>("description"));
|
||||
const [visibleLists, setVisibleLists] = useState<List[]>(lists);
|
||||
useEffect(() => {
|
||||
setVisibleLists(lists);
|
||||
|
|
@ -71,13 +56,8 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
pageSize: tablePageSize.pageSize
|
||||
}}
|
||||
scroll={{ x: tablePageSize.isNarrowWindow ? undefined : 1892 }}
|
||||
onChange={(
|
||||
_pagination: PaginationConfig,
|
||||
_filters: Record<keyof List, string[]>,
|
||||
_sorter: SorterResult<List>,
|
||||
extra: TableCurrentDataSource<List>
|
||||
) => setVisibleLists(extra.currentDataSource)}
|
||||
>
|
||||
onChange={(_pagination: PaginationConfig, _filters: Record<keyof List, string[]>, _sorter: SorterResult<List>, extra: TableCurrentDataSource<List>) =>
|
||||
setVisibleLists(extra.currentDataSource)}>
|
||||
<Table.Column<List>
|
||||
title="Info"
|
||||
key="Info"
|
||||
|
|
@ -85,10 +65,8 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
width={tablePageSize.isNarrowWindow ? 43 : undefined}
|
||||
className={styles.nogrow}
|
||||
fixed={tablePageSize.isNarrowWindow ? undefined : "left"}
|
||||
render={(_id: number, list: List) => (
|
||||
<ListInfoButton listName={list.name} {...routeComponentProps} />
|
||||
)}
|
||||
/>
|
||||
render={(_id: number, list: List) =>
|
||||
<ListInfoButton listName={list.name} {...routeComponentProps} />} />
|
||||
<Table.Column<List>
|
||||
title="Name"
|
||||
key="Name"
|
||||
|
|
@ -101,10 +79,11 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
filterDropdown={searchNameColumn.filterDropdown}
|
||||
filterIcon={searchNameColumn.filterIcon}
|
||||
onFilter={searchNameColumn.onFilter}
|
||||
render={(name: string) => <div>{name}</div>}
|
||||
/>
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
render={(name: string) =>
|
||||
<div>{name}</div>} />
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Description"
|
||||
key="Description"
|
||||
dataIndex={nameof<List>("description")}
|
||||
|
|
@ -112,63 +91,38 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
filterDropdown={searchDescriptionColumn.filterDropdown}
|
||||
filterIcon={searchDescriptionColumn.filterIcon}
|
||||
onFilter={searchDescriptionColumn.onFilter}
|
||||
render={(description: string, list: List) => (
|
||||
render={(description: string, list: List) =>
|
||||
<Description
|
||||
description={description}
|
||||
descriptionSourceUrl={list.descriptionSourceUrl}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
descriptionSourceUrl={list.descriptionSourceUrl} />} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Software"
|
||||
key="Software"
|
||||
dataIndex={nameof<List>("syntaxId")}
|
||||
sorter={(a, b) => {
|
||||
const getSoftwareIds = (l: List) =>
|
||||
software
|
||||
.filter((s: Software) => s.syntaxIds.includes(l.syntaxId))
|
||||
.map(s => s.id);
|
||||
const getSoftwareIds = (l: List) => software.filter((s: Software) => s.syntaxIds.includes(l.syntaxId)).map(s => s.id);
|
||||
return arraySorter(getSoftwareIds(a), getSoftwareIds(b), software);
|
||||
}}
|
||||
width={143}
|
||||
className={styles.nogrow}
|
||||
filters={software.map(s => ({
|
||||
text: (
|
||||
<>
|
||||
<SoftwareIcon id={s.id} />
|
||||
|
||||
{s.name} (
|
||||
{
|
||||
visibleLists.filter(
|
||||
l => s.syntaxIds && s.syntaxIds.includes(l.syntaxId)
|
||||
).length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<SoftwareIcon id={s.id} />
|
||||
{s.name}
|
||||
({visibleLists.filter(l => s.syntaxIds && s.syntaxIds.includes(l.syntaxId)).length})
|
||||
</>,
|
||||
value: s.name
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
software
|
||||
.filter((s: Software) => s.name === value)
|
||||
.flatMap(s => s.syntaxIds)
|
||||
.includes(record.syntaxId)
|
||||
}
|
||||
onFilter={(value, record) => software.filter((s: Software) => s.name === value).flatMap(s => s.syntaxIds).includes(record.syntaxId)}
|
||||
render={(syntaxId: number) =>
|
||||
syntaxId ? (
|
||||
<SoftwareCloud
|
||||
software={software.filter((s: Software) =>
|
||||
s.syntaxIds.includes(syntaxId)
|
||||
)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
syntaxId
|
||||
? <SoftwareCloud software={software.filter((s: Software) => s.syntaxIds.includes(syntaxId))} />
|
||||
: null} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Syntax"
|
||||
key="Syntax"
|
||||
dataIndex={nameof<List>("syntaxId")}
|
||||
|
|
@ -184,73 +138,48 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
width={254}
|
||||
className={styles.nogrow}
|
||||
filters={syntaxes.map(s => ({
|
||||
text: (
|
||||
<>
|
||||
<SyntaxTag name={s.name} showLabel={false} />(
|
||||
{
|
||||
visibleLists.filter(l => l.syntaxId && l.syntaxId === s.id)
|
||||
.length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<SyntaxTag name={s.name} showLabel={false} />
|
||||
({visibleLists.filter(l => l.syntaxId && l.syntaxId === s.id).length})
|
||||
</>,
|
||||
value: s.id.toString()
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
record.syntaxId ? record.syntaxId.toString() === value : false
|
||||
}
|
||||
onFilter={(value, record) => record.syntaxId
|
||||
? record.syntaxId.toString() === value
|
||||
: false}
|
||||
render={(syntaxId: number) => {
|
||||
const syntax = syntaxes.find(s => s.id === syntaxId);
|
||||
return syntax ? (
|
||||
<SyntaxTag
|
||||
name={syntax.name}
|
||||
definitionUrl={syntax.definitionUrl}
|
||||
/>
|
||||
) : null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
return syntax
|
||||
? <SyntaxTag name={syntax.name} definitionUrl={syntax.definitionUrl} />
|
||||
: null
|
||||
}} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Languages"
|
||||
key="Languages"
|
||||
dataIndex={nameof<List>("languageIds")}
|
||||
sorter={(a, b) =>
|
||||
arraySorter(a.languageIds, b.languageIds, languages)
|
||||
}
|
||||
sorter={(a, b) => arraySorter(a.languageIds, b.languageIds, languages)}
|
||||
width={129}
|
||||
className={styles.nogrow}
|
||||
filters={languages.map(l => ({
|
||||
text: (
|
||||
<>
|
||||
<Tag title={l.name}>{l.iso6391}</Tag>
|
||||
{l.name} (
|
||||
{
|
||||
visibleLists.filter(
|
||||
li => li.languageIds && li.languageIds.includes(l.id)
|
||||
).length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<Tag title={l.name}>{l.iso6391}</Tag>
|
||||
{l.name}
|
||||
({visibleLists.filter(li => li.languageIds && li.languageIds.includes(l.id)).length})
|
||||
</>,
|
||||
value: l.id.toString()
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
record.languageIds ? record.languageIds.includes(+value) : false
|
||||
}
|
||||
onFilter={(value, record) => record.languageIds
|
||||
? record.languageIds.includes(+value)
|
||||
: false}
|
||||
render={(languageIds: number[]) =>
|
||||
languageIds ? (
|
||||
<LanguageCloud
|
||||
languages={languages.filter((l: Language) =>
|
||||
languageIds.includes(l.id)
|
||||
)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
languageIds
|
||||
? <LanguageCloud languages={languages.filter((l: Language) => languageIds.includes(l.id))} />
|
||||
: null} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Tags"
|
||||
key="Tags"
|
||||
dataIndex={nameof<List>("tagIds")}
|
||||
|
|
@ -258,70 +187,45 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
width={275}
|
||||
className={styles.nogrow}
|
||||
filters={tags.map(t => ({
|
||||
text: (
|
||||
<>
|
||||
<Tag title={t.description}>{t.name}</Tag>(
|
||||
{
|
||||
visibleLists.filter(l => l.tagIds && l.tagIds.includes(t.id))
|
||||
.length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<Tag title={t.description}>{t.name}</Tag>
|
||||
({visibleLists.filter(l => l.tagIds && l.tagIds.includes(t.id)).length})
|
||||
</>,
|
||||
value: t.id.toString()
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
record.tagIds ? record.tagIds.includes(+value) : false
|
||||
}
|
||||
onFilter={(value, record) => record.tagIds
|
||||
? record.tagIds.includes(+value)
|
||||
: false}
|
||||
render={(tagIds: number[]) =>
|
||||
tagIds ? (
|
||||
<TagCloud
|
||||
tags={tags.filter((t: TagInterface) => tagIds.includes(t.id))}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
tagIds
|
||||
? <TagCloud tags={tags.filter((t: TagInterface) => tagIds.includes(t.id))} />
|
||||
: null} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="Maintainers"
|
||||
key="Maintainers"
|
||||
dataIndex={nameof<List>("maintainerIds")}
|
||||
sorter={(a, b) =>
|
||||
arraySorter(a.maintainerIds, b.maintainerIds, maintainers)
|
||||
}
|
||||
sorter={(a, b) => arraySorter(a.maintainerIds, b.maintainerIds, maintainers)}
|
||||
width={191}
|
||||
className={styles.nogrow}
|
||||
filters={maintainers.map(t => ({
|
||||
text: (
|
||||
<>
|
||||
<Tag title={t.name}>{t.name}</Tag>(
|
||||
{
|
||||
visibleLists.filter(
|
||||
l => l.maintainerIds && l.maintainerIds.includes(t.id)
|
||||
).length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<Tag title={t.name}>{t.name}</Tag>
|
||||
({visibleLists.filter(l => l.maintainerIds && l.maintainerIds.includes(t.id)).length})
|
||||
</>,
|
||||
value: t.id.toString()
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
record.maintainerIds ? record.maintainerIds.includes(+value) : false
|
||||
}
|
||||
onFilter={(value, record) => record.maintainerIds
|
||||
? record.maintainerIds.includes(+value)
|
||||
: false}
|
||||
render={(maintainerIds: number[]) =>
|
||||
maintainerIds ? (
|
||||
<MaintainerCloud
|
||||
maintainers={maintainers.filter((t: Maintainer) =>
|
||||
maintainerIds.includes(t.id)
|
||||
)}
|
||||
/>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{tablePageSize.isNarrowWindow ? null : (
|
||||
<Table.Column<List>
|
||||
maintainerIds
|
||||
? <MaintainerCloud maintainers={maintainers.filter((t: Maintainer) => maintainerIds.includes(t.id))} />
|
||||
: null} />}
|
||||
{tablePageSize.isNarrowWindow
|
||||
? null
|
||||
: <Table.Column<List>
|
||||
title="License"
|
||||
key="License"
|
||||
dataIndex={nameof<List>("licenseId")}
|
||||
|
|
@ -337,34 +241,21 @@ export const ListsTable = (props: RouteComponentProps & Props) => {
|
|||
width={215}
|
||||
className={styles.nogrow}
|
||||
filters={licenses.map(l => ({
|
||||
text: (
|
||||
<>
|
||||
<LicenseTag name={l.name} showLabel={false} />(
|
||||
{
|
||||
visibleLists.filter(
|
||||
li => li.licenseId && li.licenseId === l.id
|
||||
).length
|
||||
}
|
||||
)
|
||||
</>
|
||||
),
|
||||
text: <>
|
||||
<LicenseTag name={l.name} showLabel={false} />
|
||||
({visibleLists.filter(li => li.licenseId && li.licenseId === l.id).length})
|
||||
</>,
|
||||
value: l.id.toString()
|
||||
}))}
|
||||
onFilter={(value, record) =>
|
||||
record.licenseId ? record.licenseId.toString() === value : false
|
||||
}
|
||||
onFilter={(value, record) => record.licenseId
|
||||
? record.licenseId.toString() === value
|
||||
: false}
|
||||
render={(licenseId: number) => {
|
||||
const license = licenses.find(l => l.id === licenseId);
|
||||
return license ? (
|
||||
<LicenseTag
|
||||
name={license.name}
|
||||
descriptionUrl={license.descriptionUrl}
|
||||
showLabel={false}
|
||||
/>
|
||||
) : null;
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
return license
|
||||
? <LicenseTag name={license.name} descriptionUrl={license.descriptionUrl} showLabel={false} />
|
||||
: null
|
||||
}} />}
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,17 +1,9 @@
|
|||
import React from "react";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import React from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
useLanguages,
|
||||
useLicenses,
|
||||
useLists,
|
||||
useMaintainers,
|
||||
useSoftware,
|
||||
useSyntaxes,
|
||||
useTags
|
||||
} from "../../hooks";
|
||||
import { ListDrawer } from "./ListDrawer";
|
||||
import { ListsTable } from "./ListsTable";
|
||||
import { useLanguages, useLicenses, useLists, useMaintainers, useSoftware, useSyntaxes, useTags } from '../../hooks';
|
||||
import { ListDrawer } from './ListDrawer';
|
||||
import { ListsTable } from './ListsTable';
|
||||
|
||||
export const ListsTableHoc = (props: RouteComponentProps) => {
|
||||
const lists = useLists();
|
||||
|
|
@ -31,8 +23,7 @@ export const ListsTableHoc = (props: RouteComponentProps) => {
|
|||
software={software}
|
||||
syntaxes={syntaxes}
|
||||
tags={tags}
|
||||
{...props}
|
||||
/>
|
||||
{...props} />
|
||||
<ListDrawer
|
||||
lists={lists}
|
||||
languages={languages}
|
||||
|
|
@ -40,8 +31,7 @@ export const ListsTableHoc = (props: RouteComponentProps) => {
|
|||
maintainers={maintainers}
|
||||
software={software}
|
||||
syntaxes={syntaxes}
|
||||
tags={tags}
|
||||
/>
|
||||
tags={tags} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
@ -1,30 +1,18 @@
|
|||
interface ArraySortableEntity {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
};
|
||||
|
||||
export const arraySorter = (
|
||||
a: number[],
|
||||
b: number[],
|
||||
entities: ArraySortableEntity[]
|
||||
) =>
|
||||
export const arraySorter = (a: number[], b: number[], entities: ArraySortableEntity[]) =>
|
||||
a && a.length
|
||||
? b && b.length
|
||||
? a.length === b.length
|
||||
? entities
|
||||
.filter((e: ArraySortableEntity) => a.includes(e.id))
|
||||
.map((e: ArraySortableEntity) => e.name)
|
||||
.join()
|
||||
.toLowerCase() >
|
||||
entities
|
||||
.filter((e: ArraySortableEntity) => b.includes(e.id))
|
||||
.map((e: ArraySortableEntity) => e.name)
|
||||
.join()
|
||||
.toLowerCase()
|
||||
? entities.filter((e: ArraySortableEntity) => a.includes(e.id)).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
|
||||
> entities.filter((e: ArraySortableEntity) => b.includes(e.id)).map((e: ArraySortableEntity) => e.name).join().toLowerCase()
|
||||
? 1
|
||||
: -1
|
||||
: a.length > b.length
|
||||
? -1
|
||||
: 1
|
||||
? -1
|
||||
: 1
|
||||
: -1
|
||||
: 1;
|
||||
: 1;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { ListsTableHoc } from "./ListsTableHoc";
|
||||
import { ListsTableHoc } from './ListsTableHoc';
|
||||
|
||||
export { ListsTableHoc as ListsTable };
|
||||
export { ListsTableHoc as ListsTable };
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
.ant-table-header-column > div {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,26 @@
|
|||
import { Tag } from "antd";
|
||||
import React from "react";
|
||||
import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { Maintainer } from "../../interfaces/Maintainer";
|
||||
import styles from "./MaintainerCloud.module.css";
|
||||
import { Maintainer } from '../../interfaces/Maintainer';
|
||||
import styles from './MaintainerCloud.module.css';
|
||||
|
||||
interface Props {
|
||||
maintainers: Maintainer[];
|
||||
}
|
||||
};
|
||||
|
||||
export const MaintainerCloud = (props: Props) =>
|
||||
props.maintainers && props.maintainers.length ? (
|
||||
<div className={styles.grow}>
|
||||
props.maintainers && props.maintainers.length
|
||||
? <div className={styles.grow}>
|
||||
{props.maintainers.map((m: Maintainer, i: number) =>
|
||||
m.homeUrl ? (
|
||||
<Tag key={i}>
|
||||
<a
|
||||
href={m.homeUrl}
|
||||
m.homeUrl
|
||||
? <Tag key={i}>
|
||||
<a href={m.homeUrl}
|
||||
title={`View ${m.name}'s homepage.`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
{m.name}
|
||||
</a>
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag key={i}>{m.name}</Tag>
|
||||
)
|
||||
)}
|
||||
: <Tag key={i}>{m.name}</Tag>)}
|
||||
</div>
|
||||
) : null;
|
||||
: null;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { MaintainerCloud } from "./MaintainerCloud";
|
||||
import { MaintainerCloud } from './MaintainerCloud';
|
||||
|
||||
export { MaintainerCloud };
|
||||
export { MaintainerCloud };
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
import "./maintainers.css";
|
||||
import './maintainers.css';
|
||||
|
||||
import { Card, Icon } from "antd";
|
||||
import React from "react";
|
||||
import { Card, Icon } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { Maintainer } from "../../interfaces/Maintainer";
|
||||
import { Maintainer } from '../../interfaces/Maintainer';
|
||||
|
||||
interface Props {
|
||||
maintainers: Maintainer[];
|
||||
}
|
||||
};
|
||||
|
||||
export const Maintainers = (props: Props) =>
|
||||
props.maintainers && props.maintainers.length ? (
|
||||
<>
|
||||
props.maintainers && props.maintainers.length
|
||||
? <>
|
||||
<h3>Maintainer{props.maintainers.length > 1 ? "s" : ""}:</h3>
|
||||
{props.maintainers.map((m: Maintainer, index: number) => (
|
||||
<MaintainerComponent key={index} maintainer={m} />
|
||||
))}
|
||||
{props.maintainers.map((m: Maintainer, index: number) =>
|
||||
<MaintainerComponent key={index} maintainer={m} />)}
|
||||
</>
|
||||
) : null;
|
||||
: null;
|
||||
|
||||
interface MaintainerComponentProps {
|
||||
maintainer: Maintainer;
|
||||
|
|
@ -27,39 +26,35 @@ const MaintainerComponent = (props: MaintainerComponentProps) => {
|
|||
let actions = [];
|
||||
if (props.maintainer.homeUrl) {
|
||||
actions.push(
|
||||
<a
|
||||
href={props.maintainer.homeUrl}
|
||||
<a href={props.maintainer.homeUrl}
|
||||
title={`View ${props.maintainer.name}'s homepage.`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="home" key="home" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
if (props.maintainer.emailAddress) {
|
||||
actions.push(
|
||||
<a
|
||||
href={`mailto:${props.maintainer.emailAddress}`}
|
||||
<a href={`mailto:${props.maintainer.emailAddress}`}
|
||||
title={`Email ${props.maintainer.name}.`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="mail" key="mail" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
if (props.maintainer.twitterHandle) {
|
||||
actions.push(
|
||||
<a
|
||||
href={`https://twitter.com/${props.maintainer.twitterHandle}`}
|
||||
<a href={`https://twitter.com/${props.maintainer.twitterHandle}`}
|
||||
title={`${props.maintainer.twitterHandle} on Twitter`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
rel="noopener noreferrer">
|
||||
<Icon type="twitter" key="twitter" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return <Card title={props.maintainer.name} actions={actions} />;
|
||||
};
|
||||
return <Card
|
||||
title={props.maintainer.name}
|
||||
actions={actions} />;
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { Maintainers } from "./Maintainers";
|
||||
import { Maintainers } from './Maintainers';
|
||||
|
||||
export { Maintainers };
|
||||
export { Maintainers };
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
import React from "react";
|
||||
import React from 'react';
|
||||
|
||||
import { Software } from "../../interfaces/Software";
|
||||
import styles from "./SoftwareCloud.module.css";
|
||||
import { SoftwareIcon } from "./SoftwareIcon";
|
||||
import { Software } from '../../interfaces/Software';
|
||||
import styles from './SoftwareCloud.module.css';
|
||||
import { SoftwareIcon } from './SoftwareIcon';
|
||||
|
||||
interface Props {
|
||||
software: Software[];
|
||||
showLabel?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const SoftwareCloud = (props: Props) =>
|
||||
props.software && props.software.length ? (
|
||||
<div className={styles.grow}>
|
||||
props.software && props.software.length
|
||||
? <div className={styles.grow}>
|
||||
{props.showLabel && <h3>{`Software:`}</h3>}
|
||||
{props.software.map((s: Software, i: number) =>
|
||||
s.homeUrl ? (
|
||||
<a key={i} href={s.homeUrl} target="_blank" rel="noopener noreferrer">
|
||||
s.homeUrl
|
||||
? <a key={i}
|
||||
href={s.homeUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
<SoftwareIcon id={s.id} />
|
||||
</a>
|
||||
) : (
|
||||
<SoftwareIcon key={i} id={s.id} />
|
||||
)
|
||||
)}
|
||||
: <SoftwareIcon key={i} id={s.id} />)}
|
||||
</div>
|
||||
) : null;
|
||||
: null;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import * as React from "react";
|
||||
import * as React from 'react';
|
||||
|
||||
import {
|
||||
img01,
|
||||
|
|
@ -37,28 +37,27 @@ import {
|
|||
img35,
|
||||
img36,
|
||||
img37
|
||||
} from "./imgs";
|
||||
} from './imgs';
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
}
|
||||
};
|
||||
|
||||
export const SoftwareIcon = (props: Props) =>
|
||||
icons[props.id] ? (
|
||||
<img
|
||||
icons[props.id]
|
||||
? <img
|
||||
src={icons[props.id].image}
|
||||
height="20"
|
||||
alt={icons[props.id].imageTitle}
|
||||
title={icons[props.id].imageTitle}
|
||||
/>
|
||||
) : null;
|
||||
title={icons[props.id].imageTitle} />
|
||||
: null;
|
||||
|
||||
interface Icon {
|
||||
image: any;
|
||||
imageTitle: string;
|
||||
}
|
||||
};
|
||||
|
||||
const icons: { [id: number]: Icon } = {
|
||||
const icons: { [id: number]: Icon; } = {
|
||||
1: { image: img01, imageTitle: "uBlock Origin" },
|
||||
2: { image: img02, imageTitle: "Adblock Plus" },
|
||||
3: { image: img03, imageTitle: "AdGuard" },
|
||||
|
|
@ -95,4 +94,4 @@ const icons: { [id: number]: Icon } = {
|
|||
35: { image: img35, imageTitle: "Shadowsocks" },
|
||||
36: { image: img36, imageTitle: "ShadowsocksR" },
|
||||
37: { image: img37, imageTitle: "Shadowrocket" }
|
||||
};
|
||||
};
|
||||
|
|
@ -6,4 +6,4 @@
|
|||
declare module "*.png" {
|
||||
const content: any;
|
||||
export default content;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +1,39 @@
|
|||
import img01 from "./01-uBlock-Origin.svg";
|
||||
import img02 from "./02-Adblock-Plus.svg";
|
||||
import img03 from "./03-AdGuard.svg";
|
||||
import img04 from "./04-DNS66.png";
|
||||
import img05 from "./05-Nano-Adblocker.png";
|
||||
import img06 from "./06-AdBlock.png";
|
||||
import img07 from "./07-AdAway.png";
|
||||
import img08 from "./08-Personal-Blocklist.png";
|
||||
import img10 from "./10-Redirector.png";
|
||||
import img11 from "./11-Hosts-File-Editor.png";
|
||||
import img12 from "./12-Gas-Mask.png";
|
||||
import img13 from "./13-MinerBlock.svg";
|
||||
import img14 from "./14-Pi-hole.svg";
|
||||
import img15 from "./15-uBlock.svg";
|
||||
import img16 from "./16-Internet-Explorer-TPL.svg";
|
||||
import img17 from "./17-Google-Hit-Hider-by-Domain.png";
|
||||
import img18 from "./18-FireHOL.png";
|
||||
import img19 from "./19-Samsung-Knox.png";
|
||||
import img20 from "./20-Little-Snitch.png";
|
||||
import img21 from "./21-Privoxy.png";
|
||||
import img22 from "./22-Diversion.png";
|
||||
import img23 from "./23-dnsmasq.png";
|
||||
import img24 from "./24-Slimjet.png";
|
||||
import img25 from "./25-uMatrix.png";
|
||||
import img26 from "./26-Blokada.png";
|
||||
import img27 from "./27-hostsmgr.png";
|
||||
import img28 from "./28-personalDNSfilter.svg";
|
||||
import img29 from "./29-Unbound.png";
|
||||
import img30 from "./30-BIND.png";
|
||||
import img31 from "./31-AdGuard-Home.png";
|
||||
import img32 from "./32-AdNauseam.png";
|
||||
import img33 from "./33-Legacy-Unix-Derivatives.png";
|
||||
import img34 from "./34-Windows-command-line.png";
|
||||
import img35 from "./35-Shadowsocks.png";
|
||||
import img36 from "./36-ShadowsocksR.png";
|
||||
import img37 from "./37-Shadowrocket.png";
|
||||
import img01 from './01-uBlock-Origin.svg';
|
||||
import img02 from './02-Adblock-Plus.svg';
|
||||
import img03 from './03-AdGuard.svg';
|
||||
import img04 from './04-DNS66.png';
|
||||
import img05 from './05-Nano-Adblocker.png';
|
||||
import img06 from './06-AdBlock.png';
|
||||
import img07 from './07-AdAway.png';
|
||||
import img08 from './08-Personal-Blocklist.png';
|
||||
import img10 from './10-Redirector.png';
|
||||
import img11 from './11-Hosts-File-Editor.png';
|
||||
import img12 from './12-Gas-Mask.png';
|
||||
import img13 from './13-MinerBlock.svg';
|
||||
import img14 from './14-Pi-hole.svg';
|
||||
import img15 from './15-uBlock.svg';
|
||||
import img16 from './16-Internet-Explorer-TPL.svg';
|
||||
import img17 from './17-Google-Hit-Hider-by-Domain.png';
|
||||
import img18 from './18-FireHOL.png';
|
||||
import img19 from './19-Samsung-Knox.png';
|
||||
import img20 from './20-Little-Snitch.png';
|
||||
import img21 from './21-Privoxy.png';
|
||||
import img22 from './22-Diversion.png';
|
||||
import img23 from './23-dnsmasq.png';
|
||||
import img24 from './24-Slimjet.png';
|
||||
import img25 from './25-uMatrix.png';
|
||||
import img26 from './26-Blokada.png';
|
||||
import img27 from './27-hostsmgr.png';
|
||||
import img28 from './28-personalDNSfilter.svg';
|
||||
import img29 from './29-Unbound.png';
|
||||
import img30 from './30-BIND.png';
|
||||
import img31 from './31-AdGuard-Home.png';
|
||||
import img32 from './32-AdNauseam.png';
|
||||
import img33 from './33-Legacy-Unix-Derivatives.png';
|
||||
import img34 from './34-Windows-command-line.png';
|
||||
import img35 from './35-Shadowsocks.png';
|
||||
import img36 from './36-ShadowsocksR.png';
|
||||
import img37 from './37-Shadowrocket.png';
|
||||
|
||||
export {
|
||||
img01,
|
||||
|
|
@ -72,4 +72,4 @@ export {
|
|||
img35,
|
||||
img36,
|
||||
img37
|
||||
};
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { SoftwareCloud } from "./SoftwareCloud";
|
||||
import { SoftwareIcon } from "./SoftwareIcon";
|
||||
import { SoftwareCloud } from './SoftwareCloud';
|
||||
import { SoftwareIcon } from './SoftwareIcon';
|
||||
|
||||
export { SoftwareCloud, SoftwareIcon };
|
||||
export { SoftwareCloud, SoftwareIcon };
|
||||
|
|
@ -1,22 +1,19 @@
|
|||
import { Tag } from "antd";
|
||||
import React from "react";
|
||||
import { Tag } from 'antd';
|
||||
import React from 'react';
|
||||
|
||||
import { Tag as TagInterface } from "../../interfaces/Tag";
|
||||
import styles from "./TagCloud.module.css";
|
||||
import { Tag as TagInterface } from '../../interfaces/Tag';
|
||||
import styles from './TagCloud.module.css';
|
||||
|
||||
interface Props {
|
||||
tags: TagInterface[];
|
||||
showLabel?: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const TagCloud = (props: Props) =>
|
||||
props.tags && props.tags.length ? (
|
||||
<div className={styles.grow}>
|
||||
props.tags && props.tags.length
|
||||
? <div className={styles.grow}>
|
||||
{props.showLabel && <h3>{`Tag${props.tags.length > 1 ? "s" : ""}:`}</h3>}
|
||||
{props.tags.map((t: TagInterface, i: number) => (
|
||||
<Tag key={i} title={t.description}>
|
||||
{t.name}
|
||||
</Tag>
|
||||
))}
|
||||
{props.tags.map((t: TagInterface, i: number) =>
|
||||
<Tag key={i} title={t.description}>{t.name}</Tag>)}
|
||||
</div>
|
||||
) : null;
|
||||
: null;
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { TagCloud } from "./TagCloud";
|
||||
import { TagCloud } from './TagCloud';
|
||||
|
||||
export { TagCloud };
|
||||
export { TagCloud };
|
||||
|
|
@ -1 +1 @@
|
|||
export const SlugifyOptions = { remove: /[^a-zA-Z0-9- ]/g, lower: true };
|
||||
export const SlugifyOptions = { remove: /[^a-zA-Z0-9- ]/g, lower: true }
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
import { useLanguages } from "./useLanguages";
|
||||
import { useLicenses } from "./useLicenses";
|
||||
import { useLists } from "./useLists";
|
||||
import { useMaintainers } from "./useMaintainers";
|
||||
import { useSearchColumnFilter } from "./useSearchColumnFilter";
|
||||
import { useSoftware } from "./useSoftware";
|
||||
import { useSyntaxes } from "./useSyntaxes";
|
||||
import { useTablePageSizer } from "./useTablePageSizer";
|
||||
import { useTags } from "./useTags";
|
||||
import { useLanguages } from './useLanguages';
|
||||
import { useLicenses } from './useLicenses';
|
||||
import { useLists } from './useLists';
|
||||
import { useMaintainers } from './useMaintainers';
|
||||
import { useSearchColumnFilter } from './useSearchColumnFilter';
|
||||
import { useSoftware } from './useSoftware';
|
||||
import { useSyntaxes } from './useSyntaxes';
|
||||
import { useTablePageSizer } from './useTablePageSizer';
|
||||
import { useTags } from './useTags';
|
||||
|
||||
export {
|
||||
useLanguages,
|
||||
|
|
@ -18,4 +18,4 @@ export {
|
|||
useSyntaxes,
|
||||
useTablePageSizer,
|
||||
useTags
|
||||
};
|
||||
};
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const useApiData = <T extends {}>(url: string) => {
|
||||
const [data, setData] = useState<T>();
|
||||
useEffect(() => {
|
||||
const fetchData = async () =>
|
||||
(await fetch(url)).json().then(r => setData(r));
|
||||
const fetchData = async () => (await fetch(url)).json().then(r => setData(r));
|
||||
fetchData();
|
||||
}, [url]);
|
||||
return data;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { Language } from "../interfaces/Language";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { Language } from '../interfaces/Language';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useLanguages = () =>
|
||||
(useApiData<Language[]>("/api/v1/languages") || []).sort(
|
||||
(a: Language, b: Language) => a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useLanguages = () => (useApiData<Language[]>("/api/v1/languages") || [])
|
||||
.sort((a: Language, b: Language) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { License } from "../interfaces/License";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { License } from '../interfaces/License';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useLicenses = () =>
|
||||
(useApiData<License[]>("/api/v1/licenses") || []).sort(
|
||||
(a: License, b: License) => a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useLicenses = () => (useApiData<License[]>("/api/v1/licenses") || [])
|
||||
.sort((a: License, b: License) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,11 +1,12 @@
|
|||
import slugify from "slugify";
|
||||
import slugify from 'slugify';
|
||||
|
||||
import { SlugifyOptions } from "../constants";
|
||||
import { List } from "../interfaces/List";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { SlugifyOptions } from '../constants';
|
||||
import { List } from '../interfaces/List';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useLists = () => {
|
||||
const lists = useApiData<List[]>("/api/v1/lists");
|
||||
lists && lists.forEach(l => (l.slug = slugify(l.name, SlugifyOptions)));
|
||||
return (lists || []).sort((a: List, b: List) => a.name.localeCompare(b.name));
|
||||
};
|
||||
lists && lists.forEach(l => l.slug = slugify(l.name, SlugifyOptions));
|
||||
return (lists || [])
|
||||
.sort((a: List, b: List) => a.name.localeCompare(b.name));
|
||||
};
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { Maintainer } from "../interfaces/Maintainer";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { Maintainer } from '../interfaces/Maintainer';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useMaintainers = () =>
|
||||
(useApiData<Maintainer[]>("/api/v1/maintainers") || []).sort(
|
||||
(a: Maintainer, b: Maintainer) => a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useMaintainers = () => (useApiData<Maintainer[]>("/api/v1/maintainers") || [])
|
||||
.sort((a: Maintainer, b: Maintainer) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
import { Button, Icon, Input } from "antd";
|
||||
import { FilterDropdownProps } from "antd/lib/table";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Button, Icon, Input } from 'antd';
|
||||
import { FilterDropdownProps } from 'antd/lib/table';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
interface FilterPropsState<T> {
|
||||
filterDropdown?:
|
||||
| React.ReactNode
|
||||
| ((props: FilterDropdownProps) => React.ReactNode);
|
||||
filterDropdown?: React.ReactNode | ((props: FilterDropdownProps) => React.ReactNode);
|
||||
filterIcon?: React.ReactNode | ((filtered: boolean) => React.ReactNode);
|
||||
onFilter?: (value: any, record: T) => boolean;
|
||||
}
|
||||
|
|
@ -24,59 +22,36 @@ export const useSearchColumnFilter = <T extends {}>(dataIndex: string) => {
|
|||
clearFilters && clearFilters([]);
|
||||
};
|
||||
setFilterProps({
|
||||
filterDropdown: ({
|
||||
setSelectedKeys,
|
||||
selectedKeys,
|
||||
confirm,
|
||||
clearFilters
|
||||
}) => (
|
||||
filterDropdown: ({ setSelectedKeys, selectedKeys, confirm, clearFilters }) => (
|
||||
<div style={{ padding: 8 }}>
|
||||
<Input
|
||||
placeholder={`Search ${dataIndex.charAt(0).toUpperCase() +
|
||||
dataIndex.slice(1)}`}
|
||||
placeholder={`Search ${dataIndex.charAt(0).toUpperCase() + dataIndex.slice(1)}`}
|
||||
value={selectedKeys && selectedKeys[0]}
|
||||
onChange={e =>
|
||||
setSelectedKeys &&
|
||||
setSelectedKeys(e.target.value ? [e.target.value] : [])
|
||||
}
|
||||
onChange={e => setSelectedKeys && setSelectedKeys(e.target.value ? [e.target.value] : [])}
|
||||
onPressEnter={() => handleSearch(confirm)}
|
||||
style={{ width: 188, marginBottom: 8, display: "block" }}
|
||||
/>
|
||||
style={{ width: 188, marginBottom: 8, display: 'block' }} />
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => handleSearch(confirm)}
|
||||
icon="search"
|
||||
size="small"
|
||||
style={{ width: 90, marginRight: 8 }}
|
||||
>
|
||||
style={{ width: 90, marginRight: 8 }} >
|
||||
Search
|
||||
</Button>
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleReset(clearFilters)}
|
||||
size="small"
|
||||
style={{ width: 90 }}
|
||||
>
|
||||
style={{ width: 90 }} >
|
||||
Reset
|
||||
</Button>
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
filterIcon: filtered => (
|
||||
<Icon
|
||||
type="search"
|
||||
style={{ color: filtered ? "#1890ff" : undefined }}
|
||||
/>
|
||||
),
|
||||
filterIcon: filtered => <Icon type="search" style={{ color: filtered ? '#1890ff' : undefined }} />,
|
||||
onFilter: (value, record) => {
|
||||
const searchValue = (record as any)[dataIndex];
|
||||
return (
|
||||
searchValue &&
|
||||
searchValue
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toString().toLowerCase())
|
||||
);
|
||||
return searchValue && searchValue.toString().toLowerCase().includes(value.toString().toLowerCase())
|
||||
}
|
||||
});
|
||||
})
|
||||
}, [dataIndex]);
|
||||
return filterProps;
|
||||
};
|
||||
};
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { Software } from "../interfaces/Software";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { Software } from '../interfaces/Software';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useSoftware = () =>
|
||||
(useApiData<Software[]>("/api/v1/software") || []).sort(
|
||||
(a: Software, b: Software) => a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useSoftware = () => (useApiData<Software[]>("/api/v1/software") || [])
|
||||
.sort((a: Software, b: Software) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { Syntax } from "../interfaces/Syntax";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { Syntax } from '../interfaces/Syntax';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useSyntaxes = () =>
|
||||
(useApiData<Syntax[]>("/api/v1/syntaxes") || []).sort(
|
||||
(a: Syntax, b: Syntax) => a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useSyntaxes = () => (useApiData<Syntax[]>("/api/v1/syntaxes") || [])
|
||||
.sort((a: Syntax, b: Syntax) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,16 +1,16 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface TablePageSize {
|
||||
pageSize: number;
|
||||
isNarrowWindow: boolean;
|
||||
}
|
||||
};
|
||||
|
||||
export const useTablePageSizer = () => {
|
||||
const [state, setState] = useState<TablePageSize>(calculateSize());
|
||||
useEffect(() => {
|
||||
const updatePageSize = () => setState(calculateSize());
|
||||
window.addEventListener("resize", updatePageSize);
|
||||
return () => window.removeEventListener("resize", updatePageSize);
|
||||
window.addEventListener('resize', updatePageSize);
|
||||
return () => window.removeEventListener('resize', updatePageSize);
|
||||
}, []);
|
||||
return state;
|
||||
};
|
||||
|
|
@ -18,4 +18,4 @@ export const useTablePageSizer = () => {
|
|||
const calculateSize = () => ({
|
||||
pageSize: Math.floor((window.innerHeight - 184) / 57),
|
||||
isNarrowWindow: window.innerWidth < 576 ? true : false
|
||||
});
|
||||
});
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
import { Tag } from "../interfaces/Tag";
|
||||
import { useApiData } from "./useApiData";
|
||||
import { Tag } from '../interfaces/Tag';
|
||||
import { useApiData } from './useApiData';
|
||||
|
||||
export const useTags = () =>
|
||||
(useApiData<Tag[]>("/api/v1/tags") || []).sort((a: Tag, b: Tag) =>
|
||||
a.name.localeCompare(b.name)
|
||||
);
|
||||
export const useTags = () => (useApiData<Tag[]>("/api/v1/tags") || [])
|
||||
.sort((a: Tag, b: Tag) => a.name.localeCompare(b.name));
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import "./index.css";
|
||||
import './index.css';
|
||||
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { App } from "./App";
|
||||
import { App } from './App';
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"));
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
|
|
@ -3,4 +3,4 @@ export interface Language {
|
|||
filterListIds: number[];
|
||||
iso6391: string;
|
||||
name: string;
|
||||
}
|
||||
};
|
||||
|
|
@ -3,4 +3,4 @@ export interface License {
|
|||
descriptionUrl: string;
|
||||
filterListIds: number[];
|
||||
name: string;
|
||||
}
|
||||
};
|
||||
|
|
@ -22,4 +22,4 @@
|
|||
|
||||
// auto-generated by useLists hook
|
||||
slug: string;
|
||||
}
|
||||
};
|
||||
|
|
@ -5,4 +5,4 @@ export interface Maintainer {
|
|||
homeUrl: string;
|
||||
name: string;
|
||||
twitterHandle: string;
|
||||
}
|
||||
};
|
||||
|
|
@ -4,4 +4,4 @@ export interface Software {
|
|||
isAbpSubscribable: boolean;
|
||||
name: string;
|
||||
syntaxIds: number[];
|
||||
}
|
||||
};
|
||||
|
|
@ -4,4 +4,4 @@ export interface Syntax {
|
|||
filterListIds: number[];
|
||||
name: string;
|
||||
softwareIds: number[];
|
||||
}
|
||||
};
|
||||
|
|
@ -3,4 +3,4 @@ export interface Tag {
|
|||
description: string;
|
||||
filterListIds: number[];
|
||||
name: string;
|
||||
}
|
||||
};
|
||||
2
src/FilterLists.Web/src/react-app-env.d.ts
vendored
2
src/FilterLists.Web/src/react-app-env.d.ts
vendored
|
|
@ -1 +1 @@
|
|||
/// <reference types="react-scripts" />
|
||||
/// <reference types="react-scripts" />
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
import { nameof } from "./nameof";
|
||||
import { nameof } from './nameof';
|
||||
|
||||
export { nameof };
|
||||
export { nameof };
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
//https://stackoverflow.com/a/50470026/2343739
|
||||
export const nameof = <T>(name: Extract<keyof T, string>) => name;
|
||||
export const nameof = <T>(name: Extract<keyof T, string>) => name;
|
||||
Loading…
Reference in a new issue