|
1 | | -import React, { Component, Fragment, ReactElement } from 'react'; |
2 | | -import { withRouter, RouteComponentProps } from 'react-router-dom'; |
| 1 | +import React, { useContext } from 'react'; |
| 2 | +import { useHistory } from 'react-router-dom'; |
3 | 3 | import CardContent from '@material-ui/core/CardContent'; |
4 | 4 |
|
5 | | -import { DetailContextConsumer, VersionPageConsumerProps } from '../../pages/Version'; |
| 5 | +import { PackageDependencies } from '../../../types/packageMeta'; |
| 6 | +import { DetailContext } from '../../pages/Version'; |
6 | 7 | import NoItems from '../NoItems'; |
7 | 8 |
|
8 | 9 | import { CardWrap, Heading, Tags, Tag } from './styles'; |
9 | 10 |
|
10 | | -type DepDetailProps = { |
11 | | - name: string; |
12 | | - version: string; |
13 | | - onLoading?: () => void; |
14 | | -} & RouteComponentProps; |
15 | | - |
16 | | -interface DepDetailState { |
17 | | - name: string; |
18 | | - version: string; |
| 11 | +interface DependencyBlockProps { |
| 12 | + title: string; |
| 13 | + dependencies: PackageDependencies; |
19 | 14 | } |
20 | 15 |
|
21 | | -class DepDetail extends Component<DepDetailProps, DepDetailState> { |
22 | | - constructor(props: DepDetailProps) { |
23 | | - super(props); |
24 | | - const { name, version } = this.props; |
| 16 | +const DependencyBlock: React.FC<DependencyBlockProps> = ({ title, dependencies }) => { |
| 17 | + const { enableLoading } = useContext(DetailContext); |
| 18 | + const history = useHistory(); |
25 | 19 |
|
26 | | - this.state = { |
27 | | - name, |
28 | | - version, |
29 | | - }; |
30 | | - } |
| 20 | + const deps = Object.entries(dependencies); |
31 | 21 |
|
32 | | - public render(): ReactElement<HTMLElement> { |
33 | | - const { name, version } = this.state; |
34 | | - const tagText = `${name}@${version}`; |
35 | | - return <Tag className={'dep-tag'} clickable={true} label={tagText} onClick={this.handleOnClick} />; |
36 | | - } |
| 22 | + function handleClick(name: string): void { |
| 23 | + enableLoading && enableLoading(); |
37 | 24 |
|
38 | | - private handleOnClick = () => { |
39 | | - const { name } = this.state; |
40 | | - const { onLoading, history } = this.props; |
41 | | - |
42 | | - onLoading && onLoading(); |
43 | 25 | history.push(`/-/web/detail/${name}`); |
44 | | - }; |
45 | | -} |
| 26 | + } |
46 | 27 |
|
47 | | -const WrapperDependencyDetail = withRouter(DepDetail); |
| 28 | + return ( |
| 29 | + <CardWrap> |
| 30 | + <CardContent> |
| 31 | + <Heading variant="subtitle1">{`${title} (${deps.length})`}</Heading> |
| 32 | + <Tags> |
| 33 | + {deps.map(([name, version]) => ( |
| 34 | + // eslint-disable-next-line |
| 35 | + <Tag className={'dep-tag'} clickable={true} key={name} label={`${name}@${version}`} onClick={() => handleClick(name)} /> |
| 36 | + ))} |
| 37 | + </Tags> |
| 38 | + </CardContent> |
| 39 | + </CardWrap> |
| 40 | + ); |
| 41 | +}; |
| 42 | + |
| 43 | +function hasKeys(object?: { [key: string]: any }): boolean { |
| 44 | + return !!object && Object.keys(object).length > 0; |
| 45 | +} |
48 | 46 |
|
49 | | -class DependencyBlock extends Component<{ title: string; dependencies: [] }> { |
50 | | - public render(): ReactElement<HTMLElement> { |
51 | | - const { dependencies, title } = this.props; |
52 | | - const deps = Object.entries(dependencies) as []; |
| 47 | +const Dependencies: React.FC<{}> = () => { |
| 48 | + const { packageMeta } = useContext(DetailContext); |
53 | 49 |
|
54 | | - return ( |
55 | | - <DetailContextConsumer> |
56 | | - {({ enableLoading }) => { |
57 | | - return ( |
58 | | - <CardWrap> |
59 | | - <CardContent> |
60 | | - <Heading variant="subtitle1">{`${title} (${deps.length})`}</Heading> |
61 | | - <Tags>{this.renderTags(deps, enableLoading)}</Tags> |
62 | | - </CardContent> |
63 | | - </CardWrap> |
64 | | - ); |
65 | | - }} |
66 | | - </DetailContextConsumer> |
67 | | - ); |
| 50 | + if (!packageMeta) { |
| 51 | + throw new Error('packageMeta is required at DetailContext'); |
68 | 52 | } |
69 | 53 |
|
70 | | - private renderTags = (deps: [], enableLoading?: () => void) => |
71 | | - deps.map(dep => { |
72 | | - const [name, version] = dep as [string, string]; |
| 54 | + const { latest } = packageMeta; |
| 55 | + // FIXME: add dependencies to package meta type |
| 56 | + // @ts-ignore |
| 57 | + const { dependencies, devDependencies, peerDependencies, name } = latest; |
| 58 | + const dependencyMap = { dependencies, devDependencies, peerDependencies }; |
| 59 | + const hasDependencies = hasKeys(dependencies) || hasKeys(devDependencies) || hasKeys(peerDependencies); |
73 | 60 |
|
74 | | - return <WrapperDependencyDetail key={name} name={name} onLoading={enableLoading} version={version} />; |
75 | | - }); |
76 | | -} |
77 | | - |
78 | | -class Dependencies extends Component { |
79 | | - public render(): ReactElement<HTMLElement> { |
| 61 | + if (hasDependencies) { |
80 | 62 | return ( |
81 | | - <DetailContextConsumer> |
82 | | - {packageMeta => { |
83 | | - return this.renderDependencies(packageMeta as VersionPageConsumerProps); |
84 | | - }} |
85 | | - </DetailContextConsumer> |
| 63 | + <> |
| 64 | + {Object.entries(dependencyMap).map(([dependencyType, dependencies]) => { |
| 65 | + if (!dependencies || Object.keys(dependencies).length === 0) { |
| 66 | + return null; |
| 67 | + } |
| 68 | + |
| 69 | + return <DependencyBlock dependencies={dependencies} key={dependencyType} title={dependencyType} />; |
| 70 | + })} |
| 71 | + </> |
86 | 72 | ); |
87 | 73 | } |
88 | 74 |
|
89 | | - private checkDependencyLength<T>(dependency: Record<string, T> = {}): boolean { |
90 | | - return Object.keys(dependency).length > 0; |
91 | | - } |
92 | | - |
93 | | - private renderDependencies({ packageMeta }): ReactElement<HTMLElement> { |
94 | | - const { latest } = packageMeta; |
95 | | - const { dependencies, devDependencies, peerDependencies, name } = latest; |
96 | | - |
97 | | - const dependencyMap = { dependencies, devDependencies, peerDependencies }; |
98 | | - |
99 | | - const dependencyList = Object.keys(dependencyMap).reduce( |
100 | | - (result, value, key) => { |
101 | | - const selectedDepndency = dependencyMap[value]; |
102 | | - if (selectedDepndency && this.checkDependencyLength(selectedDepndency)) { |
103 | | - result.push(<DependencyBlock dependencies={selectedDepndency} key={key} title={value} />); |
104 | | - } |
105 | | - return result; |
106 | | - }, |
107 | | - [] as JSX.Element[] |
108 | | - ); |
109 | | - |
110 | | - if (dependencyList.length) { |
111 | | - return <Fragment>{dependencyList}</Fragment>; |
112 | | - } |
113 | | - return <NoItems className="no-dependencies" text={`${name} has no dependencies.`} />; |
114 | | - } |
115 | | -} |
| 75 | + return <NoItems className="no-dependencies" text={`${name} has no dependencies.`} />; |
| 76 | +}; |
116 | 77 |
|
117 | 78 | export default Dependencies; |
0 commit comments