mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-13 22:48:31 -05:00
chore: add sidebar experiment
This commit is contained in:
parent
8a168760f5
commit
b6a8dd37d0
14 changed files with 280 additions and 43 deletions
|
@ -160,7 +160,8 @@
|
||||||
"webpack-cli": "3.1.1",
|
"webpack-cli": "3.1.1",
|
||||||
"webpack-dev-server": "3.1.14",
|
"webpack-dev-server": "3.1.14",
|
||||||
"webpack-merge": "4.1.4",
|
"webpack-merge": "4.1.4",
|
||||||
"whatwg-fetch": "3.0.0"
|
"whatwg-fetch": "3.0.0",
|
||||||
|
"xss": "1.0.3"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"private",
|
"private",
|
||||||
|
|
49
src/webui/components/DetailContainer/index.js
Normal file
49
src/webui/components/DetailContainer/index.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
|
import { DetailContextConsumer } from '../../pages/version/index';
|
||||||
|
import Readme from '../Readme';
|
||||||
|
import {preventXSS} from '../../utils/sec-utils';
|
||||||
|
import Tabs from '@material-ui/core/Tabs/index';
|
||||||
|
import Tab from '@material-ui/core/Tab/index';
|
||||||
|
import { Content } from './styles';
|
||||||
|
|
||||||
|
class DetailContainer extends Component<any, any> {
|
||||||
|
state = {
|
||||||
|
tabPosition: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<DetailContextConsumer>
|
||||||
|
{(context) => {
|
||||||
|
return this.renderTabs(context);
|
||||||
|
}}
|
||||||
|
</DetailContextConsumer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderTabs = ({readMe}) => {
|
||||||
|
const { tabPosition } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Tabs indicatorColor={'primary'} onChange={this.handleChange} textColor={'primary'} value={tabPosition} variant={'fullWidth'}>
|
||||||
|
<Tab label={'Readme'} />
|
||||||
|
<Tab label={'Dependencies'} />
|
||||||
|
<Tab label={'Versions'} />
|
||||||
|
<Tab label={'Uplinks'} />
|
||||||
|
</Tabs>
|
||||||
|
{tabPosition === 0 && this.renderReadme(readMe)}
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderReadme = (readMe) => {
|
||||||
|
const encodedReadme = preventXSS(readMe);
|
||||||
|
|
||||||
|
return (<Content><Readme description={encodedReadme}></Readme></Content>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default DetailContainer;
|
12
src/webui/components/DetailContainer/styles.js
Normal file
12
src/webui/components/DetailContainer/styles.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styled from 'react-emotion';
|
||||||
|
|
||||||
|
export const Content = styled.div`
|
||||||
|
&& {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
`;
|
12
src/webui/components/DetailContainer/types.js
Normal file
12
src/webui/components/DetailContainer/types.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Node } from 'react';
|
||||||
|
|
||||||
|
export interface IProps {
|
||||||
|
children: Node;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
75
src/webui/components/DetailSidebar/index.js
Normal file
75
src/webui/components/DetailSidebar/index.js
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* eslint react/jsx-max-depth: 0 */
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
|
import { DetailContextConsumer } from '../../pages/version/index';
|
||||||
|
// import Paper from '@material-ui/core/Paper/index';
|
||||||
|
import Typography from '@material-ui/core/Typography/index';
|
||||||
|
import Grid from '@material-ui/core/Grid/index';
|
||||||
|
// import CardHeader from '@material-ui/core/CardHeader';
|
||||||
|
import Card from '@material-ui/core/Card/index';
|
||||||
|
import CardContent from '@material-ui/core/CardContent/index';
|
||||||
|
import { Content } from './styles';
|
||||||
|
import CopyToClipBoard from '../CopyToClipBoard';
|
||||||
|
import Button from '@material-ui/core/Button';
|
||||||
|
import CardActions from '@material-ui/core/CardActions';
|
||||||
|
// import Paper from '@material-ui/core/Paper/index';
|
||||||
|
|
||||||
|
class DetailSidebar extends Component<any, any> {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<DetailContextConsumer>
|
||||||
|
{(context) => {
|
||||||
|
return this.renderSideBar(context);
|
||||||
|
}}
|
||||||
|
</DetailContextConsumer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSideBar = ({packageMeta, packageName}) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.renderDescription(packageMeta, packageName)}
|
||||||
|
<Content>
|
||||||
|
<Grid container={true} spacing={24}>
|
||||||
|
<Grid item={true} xs={12}>
|
||||||
|
<Typography color={"textPrimary"} gutterBottom={true} variant={'title'}>
|
||||||
|
{packageName}
|
||||||
|
</Typography>
|
||||||
|
<Typography color={"textSecondary"} gutterBottom={true} variant={'subtitle2'}>
|
||||||
|
{packageMeta.latest.description}
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item={true} xs={12}>
|
||||||
|
<Card>
|
||||||
|
<CardContent>
|
||||||
|
<CopyToClipBoard text={`npm install ${packageName}`} />
|
||||||
|
<CopyToClipBoard text={`pnpm install ${packageName}`} />
|
||||||
|
<CopyToClipBoard text={`yarn add ${packageName}`} />
|
||||||
|
<CardActions>
|
||||||
|
<Button color={"primary"} variant={"contained"}>
|
||||||
|
{'Download Tarball'}
|
||||||
|
</Button>
|
||||||
|
</CardActions>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Content>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDescription = (packageMeta) => {
|
||||||
|
console.log('packageMeta', packageMeta);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export default DetailSidebar;
|
14
src/webui/components/DetailSidebar/styles.js
Normal file
14
src/webui/components/DetailSidebar/styles.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import styled from 'react-emotion';
|
||||||
|
import colors from '../../utils/styles/colors';
|
||||||
|
|
||||||
|
export const Content = styled.div`
|
||||||
|
&& {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: ${colors.white};
|
||||||
|
}
|
||||||
|
`;
|
12
src/webui/components/DetailSidebar/types.js
Normal file
12
src/webui/components/DetailSidebar/types.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
|
import type { Node } from 'react';
|
||||||
|
|
||||||
|
export interface IProps {
|
||||||
|
children: Node;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
|
@ -7,7 +7,7 @@ import styled, { css } from 'react-emotion';
|
||||||
|
|
||||||
export const Content = styled.div`
|
export const Content = styled.div`
|
||||||
&& {
|
&& {
|
||||||
background-color: #fff;
|
background-color: #ffffff;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,7 @@ const Package = ({ name: label, version, time, author: { name, avatar }, descrip
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper className={'package'} to={`detail/${label}`}>
|
<Wrapper className={'package'} to={`version/${label}`}>
|
||||||
<Header>
|
<Header>
|
||||||
{renderMainInfo()}
|
{renderMainInfo()}
|
||||||
<Overview>
|
<Overview>
|
||||||
|
|
|
@ -1,9 +1,21 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import API from '../../utils/api';
|
import API from '../../utils/api';
|
||||||
|
import Grid from '@material-ui/core/Grid/index';
|
||||||
import Loading from '../../components/Loading';
|
import Loading from '../../components/Loading';
|
||||||
|
import DetailContainer from '../../components/DetailContainer';
|
||||||
|
import DetailSidebar from '../../components/DetailSidebar';
|
||||||
|
|
||||||
|
export const DetailContext = React.createContext();
|
||||||
|
|
||||||
|
export const DetailContextProvider = DetailContext.Provider;
|
||||||
|
export const DetailContextConsumer = DetailContext.Consumer;
|
||||||
|
|
||||||
class VersionPage extends Component<any, any> {
|
class VersionPage extends Component<any, any> {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
readMe: '',
|
readMe: '',
|
||||||
packageMeta: null,
|
packageMeta: null,
|
||||||
|
@ -18,6 +30,8 @@ class VersionPage extends Component<any, any> {
|
||||||
async loadPackageInfo() {
|
async loadPackageInfo() {
|
||||||
const { match } = this.props;
|
const { match } = this.props;
|
||||||
const packageName = match.params.package;
|
const packageName = match.params.package;
|
||||||
|
// FIXME: use utility
|
||||||
|
document.title = `Verdaccio - ${packageName}`;
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
readMe: '',
|
readMe: '',
|
||||||
|
@ -41,11 +55,34 @@ class VersionPage extends Component<any, any> {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {isLoading} = this.state;
|
const { isLoading, packageMeta, readMe } = this.state;
|
||||||
|
const { match } = this.props;
|
||||||
|
const packageName = match.params.package;
|
||||||
|
|
||||||
return (
|
if (isLoading === false) {
|
||||||
<div>{isLoading && <Loading />}</div>
|
return (
|
||||||
);
|
<DetailContextProvider value={{ packageMeta, readMe, packageName }}>
|
||||||
|
<Grid className={'container content'} container={true} spacing={0}>
|
||||||
|
<Grid item={true} xs={8}>
|
||||||
|
{this.renderDetail()}
|
||||||
|
</Grid>
|
||||||
|
<Grid item={true} xs={4}>
|
||||||
|
{this.renderSidebar()}
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</DetailContextProvider>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <Loading />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDetail() {
|
||||||
|
return <DetailContainer />;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSidebar() {
|
||||||
|
return <DetailSidebar />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.container {
|
.container {
|
||||||
padding: 15px;
|
padding: 0px 15px;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
||||||
@include container-size;
|
@include container-size;
|
||||||
|
|
12
src/webui/utils/sec-utils.js
Normal file
12
src/webui/utils/sec-utils.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
/**
|
||||||
|
* @prettier
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
// $FlowFixMe
|
||||||
|
import parseXSS from 'xss';
|
||||||
|
|
||||||
|
export function preventXSS(text: string) {
|
||||||
|
const encodedText = parseXSS(text);
|
||||||
|
|
||||||
|
return encodedText;
|
||||||
|
}
|
|
@ -1,34 +1,34 @@
|
||||||
// Verdaccio
|
// Verdaccio
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
// Main colors
|
// Main colors
|
||||||
// -------------------------
|
// -------------------------
|
||||||
const colors = {
|
const colors = {
|
||||||
black: '#000',
|
black: '#000',
|
||||||
white: '#fff',
|
white: '#fff',
|
||||||
red: '#d32f2f',
|
red: '#d32f2f',
|
||||||
grey: '#808080',
|
grey: '#808080',
|
||||||
|
greySuperLight: '#f5f5f5',
|
||||||
greyLight: '#d3d3d3',
|
greyLight: '#d3d3d3',
|
||||||
greyDark: '#a9a9a9',
|
greyDark: '#a9a9a9',
|
||||||
greyChateau: '#95989a',
|
greyChateau: '#95989a',
|
||||||
greyGainsboro: '#e3e3e3',
|
greyGainsboro: '#e3e3e3',
|
||||||
greyAthens: '#d3dddd',
|
greyAthens: '#d3dddd',
|
||||||
|
|
||||||
eclipse: '#3c3c3c',
|
eclipse: '#3c3c3c',
|
||||||
paleNavy: '#e4e8f1',
|
paleNavy: '#e4e8f1',
|
||||||
saltpan: '#f7f8f6',
|
saltpan: '#f7f8f6',
|
||||||
snow: '#f9f9f9',
|
snow: '#f9f9f9',
|
||||||
|
|
||||||
nobel01: '#999999',
|
nobel01: '#999999',
|
||||||
nobel02: '#9f9f9f',
|
nobel02: '#9f9f9f',
|
||||||
|
|
||||||
// Main colors
|
// Main colors
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
primary: '#4b5e40',
|
primary: '#4b5e40',
|
||||||
secondary: '#20232a',
|
secondary: '#20232a',
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default colors;
|
export default colors;
|
||||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -3594,6 +3594,11 @@ cssesc@^2.0.0:
|
||||||
resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
|
resolved "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz#3b13bd1bb1cb36e1bcb5a4dcd27f54c5dcb35703"
|
||||||
integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
|
integrity sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==
|
||||||
|
|
||||||
|
cssfilter@0.0.10:
|
||||||
|
version "0.0.10"
|
||||||
|
resolved "https://registry.npmjs.org/cssfilter/-/cssfilter-0.0.10.tgz#c6d2672632a2e5c83e013e6864a42ce8defd20ae"
|
||||||
|
integrity sha1-xtJnJjKi5cg+AT5oZKQs6N79IK4=
|
||||||
|
|
||||||
cssnano-preset-default@^4.0.6:
|
cssnano-preset-default@^4.0.6:
|
||||||
version "4.0.6"
|
version "4.0.6"
|
||||||
resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.6.tgz#92379e2a6db4a91c0ea727f5f556eeac693eab6a"
|
resolved "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.6.tgz#92379e2a6db4a91c0ea727f5f556eeac693eab6a"
|
||||||
|
@ -13417,6 +13422,14 @@ xregexp@4.0.0:
|
||||||
resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
|
resolved "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020"
|
||||||
integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
|
integrity sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==
|
||||||
|
|
||||||
|
xss@1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.npmjs.org/xss/-/xss-1.0.3.tgz#d04bd2558fd6c29c46113824d5e8b2a910054e23"
|
||||||
|
integrity sha512-LTpz3jXPLUphMMmyufoZRSKnqMj41OVypZ8uYGzvjkMV9C1EdACrhQl/EM8Qfh5htSAuMIQFOejmKAZGkJfaCg==
|
||||||
|
dependencies:
|
||||||
|
commander "^2.9.0"
|
||||||
|
cssfilter "0.0.10"
|
||||||
|
|
||||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
|
||||||
version "4.0.1"
|
version "4.0.1"
|
||||||
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||||
|
|
Loading…
Add table
Reference in a new issue