diff --git a/.gitignore b/.gitignore index df43e6d..1058c2f 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +.idea/ + # dependencies /node_modules /.pnp diff --git a/src/app.js b/src/app.js index 8888930..a546068 100644 --- a/src/app.js +++ b/src/app.js @@ -1,13 +1,17 @@ -import React, {Component, Fragment} from 'react'; +/** + * @prettier + */ + +import React, { Component, Fragment } from 'react'; import isNil from 'lodash/isNil'; import storage from './utils/storage'; -import {makeLogin, isTokenExpire} from './utils/login'; +import { makeLogin, isTokenExpire } from './utils/login'; import Loading from './components/Loading'; import LoginModal from './components/Login'; import Header from './components/Header'; -import {Container, Content} from './components/Layout'; +import { Container, Content } from './components/Layout'; import RouterApp from './router'; import API from './utils/api'; import './styles/typeface-roboto.scss'; @@ -16,7 +20,6 @@ import 'normalize.css'; import Footer from './components/Footer'; export const AppContext = React.createContext(); - export const AppContextProvider = AppContext.Provider; export const AppContextConsumer = AppContext.Consumer; @@ -39,12 +42,29 @@ export default class App extends Component { // eslint-disable-next-line no-unused-vars componentDidUpdate(_, prevState) { - const {isUserLoggedIn} = this.state; + const { isUserLoggedIn } = this.state; if (prevState.isUserLoggedIn !== isUserLoggedIn) { this.loadOnHandler(); } } + render() { + const { isLoading, isUserLoggedIn, packages, logoUrl, user, scope } = this.state; + + return ( + + {isLoading ? ( + + ) : ( + + {this.renderContent()} + + )} + {this.renderLoginModal()} + + ); + } + isUserAlreadyLoggedIn = () => { // checks for token validity const token = storage.getItem('token'); @@ -53,7 +73,7 @@ export default class App extends Component { this.handleLogout(); } else { this.setState({ - user: {username, token}, + user: { username, token }, isUserLoggedIn: true, }); } @@ -76,7 +96,7 @@ export default class App extends Component { } }; - setLoading = (isLoading) => + setLoading = isLoading => this.setState({ isLoading, }); @@ -86,7 +106,7 @@ export default class App extends Component { * Required by:
*/ handleToggleLoginModal = () => { - this.setState((prevState) => ({ + this.setState(prevState => ({ showLoginModal: !prevState.showLoginModal, error: {}, })); @@ -97,7 +117,7 @@ export default class App extends Component { * Required by:
*/ handleDoLogin = async (usernameValue, passwordValue) => { - const {username, token, error} = await makeLogin(usernameValue, passwordValue); + const { username, token, error } = await makeLogin(usernameValue, passwordValue); if (username && token) { this.setLoggedUser(username, token); @@ -123,6 +143,7 @@ export default class App extends Component { showLoginModal: false, // set isUserLoggedIn to true }); }; + /** * Logouts user * Required by:
@@ -136,24 +157,8 @@ export default class App extends Component { }); }; - render() { - const {isLoading, isUserLoggedIn, packages, logoUrl, user, scope} = this.state; - return ( - - {isLoading ? ( - - ) : ( - - {this.renderContent()} - - )} - {this.renderLoginModal()} - - ); - } - renderLoginModal = () => { - const {error, showLoginModal} = this.state; + const { error, showLoginModal } = this.state; return ( { - const {logoUrl, user, scope} = this.state; + const { logoUrl, user: { username } = {}, scope } = this.state; - return
; + return
; }; } diff --git a/src/app.scss b/src/app.scss index b4ba27f..462f8b9 100644 --- a/src/app.scss +++ b/src/app.scss @@ -1,16 +1,16 @@ -@import "./styles/variables"; +@import './styles/variables'; .alertError { - background-color: $red !important; - min-width: inherit !important; + background-color: $red !important; + min-width: inherit !important; } .alertErrorMsg { - display: flex; - align-items: center; + display: flex; + align-items: center; } .alertIcon { - opacity: 0.9; - margin-right: 8px; -} + opacity: 0.9; + margin-right: 8px; +} \ No newline at end of file diff --git a/src/components/ActionBar/index.js b/src/components/ActionBar/index.js new file mode 100644 index 0000000..a1a771a --- /dev/null +++ b/src/components/ActionBar/index.js @@ -0,0 +1,109 @@ +/* eslint-disable react/jsx-max-depth */ +/** + * @prettier + */ + +import React, { Component } from 'react'; + +import { DetailContextConsumer } from '../../pages/version/index'; +import List from '@material-ui/core/List/index'; + +import DownloadIcon from '@material-ui/icons/CloudDownload'; +import BugReportIcon from '@material-ui/icons/BugReport'; +import HomeIcon from '@material-ui/icons/Home'; +import Tooltip from '@material-ui/core/Tooltip/index'; + +import { Fab, ActionListItem } from './styles'; + +const ACTIONS = { + homepage: { + icon: , + title: 'Visit homepage', + }, + issue: { + icon: , + title: 'Open an issue', + }, + tarball: { + icon: , + title: 'Download tarball', + }, +}; + +class ActionBar extends Component { + render() { + return ( + + {context => { + return this.renderActionBar(context); + }} + + ); + } + + renderIconsWithLink(link, component) { + if (!link) { + return null; + } + return ( + + {component} + + ); + } + + renderActionBarListItems = (packageMeta) => { + const { + latest: { + bugs: { + url: issue, + } = {}, + homepage, + dist: { + tarball, + } = {}, + } = {}, + } = packageMeta; + + const actionsMap = { + homepage, + issue, + tarball, + }; + + const renderList = Object.keys(actionsMap).reduce((component, value, key) => { + const link = actionsMap[value]; + if (link) { + const fab = ( + + {ACTIONS[value]['icon']} + + ); + component.push( + + {this.renderIconsWithLink(link, fab)} + + ); + } + return component; + }, []); + + return ( + <> + + {renderList} + + + ); + }; + + renderActionBar = ({ packageMeta = {} }) => { + return ( + + {this.renderActionBarListItems(packageMeta)} + + ); + }; +} + +export default ActionBar; diff --git a/src/components/ActionBar/styles.js b/src/components/ActionBar/styles.js new file mode 100644 index 0000000..2fce31d --- /dev/null +++ b/src/components/ActionBar/styles.js @@ -0,0 +1,24 @@ +/** + * @prettier + */ +import styled from 'react-emotion'; +import { default as MuiFab } from '@material-ui/core/Fab'; +import ListItem from '@material-ui/core/ListItem/index'; + +import colors from '../../utils/styles/colors'; + +export const ActionListItem = styled(ListItem)` + && { + padding-top: 0; + padding-left: 0; + padding-right: 0; + } +`; + +export const Fab = styled(MuiFab)` + && { + background-color: ${colors.primary}; + color: ${colors.white}; + margin-right: 10px; + } +`; diff --git a/src/components/Author/index.js b/src/components/Author/index.js index 825037a..5f26c70 100644 --- a/src/components/Author/index.js +++ b/src/components/Author/index.js @@ -1,49 +1,53 @@ -/* eslint no-unused-vars: 0 */ +/** + * @prettier + */ -import React, {Component, Fragment} from 'react'; +import React, { Component } from 'react'; -import {DetailContextConsumer} from '../../pages/version/index'; -import Card from '@material-ui/core/Card/index'; -import CardContent from '@material-ui/core/CardContent/index'; -import CopyToClipBoard from '../CopyToClipBoard'; -import CardHeader from '@material-ui/core/CardHeader/index'; -import Avatar from '@material-ui/core/Avatar'; -import CardActions from '@material-ui/core/CardActions'; -import Typography from '@material-ui/core/Typography/index'; +import Avatar from '@material-ui/core/Avatar/index'; +import List from '@material-ui/core/List/index'; +import ListItemText from '@material-ui/core/ListItemText/index'; + +import { DetailContextConsumer } from '../../pages/version/index'; +import { Heading, AuthorListItem } from './styles'; class Authors extends Component { render() { return ( - {(context) => { + {context => { return this.renderAuthor(context); }} ); } - renderAuthor = ({packageMeta}) => { - const {author} = packageMeta.latest; + renderLinkForMail(email, avatarComponent, packageName, version) { + if (!email) { + return avatarComponent; + } + return ( + + {avatarComponent} + + ); + } + + renderAuthor = ({ packageMeta }) => { + const { author, name: packageName, version } = packageMeta.latest; if (!author) { return null; } + const avatarComponent = ; return ( - - {this.renderAvatar(author)} - - ); - }; - - renderAvatar = ({name, email, url, avatar}) => { - return ( - - - - {name} - - + {'Author'}}> + + {this.renderLinkForMail(author.email, avatarComponent, packageName, version)} + + + ); }; } diff --git a/src/components/Author/styles.js b/src/components/Author/styles.js new file mode 100644 index 0000000..ae02d23 --- /dev/null +++ b/src/components/Author/styles.js @@ -0,0 +1,21 @@ +/** + * @prettier + * @flow + */ + +import styled from 'react-emotion'; +import ListItem from '@material-ui/core/ListItem/index'; +import Typography from '@material-ui/core/Typography/index'; + +export const Heading = styled(Typography)` + && { + font-weight: 700; + text-transform: capitalize; + } +`; +export const AuthorListItem = styled(ListItem)` + && { + padding-left: 0; + padding-right: 0; + } +`; diff --git a/src/components/CopyToClipBoard/index.js b/src/components/CopyToClipBoard/index.js index 513a3d1..0209665 100644 --- a/src/components/CopyToClipBoard/index.js +++ b/src/components/CopyToClipBoard/index.js @@ -7,14 +7,14 @@ import React from 'react'; import FileCopy from '@material-ui/icons/FileCopy'; import Tooltip from '@material-ui/core/Tooltip/index'; -import type {Node} from 'react'; -import {IProps} from './types'; +import type { Node } from 'react'; +import { IProps } from './types'; -import {ClipBoardCopy, ClipBoardCopyText, CopyIcon} from './styles'; -import {copyToClipBoardUtility} from '../../utils/cli-utils'; -import {TEXT} from '../../utils/constants'; +import { ClipBoardCopy, ClipBoardCopyText, CopyIcon } from './styles'; +import { copyToClipBoardUtility } from '../../utils/cli-utils'; +import { TEXT } from '../../utils/constants'; -const CopyToClipBoard = ({text}: IProps): Node => { +const CopyToClipBoard = ({ text, children }: IProps): Node => { const renderToolTipFileCopy = () => ( @@ -22,9 +22,17 @@ const CopyToClipBoard = ({text}: IProps): Node => { ); + + const renderText = children => { + if (children) { + return {children}; + } + + return {text}; + }; return ( - {text} + {renderText(children)} {renderToolTipFileCopy()} ); diff --git a/src/components/CopyToClipBoard/types.js b/src/components/CopyToClipBoard/types.js index 50115e6..36edbed 100644 --- a/src/components/CopyToClipBoard/types.js +++ b/src/components/CopyToClipBoard/types.js @@ -5,4 +5,5 @@ export interface IProps { text: string; + children?: any; } diff --git a/src/components/Dependencies/index.js b/src/components/Dependencies/index.js index 8dce80c..eebb66b 100644 --- a/src/components/Dependencies/index.js +++ b/src/components/Dependencies/index.js @@ -10,7 +10,9 @@ import { withRouter } from 'react-router-dom'; import CardContent from '@material-ui/core/CardContent/index'; import { DetailContextConsumer } from '../../pages/version'; -import { Content, CardWrap, Heading, Tags, Tag } from './styles'; + +import { CardWrap, Heading, Tags, Tag } from './styles'; +import NoItems from '../NoItems'; class DepDetail extends Component { constructor(props: any) { @@ -26,7 +28,7 @@ class DepDetail extends Component { render() { const { name, version } = this.state; const tagText = `${name}@${version}`; - return ; + return ; } handleOnClick = () => { @@ -34,20 +36,13 @@ class DepDetail extends Component { const { onLoading, history } = this.props; onLoading(); - history.push(`/-/web/version/${name}`); + history.push(`/-/web/detail/${name}`); }; } const WrappDepDetail = withRouter(DepDetail); class DependencyBlock extends Component { - renderTags = (deps: any, enableLoading: any) => - deps.map(dep => { - const [name, version] = dep; - - return ; - }); - render() { const { dependencies, title } = this.props; const deps = Object.entries(dependencies); @@ -59,7 +54,7 @@ class DependencyBlock extends Component { return ( - {title} + {`${title} (${deps.length})`} {this.renderTags(deps, enableLoading)} @@ -68,6 +63,13 @@ class DependencyBlock extends Component { ); } + + renderTags = (deps: any, enableLoading: any) => + deps.map(dep => { + const [name, version] = dep; + + return ; + }); } class Dependencies extends Component { @@ -85,23 +87,29 @@ class Dependencies extends Component { ); } + checkDependencyLength(dependency: Object = {}) { + return Object.keys(dependency).length > 0; + } + // $FlowFixMe renderDependencies({ packageMeta }) { const { latest } = packageMeta; - // console.log('renderDependencies', latest); - const { dependencies, devDependencies, peerDependencies } = latest; - // console.log('dependencies', dependencies); - // console.log('devDependencies', devDependencies); + const { dependencies, devDependencies, peerDependencies, name } = latest; - return ( - - - {dependencies && } - {devDependencies && } - {peerDependencies && } - - - ); + const dependencyMap = { dependencies, devDependencies, peerDependencies }; + + const dependencyList = Object.keys(dependencyMap).reduce((result, value, key) => { + const selectedDepndency = dependencyMap[value]; + if (selectedDepndency && this.checkDependencyLength(selectedDepndency)) { + result.push(); + } + return result; + }, []); + + if (dependencyList.length) { + return {dependencyList}; + } + return ; } } diff --git a/src/components/Dependencies/styles.js b/src/components/Dependencies/styles.js index 69c346e..3a55ed8 100644 --- a/src/components/Dependencies/styles.js +++ b/src/components/Dependencies/styles.js @@ -8,21 +8,16 @@ import Card from '@material-ui/core/Card/index'; import Typography from '@material-ui/core/Typography/index'; import Chip from '@material-ui/core/Chip/index'; -export const Content = styled.div` - && { - padding: 20px; - } -`; - export const CardWrap = styled(Card)` && { - margin: 0 0 25px; + margin: 0 0 16px; } `; export const Heading = styled(Typography)` && { font-weight: 700; + text-transform: capitalize; } `; diff --git a/src/components/DetailContainer/index.js b/src/components/DetailContainer/index.js index 708d4e3..d01514e 100644 --- a/src/components/DetailContainer/index.js +++ b/src/components/DetailContainer/index.js @@ -30,25 +30,31 @@ class DetailContainer extends Component { ); } + handleChange = (event: any, tabPosition: number) => { + event.preventDefault(); + this.setState({ tabPosition }); + }; + // $FlowFixMe renderTabs = ({ readMe }) => { const { tabPosition } = this.state; return ( - - - - - - - + <> + + + + + + +
{tabPosition === 0 && this.renderReadme(readMe)} {tabPosition === 1 && } {tabPosition === 2 && } {tabPosition === 3 && }
-
+ ); }; @@ -57,11 +63,6 @@ class DetailContainer extends Component { return ; }; - - handleChange = (event: any, tabPosition: number) => { - event.preventDefault(); - this.setState({ tabPosition }); - }; } export default DetailContainer; diff --git a/src/components/DetailSidebar/index.js b/src/components/DetailSidebar/index.js index d31fcd3..eb7e42a 100644 --- a/src/components/DetailSidebar/index.js +++ b/src/components/DetailSidebar/index.js @@ -1,17 +1,23 @@ import React, {Component} from 'react'; -import { DetailContextConsumer } from '../../pages/version/index'; -import Typography from '@material-ui/core/Typography/index'; -import Grid from '@material-ui/core/Grid/index'; +import Card from '@material-ui/core/Card/index'; +import CardContent from '@material-ui/core/CardContent/index'; +import List from '@material-ui/core/List/index'; -import Install from '../Install'; -import { Content } from './styles'; -import Authors from '../Author'; -import License from '../License'; -import Repository from '../Repository'; +import ActtionBar from '../ActionBar'; +import Author from '../Author'; import Developers from '../Developers'; +import Dist from '../Dist'; +import Engine from '../Engines'; +import Install from '../Install'; +import Repository from '../Repository'; -class DetailSidebar extends Component { + +import { DetailContextConsumer } from '../../pages/version/index'; + +import { TitleListItem, TitleListItemText } from './styles'; + +class DetailSidebar extends Component { render() { return ( @@ -20,43 +26,36 @@ class DetailSidebar extends Component { ); }; - renderSideBar = ({packageMeta, packageName}) => { + renderSideBar = ({packageName, packageMeta}) => { return ( - - - +
+ + {this.renderTitle(packageName, packageMeta)} - - + {this.renderActionBar()} {this.renderCopyCLI()} - - - {this.renderSecondLevel(8)} - - - {this.renderMaintainers()} - - - {this.renderContributors()} - - {this.renderRepository()} - - - + {this.renderEngine()} + {this.renderDist()} + {this.renderAuthor()} + {this.renderMaintainers()} + {this.renderContributors()} + + +
); } renderTitle = (packageName, packageMeta) => { return ( - <> - - {packageName} - - - {packageMeta.latest.description} - - + + + {packageName}} + secondary={packageMeta.latest.description} + /> + + ); } @@ -72,31 +71,25 @@ class DetailSidebar extends Component { return ; } - renderSecondLevel = (spacing = 24) => { - return ( - - {this.renderAuthor()} - - ); - } - renderRepository = () => { return ; } renderAuthor = () => { - return ( - <> - - - - - - - - ); + return ; + } + + renderEngine = () => { + return ; + } + + renderDist = () => { + return ; + } + + renderActionBar = () => { + return ; } } - export default DetailSidebar; diff --git a/src/components/DetailSidebar/styles.js b/src/components/DetailSidebar/styles.js index 0cdcde2..bda5acc 100644 --- a/src/components/DetailSidebar/styles.js +++ b/src/components/DetailSidebar/styles.js @@ -4,11 +4,32 @@ */ import styled from 'react-emotion'; +import Avatar from '@material-ui/core/Avatar/index'; +import ListItem from '@material-ui/core/ListItem/index'; +import ListItemText from '@material-ui/core/ListItemText/index'; + import colors from '../../utils/styles/colors'; -export const Content = styled.div` +export const TitleListItem = styled(ListItem)` && { - padding: 10px; - background-color: ${colors.white}; + padding-left: 0; + padding-right: 0; + padding-bottom: 0; + } +`; + +export const TitleListItemText = styled(ListItemText)` + && { + padding-left: 0; + padding-right: 0; + padding-top: 8px; + } +`; + +export const TitleAvatar = styled(Avatar)` + && { + color: ${colors.greySuperLight}; + background-color: ${colors.primary}; + text-transform: capitalize; } `; diff --git a/src/components/Developers/index.js b/src/components/Developers/index.js index d311bc2..31aad5f 100644 --- a/src/components/Developers/index.js +++ b/src/components/Developers/index.js @@ -1,11 +1,12 @@ import React, {Component} from 'react'; -import { DetailContextConsumer } from '../../pages/version'; -import Card from '@material-ui/core/Card'; import Avatar from '@material-ui/core/Avatar'; import Tooltip from '@material-ui/core/Tooltip'; import Add from '@material-ui/icons/Add'; -import { Details, Heading, Content, CardContent, Fab } from './styles'; + +import { DetailContextConsumer } from '../../pages/version'; + +import { Details, Heading, Content, Fab } from './styles'; interface Props { type: 'contributors' | 'maintainers' @@ -23,44 +24,59 @@ class Developers extends Component { const { type } = this.props; const developerType = packageMeta.latest[type]; if (!developerType || developerType.length === 0) return null; - return this.renderDevelopers(developerType); + return this.renderDevelopers(developerType, packageMeta); }}
); }; - renderDevelopers = (developers) => { + handleLoadMore = () => { + this.setState((prev) => ({ visibleDevs: prev.visibleDevs + 6 })); + } + + renderDevelopers = (developers, packageMeta) => { const { type } = this.props; const { visibleDevs } = this.state; return ( - - - {type} - - {developers.slice(0, visibleDevs).map(developer => ( -
{this.renderDeveloperDetails(developer)}
- ))} - {visibleDevs < developers.length && - - } -
-
-
+ <> + {type} + + {developers.slice(0, visibleDevs).map(developer => ( +
{this.renderDeveloperDetails(developer, packageMeta)}
+ ))} + {visibleDevs < developers.length && + + } +
+ ); } - renderDeveloperDetails= ({name, avatar }) => { + renderLinkForMail(email, avatarComponent, packageName, version) { + if(!email) { + return avatarComponent; + } + return ( + + {avatarComponent} + + ); + } + + renderDeveloperDetails = ({ name, avatar, email }, packageMeta) => { + const { + name: packageName, + version, + } = packageMeta.latest; + + const avatarComponent = ; return ( - + {this.renderLinkForMail(email, avatarComponent, packageName, version)} ); } - handleLoadMore = () => { - this.setState((prev) => ({ visibleDevs: prev.visibleDevs + 6 })); - } - } diff --git a/src/components/Developers/styles.js b/src/components/Developers/styles.js index 3c89c0f..ef8afcd 100644 --- a/src/components/Developers/styles.js +++ b/src/components/Developers/styles.js @@ -7,8 +7,6 @@ import Typography from '@material-ui/core/Typography'; import { default as MuiFab } from '@material-ui/core/Fab'; import colors from '../../utils/styles/colors'; -import { default as MuiCardContent } from '@material-ui/core/CardContent/index'; - export const Details = styled('span')` display: flex; flex-direction: column; @@ -16,7 +14,7 @@ export const Details = styled('span')` `; export const Content = styled('div')` - margin: -5px; + margin: 10px 0 10px 0; display: flex; flex-wrap: wrap; > * { @@ -24,12 +22,6 @@ export const Content = styled('div')` } `; -export const CardContent = styled(MuiCardContent)` - && { - padding-bottom: 20px; - } -`; - export const Heading = styled(Typography)` && { font-weight: 700; diff --git a/src/components/Dist/index.js b/src/components/Dist/index.js new file mode 100644 index 0000000..27463a6 --- /dev/null +++ b/src/components/Dist/index.js @@ -0,0 +1,59 @@ +/** + * @prettier + */ + +/* eslint-disable */ +import React, { Component } from 'react'; +import List from '@material-ui/core/List/index'; + +import { DetailContextConsumer } from '../../pages/version/index'; + +import { Heading, DistListItem, DistChips, DownloadButton } from './styles'; +import fileSizeSI from '../../utils/file-size'; + +class Dist extends Component { + render() { + return ( + + {context => { + return this.renderDist(context); + }} + + ); + } + + renderChips(dist, license) { + const distDict = { + 'file-count': dist.fileCount, + size: dist.unpackedSize && fileSizeSI(dist.unpackedSize), + license, + }; + + const chipsList = Object.keys(distDict).reduce((componentList, title, key) => { + const value = distDict[title]; + if (value) { + const label = ( + + {title.split('-').join(' ')}: {value} + + ); + componentList.push(); + } + return componentList; + }, []); + + return chipsList; + } + + renderDist = ({ packageMeta }) => { + const { dist = {}, license } = packageMeta.latest; + + return ( + {'Latest Distribution'}}> + {this.renderChips(dist, license)} + + ); + }; +} + +export default Dist; diff --git a/src/components/Dist/styles.js b/src/components/Dist/styles.js new file mode 100644 index 0000000..82c65d4 --- /dev/null +++ b/src/components/Dist/styles.js @@ -0,0 +1,39 @@ +/** + * @prettier + */ + +import styled from 'react-emotion'; +import { default as MuiFab } from '@material-ui/core/Fab/index'; +import Chip from '@material-ui/core/Chip/index'; +import ListItem from '@material-ui/core/ListItem/index'; +import Typography from '@material-ui/core/Typography/index'; + +import colors from '../../utils/styles/colors'; + +export const Heading = styled(Typography)` + && { + font-weight: 700; + text-transform: capitalize; + } +`; + +export const DistListItem = styled(ListItem)` + && { + padding-left: 0; + padding-right: 0; + } +`; + +export const DistChips = styled(Chip)` + && { + margin-right: 5px; + text-transform: capitalize; + } +`; + +export const DownloadButton = styled(MuiFab)` + && { + background-color: ${colors.primary}; + color: ${colors.white}; + } +`; diff --git a/src/components/Engines/img/node.png b/src/components/Engines/img/node.png new file mode 100644 index 0000000..e1412ad Binary files /dev/null and b/src/components/Engines/img/node.png differ diff --git a/src/components/Engines/index.js b/src/components/Engines/index.js new file mode 100644 index 0000000..000d850 --- /dev/null +++ b/src/components/Engines/index.js @@ -0,0 +1,78 @@ +/* eslint-disable */ +import React, {Component} from 'react'; + +import Avatar from '@material-ui/core/Avatar/index'; +import Grid from '@material-ui/core/Grid/index'; +import List from '@material-ui/core/List/index'; +import ListItemText from '@material-ui/core/ListItemText/index'; + +import { DetailContextConsumer } from '../../pages/version/index'; + +import { Heading, EngineListItem } from './styles'; +import node from './img/node.png'; +import npm from '../Install/img/npm.svg' + + +const ICONS = { + 'node-JS': , + 'NPM-version': , +} + +class Engine extends Component { + render() { + return ( + + {(context) => { + return this.renderEngine(context); + }} + + ); + }; + + renderEngine = ({packageMeta}) => { + const { engines } = packageMeta.latest; + if (!engines) { + return null; + } + + const engineDict = { + 'node-JS': engines.node, + 'NPM-version': engines.npm + } + + const items = Object.keys(engineDict).reduce((markup, text, key) => { + const heading = engineDict[text] + if (heading){ + markup.push( + + {this.renderListItems(heading, text)} + + ); + } + return markup; + }, []); + + if (items.length < 1) { + return null; + } + + return ( + + {items} + + ); + } + + renderListItems = (heading, text) => { + return ( + {text.split('-').join(' ')}}> + + { ICONS[text] } + + + + ); + } +} + +export default Engine; diff --git a/src/components/Engines/styles.js b/src/components/Engines/styles.js new file mode 100644 index 0000000..7905dcb --- /dev/null +++ b/src/components/Engines/styles.js @@ -0,0 +1,21 @@ +/** + * @prettier + * @flow + */ + +import styled from 'react-emotion'; +import ListItem from '@material-ui/core/ListItem/index'; +import Typography from '@material-ui/core/Typography/index'; + +export const Heading = styled(Typography)` + && { + font-weight: 700; + text-transform: capitalize; + } +`; + +export const EngineListItem = styled(ListItem)` + && { + padding-left: 0; + } +`; diff --git a/src/components/Footer/index.js b/src/components/Footer/index.js index f60901e..b8ad369 100644 --- a/src/components/Footer/index.js +++ b/src/components/Footer/index.js @@ -3,53 +3,35 @@ * @flow */ -import React from "react"; -import type { Element } from "react"; +import React from 'react'; +import type { Element } from 'react'; -import { version } from "../../../package.json"; -import { - Wrapper, - Left, - Right, - Earth, - Flags, - Love, - Flag, - Logo, - Inner, - ToolTip -} from "./styles"; -import { goToVerdaccioWebsite } from "../../utils/windows.js"; +import { version } from '../../../../package.json'; +import { Wrapper, Left, Right, Earth, Flags, Love, Flag, Logo, Inner, ToolTip } from './styles'; +import { goToVerdaccioWebsite } from '../../utils/windows.js'; const renderTooltip = () => ( - + - - - - - - - + + + + + + ); -const POWERED_LABEL = "Powered by"; -const MADEWITH_LABEL = " Made with"; -const ON_LABEL = "on"; -const HEARTH_EMOJI = "♥"; +const POWERED_LABEL = 'Powered by'; +const MADEWITH_LABEL = ' Made with'; +const ON_LABEL = 'on'; +const HEARTH_EMOJI = '♥'; const renderRight = () => ( {POWERED_LABEL} - + {`/ ${version}`} ); diff --git a/src/components/Header/index.js b/src/components/Header/index.js index 1b8c8bf..f1f1248 100644 --- a/src/components/Header/index.js +++ b/src/components/Header/index.js @@ -127,7 +127,7 @@ class Header extends Component { renderLogo = (): Node => { const { logo } = this.props; - if (logo) { + if (logo !== '') { return {'logo'}; } else { return ; diff --git a/src/components/Help/index.js b/src/components/Help/index.js index 49b679a..d553135 100644 --- a/src/components/Help/index.js +++ b/src/components/Help/index.js @@ -35,8 +35,8 @@ const Help = (): Node => { {'To publish your first package just:'} - {renderHeadingClipboardSegments('1. Login', `$ npm adduser --registry ${registryUrl}`)} - {renderHeadingClipboardSegments('2. Publish', `$ npm publish --registry ${registryUrl}`)} + {renderHeadingClipboardSegments('1. Login', `npm adduser --registry ${registryUrl}`)} + {renderHeadingClipboardSegments('2. Publish', `npm publish --registry ${registryUrl}`)} {'3. Refresh this page.'} diff --git a/src/components/Icon/img/filebinary.svg b/src/components/Icon/img/filebinary.svg new file mode 100644 index 0000000..f891b36 --- /dev/null +++ b/src/components/Icon/img/filebinary.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/Icon/img/law.svg b/src/components/Icon/img/law.svg new file mode 100644 index 0000000..499148d --- /dev/null +++ b/src/components/Icon/img/law.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/components/Icon/img/version.svg b/src/components/Icon/img/version.svg new file mode 100644 index 0000000..6a0eb92 --- /dev/null +++ b/src/components/Icon/img/version.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/Icon/index.js b/src/components/Icon/index.js index c44c861..ae78da3 100644 --- a/src/components/Icon/index.js +++ b/src/components/Icon/index.js @@ -19,8 +19,11 @@ import austria from './img/austria.svg'; import spain from './img/spain.svg'; import earth from './img/earth.svg'; import verdaccio from './img/verdaccio.svg'; +import filebinary from './img/filebinary.svg'; +import law from './img/law.svg'; import license from './img/license.svg'; import time from './img/time.svg'; +import version from './img/version.svg'; export const Icons: $Shape = { // flags @@ -33,8 +36,12 @@ export const Icons: $Shape = { austria, earth, verdaccio, + // other icons + filebinary, + law, license, time, + version, }; const Icon = ({ className, name, size = 'sm', img = false, pointer = false, ...props }: IProps): Node => { diff --git a/src/components/Icon/styles.js b/src/components/Icon/styles.js index 0454d4c..fd3455f 100644 --- a/src/components/Icon/styles.js +++ b/src/components/Icon/styles.js @@ -21,7 +21,7 @@ const getSize = (size: string) => { default: return ` width: 14px; - height: 14px; + height: 16px; `; } }; diff --git a/src/components/Icon/types.js b/src/components/Icon/types.js index 5301ddd..5b2adf6 100644 --- a/src/components/Icon/types.js +++ b/src/components/Icon/types.js @@ -17,6 +17,9 @@ export interface IIconsMap { verdaccio: string; license: string; time: string; + law: string; + version: string; + filebinary: string; [key: string]: string; } diff --git a/src/components/Install/img/npm.svg b/src/components/Install/img/npm.svg new file mode 100644 index 0000000..5df8177 --- /dev/null +++ b/src/components/Install/img/npm.svg @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/src/components/Install/img/pnpm.svg b/src/components/Install/img/pnpm.svg new file mode 100644 index 0000000..b3549a4 --- /dev/null +++ b/src/components/Install/img/pnpm.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Install/img/yarn.svg b/src/components/Install/img/yarn.svg new file mode 100644 index 0000000..86edb18 --- /dev/null +++ b/src/components/Install/img/yarn.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Install/index.js b/src/components/Install/index.js index 44a16d2..e28d1f5 100644 --- a/src/components/Install/index.js +++ b/src/components/Install/index.js @@ -1,46 +1,58 @@ -import React, {Component} from 'react'; +/** + * @prettier + */ + +import React, { Component } from 'react'; + +import List from '@material-ui/core/List/index'; +import ListItemText from '@material-ui/core/ListItemText/index'; import { DetailContextConsumer } from '../../pages/version/index'; -import Card from '@material-ui/core/Card/index'; -import CardContent from '@material-ui/core/CardContent/index'; import CopyToClipBoard from '../CopyToClipBoard'; -import Button from '@material-ui/core/Button'; -import CardActions from '@material-ui/core/CardActions'; -class Install extends Component { +import { Heading, InstallItem, PackageMangerAvatar } from './styles'; +// logos of package managers +import npm from './img/npm.svg'; +import pnpm from './img/pnpm.svg'; +import yarn from './img/yarn.svg'; + +class Install extends Component { render() { return ( - {(context) => { + {context => { return this.renderCopyCLI(context); }} ); + } + + renderCopyCLI = ({ packageName }) => { + return ( + <> + {'Installation'}}>{this.renderListItems(packageName)} + + ); }; - renderCopyCLI = ({packageName}) => { + renderListItems = packageName => { return ( - - - - - - - {this.renderDownloadButton()} - - - + <> + + + } secondary={'Install using NPM'} /> + + + + } secondary={'Install using Yarn'} /> + + + + } secondary={'Install using PNPM'} /> + + ); - } - - renderDownloadButton = () => { - return ( - - ); - } + }; } - export default Install; diff --git a/src/components/Install/styles.js b/src/components/Install/styles.js new file mode 100644 index 0000000..654368d --- /dev/null +++ b/src/components/Install/styles.js @@ -0,0 +1,28 @@ +/** + * @prettier + * @flow + */ + +import styled from 'react-emotion'; +import Typography from '@material-ui/core/Typography/index'; +import ListItem from '@material-ui/core/ListItem/index'; +import Avatar from '@material-ui/core/Avatar/index'; + +export const Heading = styled(Typography)` + && { + font-weight: 700; + text-transform: capitalize; + } +`; + +export const InstallItem = styled(ListItem)` + && { + padding: 0; + } +`; + +export const PackageMangerAvatar = styled(Avatar)` + && { + border-radius: 0px; + } +`; diff --git a/src/components/License/index.js b/src/components/License/index.js deleted file mode 100644 index f7c3c4c..0000000 --- a/src/components/License/index.js +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint no-unused-vars: 0 */ - -import React, {Component, Fragment} from 'react'; - -import { DetailContextConsumer } from '../../pages/version/index'; -import Card from '@material-ui/core/Card/index'; -import CardContent from '@material-ui/core/CardContent/index'; -import Avatar from '@material-ui/core/Avatar'; -import Notes from '@material-ui/icons/Notes'; -import Typography from "@material-ui/core/Typography/index"; - -class License extends Component { - render() { - return ( - - {(context) => { - return this.renderAuthor(context); - }} - - ); - }; - - renderAuthor = ({packageMeta}) => { - const { license } = packageMeta.latest; - if (!license) { - return null; - } - - return ( - - - {this.renderLicense(license)} - - - ); - } - - renderLicense = (license) => { - return ( - - - - {license} - - - ); - } -} - - -export default License; diff --git a/src/components/NoItems/index.js b/src/components/NoItems/index.js index 1e48a91..e8fd6e5 100644 --- a/src/components/NoItems/index.js +++ b/src/components/NoItems/index.js @@ -4,13 +4,14 @@ */ import React from 'react'; +import Typography from '@material-ui/core/Typography/index'; + import { IProps } from './types'; -import { Wrapper } from './styles'; const NoItems = ({ text }: IProps) => ( - -

{text}

-
+ + {text} + ); export default NoItems; diff --git a/src/components/NoItems/styles.js b/src/components/NoItems/styles.js deleted file mode 100644 index 1a0085d..0000000 --- a/src/components/NoItems/styles.js +++ /dev/null @@ -1,12 +0,0 @@ -/** - * @prettier - * @flow - */ - -import styled from 'react-emotion'; - -export const Wrapper = styled.div` - && { - margin: 5em 0; - } -`; diff --git a/src/components/NotFound/index.js b/src/components/NotFound/index.js index 2d7b82a..39bf395 100644 --- a/src/components/NotFound/index.js +++ b/src/components/NotFound/index.js @@ -10,6 +10,8 @@ import Typography from '@material-ui/core/Typography/index'; import { Wrapper, Inner, EmptyPackage, Heading, Card, List } from './styles'; import PackageImg from './img/package.svg'; +export const NOT_FOUND_TEXT = "Sorry, we couldn't find it..."; + // eslint-disable-next-line react/prop-types const NotFound = ({ history, width }) => { const handleGoTo = to => () => { @@ -42,7 +44,9 @@ const NotFound = ({ history, width }) => { - {"Sorry, we couldn't find it..."} + + {NOT_FOUND_TEXT} + {renderSubTitle()} {renderList()} diff --git a/src/components/Package/index.js b/src/components/Package/index.js index a2456db..6dbb45a 100644 --- a/src/components/Package/index.js +++ b/src/components/Package/index.js @@ -2,104 +2,160 @@ * @prettier * @flow */ - import React from 'react'; import type { Element } from 'react'; -import { spacing } from '../../utils/styles/mixings'; + +import BugReport from '@material-ui/icons/BugReport'; +import Grid from '@material-ui/core/Grid/index'; +import HomeIcon from '@material-ui/icons/Home'; +import ListItem from '@material-ui/core/ListItem/index'; +import Tooltip from '@material-ui/core/Tooltip/index'; import Tag from '../Tag'; +import fileSizeSI from '../../utils/file-size'; import { formatDate, formatDateDistance } from '../../utils/package'; - import { IProps } from './types'; + import { - WrapperLink, - Header, - MainInfo, - Name, - Version, - Overview, - Published, - OverviewItem, - Description, - Icon, - Text, - Details, - Avatar, Author, - Field, - Content, - Footer, + Avatar, + Description, + Details, + GridRightAligned, + Icon, + IconButton, + OverviewItem, + PackageList, + PackageListItem, + PackageListItemText, + PackageTitle, + Published, + TagContainer, + Text, + WrapperLink, } from './styles'; -const getInitialsName = (name: string) => - name - .split(' ') - .reduce((accumulator, currentValue) => accumulator.charAt(0) + currentValue.charAt(0), '') - .toUpperCase(); +const Package = ({ + author: { name: authorName, avatar: authorAvatar }, + bugs: { url } = {}, + description, + dist: { unpackedSize } = {}, + homepage, + keywords = [], + license, + name: packageName, + time, + version, +}: IProps): Element => { + // + const renderVersionInfo = () => + version && ( + + + {`v${version}`} + + ); -const Package = ({ name: label, version, time, author: { name, avatar }, description, license, keywords = [] }: IProps): Element => { - const renderMainInfo = () => ( - - {label} - {`v${version}`} - - ); + const renderAuthorInfo = () => + authorName && ( + + +
+ +
+
+ ); - const renderAuthorInfo = () => ( - - - {!avatar && getInitialsName(name)} - -
- -
-
- ); + const renderFileSize = () => + unpackedSize && ( + + + {fileSizeSI(unpackedSize)} + + ); const renderLicenseInfo = () => license && ( - + {license} ); - const renderPublishedInfo = () => ( - - - {`Published on ${formatDate(time)} •`} - {`${formatDateDistance(time)} ago`} - - ); - - const renderDescription = () => - description && ( - - {description} - + const renderPublishedInfo = () => + time && ( + + + {`Published on ${formatDate(time)} •`} + {`${formatDateDistance(time)} ago`} + ); + const renderHomePageLink = () => + homepage && ( + + + + {/* eslint-disable-next-line react/jsx-max-depth */} + + + + + ); + + const renderBugsLink = () => + url && ( + + + + {/* eslint-disable-next-line react/jsx-max-depth */} + + + + + ); + + const renderPrimaryComponent = () => { + return ( + + + + {/* eslint-disable-next-line react/jsx-max-depth */} + {packageName} + + + + {renderHomePageLink()} + {renderBugsLink()} + + + ); + }; + + const renderSecondaryComponent = () => { + const tags = keywords.sort().map((keyword, index) => {keyword}); + return ( + <> + {description} + {tags.length > 0 && {tags}} + + ); + }; + return ( - -
- {renderMainInfo()} - - {renderLicenseInfo()} - {renderPublishedInfo()} - -
- - {renderAuthorInfo()} - {renderDescription()} - - {keywords.length > 0 && ( -
- {keywords.sort().map((keyword, index) => ( - {keyword} - ))} -
- )} -
+ + + + + + {renderAuthorInfo()} + {renderVersionInfo()} + {renderPublishedInfo()} + {renderFileSize()} + {renderLicenseInfo()} + + ); }; + export default Package; diff --git a/src/components/Package/styles.js b/src/components/Package/styles.js index 6390f07..eaa4860 100644 --- a/src/components/Package/styles.js +++ b/src/components/Package/styles.js @@ -3,126 +3,68 @@ * @flow */ -import styled, { css } from 'react-emotion'; +import styled from 'react-emotion'; import { Link } from 'react-router-dom'; -import { default as Photo } from '@material-ui/core/Avatar'; -import { default as Ico } from '../Icon'; -import mq from '../../utils/styles/media'; -import { ellipsis } from '../../utils/styles/mixings'; -import colors from '../../utils/styles/colors'; +import Grid from '@material-ui/core/Grid/index'; +import List from '@material-ui/core/List/index'; +import ListItem from '@material-ui/core/ListItem/index'; +import ListItemText from '@material-ui/core/ListItemText/index'; +import MuiIconButton from '@material-ui/core/IconButton/index'; +import Photo from '@material-ui/core/Avatar'; +import Typography from '@material-ui/core/Typography/index'; +import { breakpoints } from '../../utils/styles/media'; +import Ico from '../Icon'; import Label from '../Label'; - -// HEADER -export const Header = styled.div` - && { - display: flex; - flex-direction: column; - padding: 0 0 5px 0; - } -`; - -export const Name = styled.span` - && { - ${ellipsis('50%')}; - } -`; - -export const MainInfo = styled.span` - && { - font-size: 16px; - font-weight: 600; - line-height: 30px; - flex: 1; - color: #3a8bff; - padding: 0 10px 0 0; - overflow: hidden; - display: flex; - align-items: center; - :hover { - ${Name} { - text-decoration: underline; - } - } - } -`; +import colors from '../../utils/styles/colors'; export const OverviewItem = styled.span` && { display: flex; align-items: center; - margin: 0 0 5px 0; - color: ${colors.greyLight}; - } -`; - -export const Overview = styled.span` - && { - position: relative; - display: flex; - flex-direction: column; - } -`; - -export const Version = styled.span` - && { + margin: 0 0 0 16px; + color: ${colors.greyLight2}; font-size: 12px; - padding: 0 0 0 10px; - margin: 0 0 0 5px; - color: #9f9f9f; - position: relative; - ${ellipsis('100%')}; - :before { - content: '•'; - position: absolute; - left: 0; + @media (max-width: ${breakpoints.medium}px) { + &:nth-child(3) { + display: none; + } + } + @media (max-width: ${breakpoints.small}px) { + &:nth-child(4) { + display: none; + } } } `; export const Icon = styled(Ico)` && { - margin: 1px 5px 0 0; - fill: ${colors.greyLight}; + margin: 2px 10px 0px 0; + fill: ${colors.greyLight2}; } `; export const Published = styled.span` && { - display: none; - color: ${colors.greyLight}; - ${({ modifiers }) => modifiers}; - } -`; - -// Content -export const Field = styled.div` - && { - padding: 0 0 5px 0; - } -`; - -export const Content = styled.div` - && { - ${Field} { - :last-child { - padding: 0; - } - } + color: ${colors.greyLight2}; + margin: 0px 5px 0px 0px; } `; export const Text = styled(Label)` && { - color: #908ba1; + font-size: 12px; + font-weight: 500; + color: ${colors.greyLight2}; } `; export const Details = styled.span` && { margin-left: 5px; - line-height: 14px; + line-height: 1.5; display: flex; flex-direction: column; } @@ -137,63 +79,88 @@ export const Author = styled.div` export const Avatar = styled(Photo)` && { - width: 30px; - height: 30px; - background: #4b5e40; - font-size: 15px; + width: 20px; + height: 20px; } `; -export const Description = styled.div` - && { - margin: 5px 0; - } -`; - -// Footer -export const Footer = styled.div` - && { - display: none; - padding: 5px 0 0 0; - } -`; - -// Container export const WrapperLink = styled(Link)` && { - font-size: 12px; - background-color: white; - margin: 0 0 15px 0; - transition: box-shadow 0.15s; - box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15); - border-radius: 3px; - padding: 10px; text-decoration: none; - display: block; - color: #2f273c; - ${mq.medium(css` - ${Header} { - flex-direction: row; - justify-content: space-between; - align-items: center; - } - ${OverviewItem} { - margin: 0 0 0 10px; - } - ${Overview} { - flex-direction: row; - ${OverviewItem} { - :first-child { - margin: 0; - } - } - } - ${Footer} { - display: block; - } - ${Published} { - display: inline-block; - } - `)}; } `; + +export const PackageTitle = styled.span` + && { + font-weight: 600; + font-size: 20px; + display: block; + margin-bottom: 12px; + color: ${colors.eclipse}; + cursor: pointer; + + &:hover { + color: ${colors.black}; + } + + @media (max-width: ${breakpoints.small}px) { + font-size: 14px; + margin-bottom: 8px; + } + } +`; + +export const GridRightAligned = styled(Grid)` + && { + text-align: right; + } +`; + +export const PackageList = styled(List)` + && { + padding: 12px 0 12px 0; + + &:hover { + background-color: ${colors.greyLight3}; + } + } +`; + +export const IconButton = styled(MuiIconButton)` + && { + padding: 6px; + + svg { + font-size: 16px; + } + } +`; + +export const TagContainer = styled.span` + && { + margin-top: 8px; + margin-bottom: 12px; + display: block; + @media (max-width: ${breakpoints.medium}px) { + display: none; + } + } +`; + +export const PackageListItem = styled(ListItem)` + && { + padding-top: 0; + } +`; + +export const PackageListItemText = styled(ListItemText)` + && { + padding-right: 0; + } +`; + +export const Description = styled(Typography)` + color: ${colors.greyDark2}; + font-size: 14px; + padding-right: 0; +`; diff --git a/src/components/Package/types.js b/src/components/Package/types.js index f39ea33..365a159 100644 --- a/src/components/Package/types.js +++ b/src/components/Package/types.js @@ -11,6 +11,9 @@ export interface IProps { description?: string; keywords?: string[]; license?: string; + homepage: string; + bugs: IBugs; + dist: IDist; } export interface IAuthor { @@ -18,3 +21,10 @@ export interface IAuthor { avatar: string; email: string; } + +export interface IBugs { + url: string; +} +export interface IDist { + unpackedSize: number; +} diff --git a/src/components/PackageDetail/index.js b/src/components/PackageDetail/index.js deleted file mode 100644 index 493e98f..0000000 --- a/src/components/PackageDetail/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import isNil from 'lodash/isNil'; - -import Readme from '../Readme'; - -import classes from './packageDetail.scss'; - -const displayState = (description) => { - return !isNil(description) ? : ''; -}; - -const PackageDetail = ({packageName, readMe}) => { - return ( -
-

- {packageName} -

-
- {displayState(readMe)} -
-
- ); -}; - -PackageDetail.propTypes = { - readMe: PropTypes.string, - packageName: PropTypes.string.isRequired, -}; - -export default PackageDetail; diff --git a/src/components/PackageDetail/packageDetail.scss b/src/components/PackageDetail/packageDetail.scss deleted file mode 100644 index c918115..0000000 --- a/src/components/PackageDetail/packageDetail.scss +++ /dev/null @@ -1,16 +0,0 @@ -@import '../../styles/variables'; -@import '../../styles/mixins'; - -.pkgDetail { - .title { - font-size: $font-size-xxl; - font-weight: $font-weight-semibold; - margin: 0 0 40px; - padding-bottom: 5px; - @include border-bottom-default($greyGainsboro); - } - - .readme { - margin-bottom: 5em; - } -} diff --git a/src/components/PackageList/index.js b/src/components/PackageList/index.js index 70d66e6..db869eb 100644 --- a/src/components/PackageList/index.js +++ b/src/components/PackageList/index.js @@ -1,9 +1,11 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; +import Divider from '@material-ui/core/Divider'; + import Package from '../Package'; import Help from '../Help'; -import { formatAuthor, formatLicense } from '../../utils/package'; +import { formatLicense } from '../../utils/package'; import classes from './packageList.scss'; @@ -12,28 +14,6 @@ export default class PackageList extends React.Component { packages: PropTypes.array, }; - renderPackages = () => { - return ( - - {this.renderList()} - - ); - } - - renderList = () => { - const { packages } = this.props; - return ( - packages.map((pkg, i) => { - const { name, version, description, time, keywords } = pkg; - const author = formatAuthor(pkg.author); - const license = formatLicense(pkg.license); - return ( - - ); - }) - ); - } - render() { return (
@@ -46,7 +26,32 @@ export default class PackageList extends React.Component { hasPackages() { const {packages} = this.props; - return packages.length > 0; } + + renderPackages = () => { + return ( + + {this.renderList()} + + ); + } + + renderList = () => { + const { packages } = this.props; + return ( + packages.map((pkg, i) => { + const { name, version, description, time, keywords, dist, homepage, bugs } = pkg; + const author = pkg.author; + // TODO: move format license to API side. + const license = formatLicense(pkg.license); + return ( + + {i !== 0 && } + + + ); + }) + ); + } } diff --git a/src/components/PackageSidebar/Module/index.jsx b/src/components/PackageSidebar/Module/index.jsx deleted file mode 100644 index ba2409d..0000000 --- a/src/components/PackageSidebar/Module/index.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import classes from './style.scss'; - -export default function Module({title, description, children, className}) { - return ( -
-

- {title} - {description && {description}} -

-
- {children} -
-
- ); -} - -Module.propTypes = { - title: PropTypes.string.isRequired, - description: PropTypes.string, - children: PropTypes.any.isRequired, - className: PropTypes.string, -}; diff --git a/src/components/PackageSidebar/Module/style.scss b/src/components/PackageSidebar/Module/style.scss deleted file mode 100644 index 80164c7..0000000 --- a/src/components/PackageSidebar/Module/style.scss +++ /dev/null @@ -1,24 +0,0 @@ -@import '../../../styles/variables'; -@import '../../../styles/mixins'; - -.module { - - margin-bottom: 10px; - - .moduleTitle { - display: flex; - align-items: flex-end; - font-size: $font-size-lg; - margin: 0 0 10px; - padding: 5px 0; - font-weight: $font-weight-semibold; - @include border-bottom-default($greyGainsboro); - - span { // description - font-size: $font-size-sm; - color: $greyChateau; - margin-left: auto; - font-weight: $font-weight-light; - } - } -} diff --git a/src/components/PackageSidebar/ModuleContentPlaceholder/index.jsx b/src/components/PackageSidebar/ModuleContentPlaceholder/index.jsx deleted file mode 100644 index 4df6744..0000000 --- a/src/components/PackageSidebar/ModuleContentPlaceholder/index.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import classes from './style.scss'; - -export default function ModuleContentPlaceholder({text}) { - return

{text}

; -} -ModuleContentPlaceholder.propTypes = { - text: PropTypes.string.isRequired, -}; diff --git a/src/components/PackageSidebar/ModuleContentPlaceholder/style.scss b/src/components/PackageSidebar/ModuleContentPlaceholder/style.scss deleted file mode 100644 index 7980ba7..0000000 --- a/src/components/PackageSidebar/ModuleContentPlaceholder/style.scss +++ /dev/null @@ -1,8 +0,0 @@ -@import '../../../styles/variables'; - -.emptyPlaceholder { - text-align: center; - margin: 20px 0; - font-size: $font-size-base; - color: $greyChateau; -} diff --git a/src/components/PackageSidebar/index.jsx b/src/components/PackageSidebar/index.jsx deleted file mode 100644 index 19db617..0000000 --- a/src/components/PackageSidebar/index.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import get from 'lodash/get'; -import LastSync from './modules/LastSync'; -import DistTags from './modules/DistTags'; -import Maintainers from './modules/Maintainers'; -import Dependencies from './modules/Dependencies'; -import PeerDependencies from './modules/PeerDependencies'; -import Infos from './modules/Infos'; - -import { - formatLicense, - formatRepository, - getLastUpdatedPackageTime, - getRecentReleases, -} from '../../utils/package'; -import API from '../../utils/api'; -import {DIST_TAGS} from '../../../lib/constants'; - -export default class PackageSidebar extends React.Component { - state = {}; - - static propTypes = { - packageName: PropTypes.string.isRequired, - }; - - constructor(props) { - super(props); - this.loadPackageData = this.loadPackageData.bind(this); - } - - async componentDidMount() { - const { packageName } = this.props; - await this.loadPackageData(packageName); - } - - async loadPackageData(packageName) { - let packageMeta; - - try { - packageMeta = await API.request(`sidebar/${packageName}`, 'GET'); - } catch (err) { - this.setState({ - failed: true, - }); - } - - this.setState({ - packageMeta, - }); - } - - render() { - const { packageMeta } = this.state; - - if (packageMeta) { - const {time, _uplinks} = packageMeta; - - // Infos component - const license = formatLicense(get(packageMeta, 'latest.license', null)); - const repository = formatRepository( - get(packageMeta, 'latest.repository', null) - ); - const homepage = get(packageMeta, 'latest.homepage', null); - - // dist-tags - const distTags = packageMeta[DIST_TAGS]; - - // Lastsync component - const recentReleases = getRecentReleases(time); - const lastUpdated = getLastUpdatedPackageTime(_uplinks); - - // Dependencies component - const dependencies = get(packageMeta, 'latest.dependencies', {}); - const peerDependencies = get(packageMeta, 'latest.peerDependencies', {}); - - // Maintainers component - return ( - - ); - } - return ( - - ); - } -} diff --git a/src/components/PackageSidebar/modules/Dependencies/index.jsx b/src/components/PackageSidebar/modules/Dependencies/index.jsx deleted file mode 100644 index d2bb778..0000000 --- a/src/components/PackageSidebar/modules/Dependencies/index.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Module from '../../Module'; - -import {getDetailPageURL} from '../../../../utils/url'; -import ModuleContentPlaceholder from '../../ModuleContentPlaceholder'; - -import classes from './style.scss'; - -export const NO_DEPENDENCIES = 'Zero Dependencies!'; -export const DEP_ITEM_CLASS = 'dependency-item'; - -const renderDependenciesList = (dependencies, dependenciesList) => { - return ( -
    - {dependenciesList.map((dependenceName, index) => { - return ( -
  • - {dependenceName} - {index < dependenciesList.length - 1 && {', '}} -
  • - ); - })} -
- ); -}; - -const Dependencies = ({dependencies = {}, title = 'Dependencies'}) => { - const dependenciesList = Object.keys(dependencies); - return ( - - {dependenciesList.length > 0 ? ( - renderDependenciesList(dependencies, dependenciesList) - ) : ( - - )} - - ); -}; - -Dependencies.propTypes = { - dependencies: PropTypes.object, - title: PropTypes.string, -}; - -export default Dependencies; diff --git a/src/components/PackageSidebar/modules/Dependencies/style.scss b/src/components/PackageSidebar/modules/Dependencies/style.scss deleted file mode 100644 index 8c8f84a..0000000 --- a/src/components/PackageSidebar/modules/Dependencies/style.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import '../../../../styles/variables'; - -.dependenciesModule { - li { - display: inline-block; - font-size: $font-size-sm; - line-height: $line-height-xxs; - - a { - color: inherit; - } - } -} diff --git a/src/components/PackageSidebar/modules/DistTags/index.jsx b/src/components/PackageSidebar/modules/DistTags/index.jsx deleted file mode 100644 index 48f8cd1..0000000 --- a/src/components/PackageSidebar/modules/DistTags/index.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import propTypes from 'prop-types'; -import Module from '../../Module'; -import ModuleContentPlaceholder from '../../ModuleContentPlaceholder'; - -import classes from './style.scss'; - -const renderDistTags = (distTags) => { - - const tags = Object.entries(distTags); - - return ( -
    - {tags.map((tagItem) => { - const [tag, version] = tagItem; - - return ( -
  • - {tag} - {version} -
  • - ); - })} -
- ); -}; - -const DistTags = ({distTags = {}}) => { - const hasTags = Object.keys(distTags).length > 0; - - return ( - - {hasTags ? ( - renderDistTags(distTags) - ) : ( - - )} - - ); -}; - -DistTags.propTypes = { - distTags: propTypes.object, -}; - -export default DistTags; diff --git a/src/components/PackageSidebar/modules/DistTags/style.scss b/src/components/PackageSidebar/modules/DistTags/style.scss deleted file mode 100644 index f7823cc..0000000 --- a/src/components/PackageSidebar/modules/DistTags/style.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import '../../../../styles/variables'; - -.releasesModule { - li { - display: flex; - font-size: $font-size-sm; - line-height: $line-height-xs; - - span:last-child { - margin-left: auto; - } - } -} diff --git a/src/components/PackageSidebar/modules/Infos/index.jsx b/src/components/PackageSidebar/modules/Infos/index.jsx deleted file mode 100644 index 14733b8..0000000 --- a/src/components/PackageSidebar/modules/Infos/index.jsx +++ /dev/null @@ -1,40 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Module from '../../Module'; -import ModuleContentPlaceholder from '../../ModuleContentPlaceholder'; - -import classes from './style.scss'; - -const renderSection = (title, url) => ( -
  • - {title} - - {url} - -
  • -); - -const Infos = ({homepage, repository, license}) => { - const showInfo = homepage || repository || license; - return ( - - {showInfo ? ( -
      - {homepage && renderSection('Homepage', homepage)} - {repository && renderSection('Repository', repository)} - {license && ( -
    • - {'License'} - {license} -
    • )} -
    ) : } -
    ); -}; - -Infos.propTypes = { - homepage: PropTypes.string, - repository: PropTypes.string, - license: PropTypes.string, -}; - -export default Infos; diff --git a/src/components/PackageSidebar/modules/Infos/style.scss b/src/components/PackageSidebar/modules/Infos/style.scss deleted file mode 100644 index 13c8aa2..0000000 --- a/src/components/PackageSidebar/modules/Infos/style.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import '../../../../styles/variables'; -@import '../../../../styles/mixins'; - -.infosModule { - li { - display: flex; - font-size: $font-size-sm; - line-height: $line-height-xs; - - a { - color: inherit; - max-width: 150px; - @include ellipsis; - } - - a:last-child, - span:last-child { - margin-left: auto; - } - } -} diff --git a/src/components/PackageSidebar/modules/LastSync/index.jsx b/src/components/PackageSidebar/modules/LastSync/index.jsx deleted file mode 100644 index 3730b8e..0000000 --- a/src/components/PackageSidebar/modules/LastSync/index.jsx +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import propTypes from 'prop-types'; -import Module from '../../Module'; -import ModuleContentPlaceholder from '../../ModuleContentPlaceholder'; - -import classes from './style.scss'; - -const renderRecentReleases = (recentReleases) => ( -
      - {recentReleases.map((versionInfo) => { - const {version, time} = versionInfo; - return ( -
    • - {version} - {time} -
    • - ); - })} -
    -); - -const LastSync = ({recentReleases = [], lastUpdated = ''}) => { - return ( - - {recentReleases.length ? ( - renderRecentReleases(recentReleases) - ) : ( - - )} - - ); -}; - -LastSync.propTypes = { - recentReleases: propTypes.array, - lastUpdated: propTypes.string, -}; - -export default LastSync; diff --git a/src/components/PackageSidebar/modules/LastSync/style.scss b/src/components/PackageSidebar/modules/LastSync/style.scss deleted file mode 100644 index f7823cc..0000000 --- a/src/components/PackageSidebar/modules/LastSync/style.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import '../../../../styles/variables'; - -.releasesModule { - li { - display: flex; - font-size: $font-size-sm; - line-height: $line-height-xs; - - span:last-child { - margin-left: auto; - } - } -} diff --git a/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/index.jsx b/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/index.jsx deleted file mode 100644 index 5975e30..0000000 --- a/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/index.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import classes from './style.scss'; - -const MaintainerInfo = ({title, name, avatar}) => { - const avatarDescription = `${title} ${name}'s avatar`; - return ( -
    - {avatarDescription} - {name} -
    - ); -}; - -MaintainerInfo.propTypes = { - title: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - avatar: PropTypes.string.isRequired, -}; - -export default MaintainerInfo; diff --git a/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/style.scss b/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/style.scss deleted file mode 100644 index dc1805d..0000000 --- a/src/components/PackageSidebar/modules/Maintainers/MaintainerInfo/style.scss +++ /dev/null @@ -1,26 +0,0 @@ -@import '../../../../../styles/variables'; -@import '../../../../../styles/mixins'; - -.maintainer { - display: flex; - line-height: $line-height-xl; - cursor: default; - - &:not(:last-child) { - margin-bottom: 10px; - } - - img { - width: 30px; - height: 30px; - margin-right: 10px; - border-radius: 100%; - flex-shrink: 0; - } - - span { - font-size: $font-size-sm; - flex-shrink: 1; - @include ellipsis; - } -} diff --git a/src/components/PackageSidebar/modules/Maintainers/index.jsx b/src/components/PackageSidebar/modules/Maintainers/index.jsx deleted file mode 100644 index bbdea22..0000000 --- a/src/components/PackageSidebar/modules/Maintainers/index.jsx +++ /dev/null @@ -1,122 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import get from 'lodash/get'; -import filter from 'lodash/filter'; -import size from 'lodash/size'; -import has from 'lodash/has'; -import uniqBy from 'lodash/uniqBy'; - -import Module from '../../Module'; -import MaintainerInfo from './MaintainerInfo'; -import ModuleContentPlaceholder from '../../ModuleContentPlaceholder'; - -import classes from './style.scss'; - -const CONTRIBUTORS_TO_SHOW = 5; - -export default class Maintainers extends React.Component { - static propTypes = { - packageMeta: PropTypes.object.isRequired, - }; - - state = {}; - - constructor(props) { - super(props); - this.handleShowAllContributors = this.handleShowAllContributors.bind(this); - } - - get author() { - return get(this, 'props.packageMeta.latest.author'); - } - - get contributors() { - const contributors = get(this, 'props.packageMeta.latest.contributors', {}); - return filter(contributors, (contributor) => { - return ( - contributor.name !== get(this, 'author.name') && - contributor.email !== get(this, 'author.email') - ); - }); - } - - get showAllContributors() { - const { showAllContributors } = this.state; - return showAllContributors || size(this.contributors) <= 5; - } - - get uniqueContributors() { - if (!this.contributors) { - return []; - } - - return uniqBy(this.contributors, (contributor) => contributor.name).slice( - 0, - CONTRIBUTORS_TO_SHOW - ); - } - - handleShowAllContributors() { - this.setState({ - showAllContributors: true, - }); - } - - renderContributors() { - if (!this.contributors) return null; - - return (this.showAllContributors - ? this.contributors - : this.uniqueContributors - ).map((contributor, index) => { - return ( - - ); - }); - } - - renderAuthorAndContributors(author) { - return ( -
    -
      - {author && - author.name && ( - - )} - {this.renderContributors()} -
    - {!this.showAllContributors && ( - - )} -
    - ); - } - - render() { - const contributors = this.renderContributors(); - return ( - - {contributors.length || has(this.author, 'name') ? ( - this.renderAuthorAndContributors(this.author) - ) : ( - - )} - - ); - } -} diff --git a/src/components/PackageSidebar/modules/Maintainers/style.scss b/src/components/PackageSidebar/modules/Maintainers/style.scss deleted file mode 100644 index 6294904..0000000 --- a/src/components/PackageSidebar/modules/Maintainers/style.scss +++ /dev/null @@ -1,13 +0,0 @@ -@import '../../../../styles/variables'; - -.maintainersModule { - .showAllContributors { - cursor: pointer; - width: 100%; - background: none; - border: none; - font-size: $font-size-sm; - text-align: center; - padding: 10px 0; - } -} diff --git a/src/components/PackageSidebar/modules/PeerDependencies/index.jsx b/src/components/PackageSidebar/modules/PeerDependencies/index.jsx deleted file mode 100644 index 287073f..0000000 --- a/src/components/PackageSidebar/modules/PeerDependencies/index.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import Dependencies from '../Dependencies'; - -export const TITLE = 'Peer Dependencies'; - -const PeerDependencies = ({dependencies = {}, title = TITLE}) => { - return ( - - ); -}; - -PeerDependencies.propTypes = { - dependencies: PropTypes.object, - title: PropTypes.string, -}; - -export default PeerDependencies; diff --git a/src/components/Repository/img/git.png b/src/components/Repository/img/git.png new file mode 100644 index 0000000..51f4ae5 Binary files /dev/null and b/src/components/Repository/img/git.png differ diff --git a/src/components/Repository/index.js b/src/components/Repository/index.js index ded0216..68c2239 100644 --- a/src/components/Repository/index.js +++ b/src/components/Repository/index.js @@ -1,61 +1,59 @@ -/* eslint no-unused-vars: 0 */ /* eslint react/jsx-max-depth: 0 */ -import React, {Component, Fragment} from 'react'; +import React, {Component} from 'react'; +import Avatar from '@material-ui/core/Avatar'; +import List from '@material-ui/core/List'; +import ListItemText from '@material-ui/core/ListItemText'; import { DetailContextConsumer } from '../../pages/version/index'; -import Card from '@material-ui/core/Card/index'; -import CardContent from '@material-ui/core/CardContent/index'; -import Grid from '@material-ui/core/Grid/index'; -import GitHub from '../../icons/GitHub'; import CopyToClipBoard from '../CopyToClipBoard'; -import BugReport from '@material-ui/icons/BugReport'; -import CardActions from '@material-ui/core/CardActions/index'; -import Button from '@material-ui/core/Button'; -import {GridRepo} from './styles'; + +import { Heading, GithubLink, RepositoryListItem } from './styles'; +import git from './img/git.png'; class Repository extends Component { render() { return ( {(context) => { - return this.renderAuthor(context); + return this.renderRepository(context); }} ); }; - renderAuthor = ({packageMeta}) => { - const { repository, bugs } = packageMeta.latest; - if (!repository) { + renderRepositoryText(url) { + return ({url}); + } + + renderRepository = ({packageMeta}) => { + const { + repository: { + url, + } = {}, + } = packageMeta.latest; + + if (!url) { return null; } return ( - - - - {this.renderRepository(repository, bugs)} - - - - - - - + <> + {'Repository'}}> + + + + + + ); } - - renderRepository = ({url, type}, bugs) => { + + renderContent(url) { return ( - - - - - - - - + + {this.renderRepositoryText(url)} + ); } } diff --git a/src/components/Repository/styles.js b/src/components/Repository/styles.js index 8533347..12eddd1 100644 --- a/src/components/Repository/styles.js +++ b/src/components/Repository/styles.js @@ -5,9 +5,42 @@ import styled from 'react-emotion'; import Grid from '@material-ui/core/Grid/index'; +import ListItem from '@material-ui/core/ListItem/index'; +import Typography from '@material-ui/core/Typography/index'; + +import Github from '../../icons/GitHub'; +import colors from '../../utils/styles/colors'; + +export const Heading = styled(Typography)` + && { + font-weight: 700; + text-transform: capitalize; + } +`; export const GridRepo = styled(Grid)` && { align-items: center; } `; + +export const GithubLink = styled('a')` + && { + color: ${colors.primary}; + } +`; + +export const GithubLogo = styled(Github)` + && { + font-size: 40px; + color: ${colors.primary}; + background-color: ${colors.greySuperLight}; + } +`; + +export const RepositoryListItem = styled(ListItem)` + && { + padding-left: 0; + padding-right: 0; + } +`; diff --git a/src/components/Search/index.js b/src/components/Search/index.js index 7fcc752..a013c71 100644 --- a/src/components/Search/index.js +++ b/src/components/Search/index.js @@ -100,7 +100,7 @@ export class Search extends Component { case 'enter': this.setState({ search: '' }); // $FlowFixMe - history.push(`/-/web/version/${suggestionValue}`); + history.push(`/-/web/detail/${suggestionValue}`); break; } }; diff --git a/src/components/Tag/styles.js b/src/components/Tag/styles.js index a6eea8f..cd624ed 100644 --- a/src/components/Tag/styles.js +++ b/src/components/Tag/styles.js @@ -11,10 +11,10 @@ export const Wrapper = styled.span` vertical-align: middle; line-height: 22px; border-radius: 2px; - color: #9f9f9f; - background-color: hsla(0, 0%, 51%, 0.1); + color: #485a3e; + background-color: #f3f4f2; padding: 0.22rem 0.4rem; - margin: 5px 10px 0 0; + margin: 8px 8px 0 0; ${ellipsis('300px')}; } `; diff --git a/src/components/UpLinks/index.js b/src/components/UpLinks/index.js index 13bf1d3..8756a07 100644 --- a/src/components/UpLinks/index.js +++ b/src/components/UpLinks/index.js @@ -1,13 +1,15 @@ /** * @prettier */ - -import { DetailContextConsumer } from '../../pages/version/index'; -import { formatDateDistance } from '../../utils/package'; -import { Heading, Spacer, ListItemText } from './styles'; +import React from 'react'; import List from '@material-ui/core/List/index'; import ListItem from '@material-ui/core/ListItem/index'; -import React from 'react'; + +import { DetailContextConsumer } from '../../pages/version/index'; +import NoItems from '../NoItems'; +import { formatDateDistance } from '../../utils/package'; + +import { Heading, Spacer, ListItemText } from './styles'; class UpLinks extends React.PureComponent { render() { @@ -15,7 +17,7 @@ class UpLinks extends React.PureComponent { // $FlowFixMe {({ packageMeta }) => { - return this.renderContent(packageMeta._uplinks); + return this.renderContent(packageMeta._uplinks, packageMeta.latest); }} ); @@ -35,15 +37,18 @@ class UpLinks extends React.PureComponent { ); - renderContent(uplinks) { - return ( - uplinks && ( - <> - {'Uplinks'} - {this.renderUpLinksList(uplinks)} - - ) - ); + renderContent(uplinks, { name }) { + if (Object.keys(uplinks).length > 0) { + return ( + uplinks && ( + <> + {'Uplinks'} + {this.renderUpLinksList(uplinks)} + + ) + ); + } + return ; } } diff --git a/src/components/Versions/index.js b/src/components/Versions/index.js index 7dfe711..73496cf 100644 --- a/src/components/Versions/index.js +++ b/src/components/Versions/index.js @@ -17,40 +17,44 @@ class Versions extends React.PureComponent { // $FlowFixMe {({ packageMeta }) => { - return this.renderContent(packageMeta[DIST_TAGS], packageMeta.versions); + return this.renderContent(packageMeta); }} ); } - renderPackageList = (packages: any, isVersion: boolean = false) => ( - - {Object.keys(packages) - .reverse() - .map(version => ( - - {version} - - {isVersion ? `${formatDateDistance('2017-10-26T09:03:15.044Z')} ago` : packages[version]} - - ))} - - ); + renderPackageList = (packages: any, isVersion: boolean = false, timeMap: Object = {}) => { + return ( + + {Object.keys(packages) + .reverse() + .map(version => ( + + {version} + + {isVersion && timeMap[version] ? `${formatDateDistance(timeMap[version])} ago` : packages[version]} + + ))} + + ); + }; // $FlowFixMe - renderContent(distTags: object, versions: object) { + renderContent(packageMeta) { + const { versions = {}, time: timeMap = {}, [DIST_TAGS]: distTags = {} } = packageMeta; + return ( <> {distTags && ( <> {'Current Tags'} - {this.renderPackageList(distTags)} + {this.renderPackageList(distTags, false, timeMap)} )} {versions && ( <> {'Version History'} - {this.renderPackageList(versions, true)} + {this.renderPackageList(versions, true, timeMap)} )} diff --git a/src/history.js b/src/history.js index d000bcb..f8407b0 100644 --- a/src/history.js +++ b/src/history.js @@ -2,14 +2,8 @@ * @prettier */ -import {createBrowserHistory} from 'history'; +import { createBrowserHistory } from 'history'; const history = createBrowserHistory(); -// Listen for changes to the current location. -history.listen((location, action) => { - // location is an object like window.location - console.log('====>', action, location.pathname, location.state); -}); - export default history; diff --git a/src/index.js b/src/index.js index c1c9331..136c63c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,12 +1,14 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { AppContainer } from "react-hot-loader"; +import './utils/__setPublicPath__'; -import App from "./app"; +import React from 'react'; +import ReactDOM from 'react-dom'; +import {AppContainer} from 'react-hot-loader'; -const rootNode = document.getElementById("root"); +import App from './app'; -const renderApp = Component => { +const rootNode = document.getElementById('root'); + +const renderApp = (Component) => { ReactDOM.render( @@ -18,7 +20,7 @@ const renderApp = Component => { renderApp(App); if (module.hot) { - module.hot.accept("./app", () => { + module.hot.accept('./app', () => { renderApp(App); }); } diff --git a/src/pages/detail/detail.scss b/src/pages/detail/detail.scss deleted file mode 100644 index 849a7f6..0000000 --- a/src/pages/detail/detail.scss +++ /dev/null @@ -1,25 +0,0 @@ -@import '../../styles/variables'; -@import '../../styles/mixins'; - -.twoColumn { - @include container-size; - display: flex; - - > div { - &:first-child { - flex-shrink: 1; - min-width: 300px; - width: 100%; - } - } - - > aside { - &:last-child { - margin-left: auto; - - padding-left: 15px; - flex-shrink: 0; - width: 285px; - } - } -} diff --git a/src/pages/detail/index.jsx b/src/pages/detail/index.jsx deleted file mode 100644 index bc526a4..0000000 --- a/src/pages/detail/index.jsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import isEmpty from 'lodash/isEmpty'; - -import PackageDetail from '../../components/PackageDetail'; -import NotFound from '../../components/NotFound'; -import Spinner from '../../components/Spinner'; -import API from '../../utils/api'; - -import classes from './detail.scss'; -import PackageSidebar from '../../components/PackageSidebar/index'; - -export default class Detail extends Component { - static propTypes = { - match: PropTypes.object, - isUserLoggedIn: PropTypes.bool, - }; - - state = { - readMe: '', - notFound: false, - }; - - getPackageName(props = this.props) { - const params = props.match.params; - return `${(params.scope && '@' + params.scope + '/') || ''}${ - params.package - }`; - } - - get packageName() { - return this.getPackageName(); - } - - async componentDidMount() { - await this.loadPackageInfo(this.packageName); - } - - componentDidUpdate(prevProps) { - const { isUserLoggedIn, match } = this.props; - const condition1 = prevProps.isUserLoggedIn !== isUserLoggedIn; - const condition2 = - prevProps.match.params.package !== match.params.package; - if (condition1 || condition2) { - const packageName = this.getPackageName(this.props); - this.loadPackageInfo(packageName); - } - } - - async loadPackageInfo(packageName) { - this.setState({ - readMe: '', - }); - - try { - const resp = await API.request(`package/readme/${packageName}`, 'GET'); - this.setState({ - readMe: resp, - notFound: false, - }); - } catch (err) { - this.setState({ - notFound: true, - }); - } - } - - render() { - const { notFound, readMe } = this.state; - - if (notFound) { - return ( -
    - -
    - ); - } else if (isEmpty(readMe)) { - return ; - } - return ( -
    - - -
    - ); - } -} diff --git a/src/pages/home/index.js b/src/pages/home/index.js index 94530f8..e2661b3 100644 --- a/src/pages/home/index.js +++ b/src/pages/home/index.js @@ -1,4 +1,4 @@ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import PackageList from '../../components/PackageList'; @@ -10,10 +10,10 @@ class Home extends Component { }; render() { - const {packages} = this.props; + const { packages } = this.props; return ( -
    - +
    +
    ); } diff --git a/src/pages/version/index.js b/src/pages/version/index.js index e40ff3f..ad7cefb 100644 --- a/src/pages/version/index.js +++ b/src/pages/version/index.js @@ -3,13 +3,14 @@ * @flow */ -import React, {Component} from 'react'; +import React, { Component } from 'react'; import Grid from '@material-ui/core/Grid/index'; import Loading from '../../components/Loading'; import DetailContainer from '../../components/DetailContainer'; import DetailSidebar from '../../components/DetailSidebar'; -import {callDetailPage} from '../../utils/calls'; -import {getRouterPackageName} from '../../utils/package'; +import { callDetailPage } from '../../utils/calls'; +import { getRouterPackageName } from '../../utils/package'; +import NotFound from '../../components/NotFound'; export const DetailContext = React.createContext(); @@ -35,9 +36,9 @@ class VersionPage extends Component { /* eslint no-unused-vars: 0 */ async componentDidUpdate(nextProps: any, prevState: any) { - const {packageName} = this.state; + const { packageName } = this.state; if (packageName !== prevState.packageName) { - const {readMe, packageMeta} = await callDetailPage(packageName); + const { readMe, packageMeta } = await callDetailPage(packageName); this.setState({ readMe, packageMeta, @@ -49,7 +50,7 @@ class VersionPage extends Component { } static getDerivedStateFromProps(nextProps: any, prevState: any) { - const {match} = nextProps; + const { match } = nextProps; const packageName = getRouterPackageName(match); if (packageName !== prevState.packageName) { @@ -70,7 +71,7 @@ class VersionPage extends Component { } async loadPackageInfo() { - const {packageName} = this.state; + const { packageName } = this.state; // FIXME: use utility document.title = `Verdaccio - ${packageName}`; @@ -79,7 +80,7 @@ class VersionPage extends Component { }); try { - const {readMe, packageMeta} = await callDetailPage(packageName); + const { readMe, packageMeta } = await callDetailPage(packageName); this.setState({ readMe, packageMeta, @@ -103,11 +104,15 @@ class VersionPage extends Component { }; render() { - const {isLoading, packageMeta, readMe, packageName} = this.state; + const { isLoading, packageMeta, readMe, packageName } = this.state; - if (isLoading === false) { + if (isLoading) { + return ; + } else if (!packageMeta) { + return ; + } else { return ( - + {this.renderDetail()} @@ -118,8 +123,6 @@ class VersionPage extends Component { ); - } else { - return ; } } diff --git a/src/router.js b/src/router.js index 915d919..53aaeb8 100644 --- a/src/router.js +++ b/src/router.js @@ -5,19 +5,17 @@ /* eslint react/jsx-max-depth:0 */ -import React, { Component, Fragment } from "react"; -import { Router, Route, Switch } from "react-router-dom"; -import { AppContextConsumer } from "./app"; +import React, { Component, Fragment } from 'react'; +import { Router, Route, Switch } from 'react-router-dom'; +import { AppContextConsumer } from './app'; -// import {asyncComponent} from '../others/utils/asyncComponent'; -import history from "./history"; -import Header from "./components/Header"; -import HomePage from "./pages/home"; -import NotFound from './components/NotFound' -// const NotFound = asyncComponent(() => import("./components/NotFound")); -// const DetailPackage = asyncComponent(() => import("./pages/detail")); -// const VersionPackage = asyncComponent(() => import("./pages/version")); -// const HomePage = asyncComponent(() => import("./pages/home")); +import { asyncComponent } from './utils/asyncComponent'; +import history from './history'; +import Header from './components/Header'; + +const NotFound = asyncComponent(() => import('./components/NotFound')); +const VersionPackage = asyncComponent(() => import('./pages/version')); +const HomePage = asyncComponent(() => import('./pages/home')); class RouterApp extends Component { render() { @@ -26,11 +24,9 @@ class RouterApp extends Component { {this.renderHeader()} - - {/* - - - */} + + + @@ -44,15 +40,7 @@ class RouterApp extends Component { return ( {function renderConsumerVersionPage({ logoUrl, scope, user }) { - return ( -
    - ); + return
    ; }} ); @@ -62,21 +50,7 @@ class RouterApp extends Component { return ( {function renderConsumerVersionPage({ isUserLoggedIn, packages }) { - return ( - - ); - }} - - ); - }; - - renderDetailPage = (routerProps: any) => { - return ( - - {function renderConsumerVersionPage({ isUserLoggedIn }) { - return ( - - ); + return ; }} ); @@ -86,9 +60,7 @@ class RouterApp extends Component { return ( {function renderConsumerVersionPage({ isUserLoggedIn }) { - return ( - - ); + return ; }} ); diff --git a/src/styles/mixins.scss b/src/styles/mixins.scss index 17bcb83..2b2bc0e 100644 --- a/src/styles/mixins.scss +++ b/src/styles/mixins.scss @@ -37,9 +37,10 @@ } @mixin container-size { - margin-left: auto; - margin-right: auto; @media screen and (min-width: $break-lg) { max-width: $break-lg; + width: 100%; + margin-left: auto; + margin-right: auto; } } diff --git a/src/utils/__setPublicPath__.js b/src/utils/__setPublicPath__.js index ad0f2d9..f528199 100644 --- a/src/utils/__setPublicPath__.js +++ b/src/utils/__setPublicPath__.js @@ -1,3 +1,3 @@ if (!__DEBUG__) { - __webpack_public_path__ = window.VERDACCIO_API_URL.replace(/\/verdaccio\/$/, '/static/'); // eslint-disable-line + __webpack_public_path__ = window.VERDACCIO_API_URL.replace(/\/verdaccio\/$/, '/static/') // eslint-disable-line } diff --git a/src/utils/api.js b/src/utils/api.js index 2d23a67..20b0faf 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -2,48 +2,48 @@ import storage from './storage'; class API { request(url, method = 'GET', options = {}) { - if (!window.VERDACCIO_API_URL) { - throw new Error('VERDACCIO_API_URL is not defined!'); - } + if (!window.VERDACCIO_API_URL) { + throw new Error('VERDACCIO_API_URL is not defined!'); + } - const token = storage.getItem('token'); - if (token) { - if (!options.headers) options.headers = {}; + const token = storage.getItem('token'); + if (token) { + if (!options.headers) options.headers = {}; - options.headers.authorization = `Bearer ${token}`; - } + options.headers.authorization = `Bearer ${token}`; + } - if (!['http://', 'https://', '//'].some((prefix) => url.startsWith(prefix))) { - url = window.VERDACCIO_API_URL + url; - } + if (!['http://', 'https://', '//'].some((prefix) => url.startsWith(prefix))) { + url = window.VERDACCIO_API_URL + url; + } - /** - * Handles response according to content type - * @param {object} response - * @returns {promise} - */ - function handleResponseType(response) { - if (response.headers) { - const contentType = response.headers.get('Content-Type'); - if (contentType.includes('application/pdf')) { - return Promise.all([response.ok, response.blob()]); - } - if (contentType.includes('application/json')) { - return Promise.all([response.ok, response.json()]); - } - // it includes all text types - if (contentType.includes('text/')) { - return Promise.all([response.ok, response.text()]); + /** + * Handles response according to content type + * @param {object} response + * @returns {promise} + */ + function handleResponseType(response) { + if (response.headers) { + const contentType = response.headers.get('Content-Type'); + if (contentType.includes('application/pdf')) { + return Promise.all([response.ok, response.blob()]); + } + if (contentType.includes('application/json')) { + return Promise.all([response.ok, response.json()]); + } + // it includes all text types + if (contentType.includes('text/')) { + return Promise.all([response.ok, response.text()]); + } } } - } - return new Promise((resolve, reject) => { - fetch(url, { - method, - credentials: 'same-origin', - ...options, - }) + return new Promise((resolve, reject) => { + fetch(url, { + method, + credentials: 'same-origin', + ...options, + }) .then(handleResponseType) .then(([responseOk, body]) => { if (responseOk) { @@ -52,11 +52,11 @@ class API { reject(body); } }) - .catch((error) => { + .catch(error => { reject(error); }); - }); - } + }); + } } export default new API(); diff --git a/src/utils/asyncComponent.js b/src/utils/asyncComponent.js index 6ffa7b1..7aadf71 100644 --- a/src/utils/asyncComponent.js +++ b/src/utils/asyncComponent.js @@ -1,24 +1,32 @@ +/** + * @prettier + */ + import React from 'react'; export function asyncComponent(getComponent) { return class AsyncComponent extends React.Component { static Component = null; - state = {Component: AsyncComponent.Component}; + state = { Component: AsyncComponent.Component }; componentDidMount() { - const {Component} = this.state; - + const { Component } = this.state; if (!Component) { - getComponent().then(({default: Component}) => { - AsyncComponent.Component = Component; - /* eslint react/no-did-mount-set-state:0 */ - this.setState({Component}); - }); + getComponent() + .then(({ default: Component }) => { + AsyncComponent.Component = Component; + /* eslint react/no-did-mount-set-state:0 */ + this.setState({ Component }); + }) + .catch(err => { + console.error(err); + }); } } render() { - const {Component} = this.state; + const { Component } = this.state; if (Component) { + // eslint-disable-next-line verdaccio/jsx-spread return ; } diff --git a/src/utils/file-size.js b/src/utils/file-size.js new file mode 100644 index 0000000..5281a57 --- /dev/null +++ b/src/utils/file-size.js @@ -0,0 +1,5 @@ +export default function fileSizeSI(a, b, c, d, e) { + return (b = Math, c = b.log, d = 1e3, e = c(a) / c(d) | 0, a / b.pow(d, e)).toFixed(2) + + ' ' + (e ? 'kMGTPEZY'[--e] + 'B' : 'Bytes'); +}; + diff --git a/src/utils/login.js b/src/utils/login.js index 0dfe646..a51083d 100644 --- a/src/utils/login.js +++ b/src/utils/login.js @@ -1,76 +1,70 @@ -import isString from "lodash/isString"; -import isNumber from "lodash/isNumber"; -import isEmpty from "lodash/isEmpty"; -import { Base64 } from "js-base64"; -import API from "./api"; - -const HEADERS = { - JSON: "application/json", - JSON_CHARSET: "application/json; charset=utf-8", - OCTET_STREAM: "application/octet-stream; charset=utf-8", - TEXT_CHARSET: "text/plain; charset=utf-8", - GZIP: "gzip" -}; +import isString from 'lodash/isString'; +import isNumber from 'lodash/isNumber'; +import isEmpty from 'lodash/isEmpty'; +import {Base64} from 'js-base64'; +import API from './api'; +import {HEADERS} from '../../lib/constants'; export function isTokenExpire(token) { - if (!isString(token)) { - return true; - } + if (!isString(token)) { + return true; + } - let [, payload] = token.split("."); + let [,payload] = token.split('.'); - if (!payload) { - return true; - } + if (!payload) { + return true; + } - try { - payload = JSON.parse(Base64.decode(payload)); - } catch (error) { - // eslint-disable-next-line - console.error("Invalid token:", error, token); - return true; - } + try { + payload = JSON.parse(Base64.decode(payload)); + } catch (error) { + // eslint-disable-next-line + console.error('Invalid token:', error, token); + return true; + } - if (!payload.exp || !isNumber(payload.exp)) { - return true; - } - // Report as expire before (real expire time - 30s) - const jsTimestamp = payload.exp * 1000 - 30000; - const expired = Date.now() >= jsTimestamp; + if (!payload.exp || !isNumber(payload.exp)) { + return true; + } + // Report as expire before (real expire time - 30s) + const jsTimestamp = (payload.exp * 1000) - 30000; + const expired = Date.now() >= jsTimestamp; - return expired; + return expired; } + export async function makeLogin(username, password) { - // checks isEmpty - if (isEmpty(username) || isEmpty(password)) { - const error = { - title: "Unable to login", - type: "error", - description: "Username or password can't be empty!" - }; - return { error }; - } + // checks isEmpty + if (isEmpty(username) || isEmpty(password)) { + const error = { + title: 'Unable to login', + type: 'error', + description: 'Username or password can\'t be empty!', + }; + return {error}; + } - try { - const response = await API.request("login", "POST", { - body: JSON.stringify({ username, password }), - headers: { - Accept: HEADERS.JSON, - "Content-Type": HEADERS.JSON - } - }); - const result = { - username: response.username, - token: response.token - }; - return result; - } catch (e) { - const error = { - title: "Unable to login", - type: "error", - description: e.error - }; - return { error }; - } + try { + const response = await API.request('login', 'POST', { + body: JSON.stringify({username, password}), + headers: { + Accept: HEADERS.JSON, + 'Content-Type': HEADERS.JSON, + }, + }); + const result = { + username: response.username, + token: response.token, + }; + return result; + } catch (e) { + const error = { + title: 'Unable to login', + type: 'error', + description: e.error, + }; + return {error}; + } } diff --git a/src/utils/package.js b/src/utils/package.js index b3d18e1..3e26213 100644 --- a/src/utils/package.js +++ b/src/utils/package.js @@ -4,7 +4,6 @@ import format from 'date-fns/format'; import distanceInWordsToNow from 'date-fns/distance_in_words_to_now'; export const TIMEFORMAT = 'DD.MM.YYYY, HH:mm:ss'; -export const DEFAULT_USER = 'Anonymous'; /** * Formats license field for webui. @@ -39,36 +38,6 @@ export function formatRepository(repository) { } -/** - * Formats author field for webui. - * @see https://docs.npmjs.com/files/package.json#author - */ -export function formatAuthor(author) { - let authorDetails = { - name: DEFAULT_USER, - email: '', - avatar: '', - }; - - if (isString(author)) { - authorDetails = { - ...authorDetails, - name: author ? author : authorDetails.name, - }; - } - - if (isObject(author)) { - authorDetails = { - ...authorDetails, - name: author.name ? author.name : authorDetails.name, - email: author.email ? author.email : authorDetails.email, - avatar: author.avatar ? author.avatar : authorDetails.avatar, - }; - } - - return authorDetails; -} - /** * For component * @param {array} uplinks diff --git a/src/utils/styles/colors.js b/src/utils/styles/colors.js index 4cd495f..fecfa9c 100644 --- a/src/utils/styles/colors.js +++ b/src/utils/styles/colors.js @@ -10,7 +10,10 @@ const colors = { grey: '#808080', greySuperLight: '#f5f5f5', greyLight: '#d3d3d3', + greyLight2: '#908ba1', + greyLight3: '#f3f4f240', greyDark: '#a9a9a9', + greyDark2: '#586069', greyChateau: '#95989a', greyGainsboro: '#e3e3e3', greyAthens: '#d3dddd', diff --git a/src/utils/styles/media.js b/src/utils/styles/media.js index 3240d70..1c74025 100644 --- a/src/utils/styles/media.js +++ b/src/utils/styles/media.js @@ -1,6 +1,6 @@ import { css } from 'emotion'; -const breakpoints = { +export const breakpoints = { small: 576, medium: 768, large: 1024, diff --git a/src/utils/url.js b/src/utils/url.js index 5346870..d704be5 100644 --- a/src/utils/url.js +++ b/src/utils/url.js @@ -2,11 +2,3 @@ export function getRegistryURL() { // Don't add slash if it's not a sub directory return `${location.origin}${location.pathname === '/' ? '' : location.pathname}`; } - -/** - * Get specified package detail page url - * @param {string} packageName - */ -export function getDetailPageURL(packageName) { - return `${getRegistryURL()}/-/web/version/${packageName}`; -} diff --git a/src/utils/windows.js b/src/utils/windows.js index 2a15a2c..9fb3b37 100644 --- a/src/utils/windows.js +++ b/src/utils/windows.js @@ -1,3 +1,3 @@ export function goToVerdaccioWebsite() { - window.open('https://www.verdaccio.org/', '_blank'); + window.open('https://www.verdaccio.org/', '_blank'); }