Prettier everything up
This commit is contained in:
@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import contentHTML from './About.md'
|
||||
|
||||
const About = () =>
|
||||
<div className="wrapper" dangerouslySetInnerHTML={{ __html: contentHTML }}/>
|
||||
const About = () => (
|
||||
<div className="wrapper" dangerouslySetInnerHTML={{ __html: contentHTML }} />
|
||||
)
|
||||
|
||||
export default About
|
||||
|
||||
@ -2,9 +2,10 @@ import React from 'react'
|
||||
import { HashRouter } from 'react-router-dom'
|
||||
import Layout from './Layout'
|
||||
|
||||
const App = () =>
|
||||
const App = () => (
|
||||
<HashRouter>
|
||||
<Layout/>
|
||||
<Layout />
|
||||
</HashRouter>
|
||||
)
|
||||
|
||||
export default App
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import React from 'react'
|
||||
import contentHTML from './Home.md'
|
||||
|
||||
const Home = () =>
|
||||
<div className="wrapper" dangerouslySetInnerHTML={{ __html: contentHTML }}/>
|
||||
const Home = () => (
|
||||
<div className="wrapper" dangerouslySetInnerHTML={{ __html: contentHTML }} />
|
||||
)
|
||||
|
||||
export default Home
|
||||
|
||||
@ -54,8 +54,7 @@ class Layout extends React.Component {
|
||||
if (window.localStorage) {
|
||||
const savedStats = window.localStorage.savedStats
|
||||
|
||||
if (savedStats)
|
||||
this.setState({ stats: JSON.parse(savedStats) })
|
||||
if (savedStats) this.setState({ stats: JSON.parse(savedStats) })
|
||||
|
||||
window.onbeforeunload = () => {
|
||||
localStorage.savedStats = JSON.stringify(this.state.stats)
|
||||
@ -72,21 +71,32 @@ class Layout extends React.Component {
|
||||
const { underlineLeft, underlineWidth, useSpring } = this.state
|
||||
|
||||
const style = {
|
||||
left: useSpring ? spring(underlineLeft, { stiffness: 220 }) : underlineLeft,
|
||||
left: useSpring
|
||||
? spring(underlineLeft, { stiffness: 220 })
|
||||
: underlineLeft,
|
||||
width: useSpring ? spring(underlineWidth) : underlineWidth
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<WindowSize onChange={this.adjustUnderline}/>
|
||||
<WindowSize onChange={this.adjustUnderline} />
|
||||
<div className="wrapper">
|
||||
<header>
|
||||
<h1 className="layout-title">unpkg</h1>
|
||||
<nav className="layout-nav">
|
||||
<ol className="layout-nav-list" ref={node => this.listNode = node}>
|
||||
<li><Link to="/">Home</Link></li>
|
||||
<li><Link to="/stats">Stats</Link></li>
|
||||
<li><Link to="/about">About</Link></li>
|
||||
<ol
|
||||
className="layout-nav-list"
|
||||
ref={node => (this.listNode = node)}
|
||||
>
|
||||
<li>
|
||||
<Link to="/">Home</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/stats">Stats</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to="/about">About</Link>
|
||||
</li>
|
||||
</ol>
|
||||
<Motion
|
||||
defaultStyle={{ left: underlineLeft, width: underlineWidth }}
|
||||
@ -107,9 +117,12 @@ class Layout extends React.Component {
|
||||
</div>
|
||||
|
||||
<Switch>
|
||||
<Route path="/stats" render={() => <Stats data={this.state.stats}/>}/>
|
||||
<Route path="/about" component={About}/>
|
||||
<Route path="/" component={Home}/>
|
||||
<Route
|
||||
path="/stats"
|
||||
render={() => <Stats data={this.state.stats} />}
|
||||
/>
|
||||
<Route path="/about" component={About} />
|
||||
<Route path="/" component={Home} />
|
||||
</Switch>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -24,12 +24,11 @@ class NumberTextInput extends React.Component {
|
||||
this.setState({ value: this.props.value })
|
||||
}
|
||||
|
||||
handleChange = (event) => {
|
||||
handleChange = event => {
|
||||
const value = this.props.parseNumber(event.target.value)
|
||||
|
||||
this.setState({ value }, () => {
|
||||
if (this.props.onChange)
|
||||
this.props.onChange(value)
|
||||
if (this.props.onChange) this.props.onChange(value)
|
||||
})
|
||||
}
|
||||
|
||||
@ -39,7 +38,12 @@ class NumberTextInput extends React.Component {
|
||||
const displayValue = formatNumber(value)
|
||||
|
||||
return (
|
||||
<input {...props} type="text" value={displayValue} onChange={this.handleChange}/>
|
||||
<input
|
||||
{...props}
|
||||
type="text"
|
||||
value={displayValue}
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
200
client/Stats.js
200
client/Stats.js
@ -8,13 +8,15 @@ import formatPercent from './utils/formatPercent'
|
||||
|
||||
import { continents, countries } from 'countries-list'
|
||||
|
||||
const getCountriesByContinent = (continent) =>
|
||||
Object.keys(countries).filter(country => countries[country].continent === continent)
|
||||
const getCountriesByContinent = continent =>
|
||||
Object.keys(countries).filter(
|
||||
country => countries[country].continent === continent
|
||||
)
|
||||
|
||||
const sumKeyValues = (hash, keys) =>
|
||||
keys.reduce((n, key) => n + (hash[key] || 0), 0)
|
||||
|
||||
const sumValues = (hash) =>
|
||||
const sumValues = hash =>
|
||||
Object.keys(hash).reduce((memo, key) => memo + hash[key], 0)
|
||||
|
||||
class Stats extends React.Component {
|
||||
@ -30,8 +32,7 @@ class Stats extends React.Component {
|
||||
render() {
|
||||
const { data } = this.props
|
||||
|
||||
if (data == null)
|
||||
return null
|
||||
if (data == null) return null
|
||||
|
||||
const totals = data.totals
|
||||
|
||||
@ -42,25 +43,43 @@ class Stats extends React.Component {
|
||||
// Packages
|
||||
const packageRows = []
|
||||
|
||||
Object.keys(totals.requests.package).sort((a, b) => {
|
||||
return totals.requests.package[b] - totals.requests.package[a]
|
||||
}).forEach(packageName => {
|
||||
const requests = totals.requests.package[packageName]
|
||||
const bandwidth = totals.bandwidth.package[packageName]
|
||||
Object.keys(totals.requests.package)
|
||||
.sort((a, b) => {
|
||||
return totals.requests.package[b] - totals.requests.package[a]
|
||||
})
|
||||
.forEach(packageName => {
|
||||
const requests = totals.requests.package[packageName]
|
||||
const bandwidth = totals.bandwidth.package[packageName]
|
||||
|
||||
if (requests >= this.state.minPackageRequests) {
|
||||
packageRows.push(
|
||||
<tr key={packageName}>
|
||||
<td><a href={`https://npmjs.org/package/${packageName}`} title={`${packageName} on npm`}>{packageName}</a></td>
|
||||
<td>{formatNumber(requests)} ({formatPercent(requests / totals.requests.all)}%)</td>
|
||||
{bandwidth
|
||||
? <td>{formatBytes(bandwidth)} ({formatPercent(bandwidth / totals.bandwidth.all)}%)</td>
|
||||
: <td>-</td>
|
||||
}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
})
|
||||
if (requests >= this.state.minPackageRequests) {
|
||||
packageRows.push(
|
||||
<tr key={packageName}>
|
||||
<td>
|
||||
<a
|
||||
href={`https://npmjs.org/package/${packageName}`}
|
||||
title={`${packageName} on npm`}
|
||||
>
|
||||
{packageName}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{formatNumber(requests)} ({formatPercent(
|
||||
requests / totals.requests.all
|
||||
)}%)
|
||||
</td>
|
||||
{bandwidth ? (
|
||||
<td>
|
||||
{formatBytes(bandwidth)} ({formatPercent(
|
||||
bandwidth / totals.bandwidth.all
|
||||
)}%)
|
||||
</td>
|
||||
) : (
|
||||
<td>-</td>
|
||||
)}
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
// Regions
|
||||
const regionRows = []
|
||||
@ -85,12 +104,23 @@ class Stats extends React.Component {
|
||||
const continentName = continents[continent]
|
||||
const continentData = continentsData[continent]
|
||||
|
||||
if (continentData.requests > this.state.minCountryRequests && continentData.bandwidth !== 0) {
|
||||
if (
|
||||
continentData.requests > this.state.minCountryRequests &&
|
||||
continentData.bandwidth !== 0
|
||||
) {
|
||||
regionRows.push(
|
||||
<tr key={continent} className="continent-row">
|
||||
<td>{continentName}</td>
|
||||
<td>{formatNumber(continentData.requests)} ({formatPercent(continentData.requests / totals.requests.all)}%)</td>
|
||||
<td>{formatBytes(continentData.bandwidth)} ({formatPercent(continentData.bandwidth / totals.bandwidth.all)}%)</td>
|
||||
<td>
|
||||
{formatNumber(continentData.requests)} ({formatPercent(
|
||||
continentData.requests / totals.requests.all
|
||||
)}%)
|
||||
</td>
|
||||
<td>
|
||||
{formatBytes(continentData.bandwidth)} ({formatPercent(
|
||||
continentData.bandwidth / totals.bandwidth.all
|
||||
)}%)
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@ -106,8 +136,16 @@ class Stats extends React.Component {
|
||||
regionRows.push(
|
||||
<tr key={continent + country} className="country-row">
|
||||
<td className="country-name">{countries[country].name}</td>
|
||||
<td>{formatNumber(countryRequests)} ({formatPercent(countryRequests / totals.requests.all)}%)</td>
|
||||
<td>{formatBytes(countryBandwidth)} ({formatPercent(countryBandwidth / totals.bandwidth.all)}%)</td>
|
||||
<td>
|
||||
{formatNumber(countryRequests)} ({formatPercent(
|
||||
countryRequests / totals.requests.all
|
||||
)}%)
|
||||
</td>
|
||||
<td>
|
||||
{formatBytes(countryBandwidth)} ({formatPercent(
|
||||
countryBandwidth / totals.bandwidth.all
|
||||
)}%)
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
@ -116,30 +154,58 @@ class Stats extends React.Component {
|
||||
})
|
||||
|
||||
// Protocols
|
||||
const protocolRows = Object.keys(totals.requests.protocol).sort((a, b) => {
|
||||
return totals.requests.protocol[b] - totals.requests.protocol[a]
|
||||
}).map(protocol => {
|
||||
const requests = totals.requests.protocol[protocol]
|
||||
const protocolRows = Object.keys(totals.requests.protocol)
|
||||
.sort((a, b) => {
|
||||
return totals.requests.protocol[b] - totals.requests.protocol[a]
|
||||
})
|
||||
.map(protocol => {
|
||||
const requests = totals.requests.protocol[protocol]
|
||||
|
||||
return (
|
||||
<tr key={protocol}>
|
||||
<td>{protocol}</td>
|
||||
<td>{formatNumber(requests)} ({formatPercent(requests / sumValues(totals.requests.protocol))}%)</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
return (
|
||||
<tr key={protocol}>
|
||||
<td>{protocol}</td>
|
||||
<td>
|
||||
{formatNumber(requests)} ({formatPercent(
|
||||
requests / sumValues(totals.requests.protocol)
|
||||
)}%)
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className="wrapper">
|
||||
<p>From <strong>{formatDate(since, 'MMM D')}</strong> to <strong>{formatDate(until, 'MMM D')}</strong> unpkg served <strong>{formatNumber(totals.requests.all)}</strong> requests and a total of <strong>{formatBytes(totals.bandwidth.all)}</strong> of data to <strong>{formatNumber(totals.uniques.all)}</strong> unique visitors, <strong>{formatPercent(totals.requests.cached / totals.requests.all, 0)}%</strong> of which were served from the cache.</p>
|
||||
<p>
|
||||
From <strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||
<strong>{formatDate(until, 'MMM D')}</strong> unpkg served{' '}
|
||||
<strong>{formatNumber(totals.requests.all)}</strong> requests and a
|
||||
total of <strong>{formatBytes(totals.bandwidth.all)}</strong> of data
|
||||
to <strong>{formatNumber(totals.uniques.all)}</strong> unique
|
||||
visitors,{' '}
|
||||
<strong>
|
||||
{formatPercent(totals.requests.cached / totals.requests.all, 0)}%
|
||||
</strong>{' '}
|
||||
of which were served from the cache.
|
||||
</p>
|
||||
|
||||
<h3>Packages</h3>
|
||||
|
||||
<p>The table below shows the most popular packages served by unpkg from <strong>{formatDate(since, 'MMM D')}</strong> to <strong>{formatDate(until, 'MMM D')}</strong>. Only the top {Object.keys(totals.requests.package).length} packages are shown.</p>
|
||||
<p>
|
||||
The table below shows the most popular packages served by unpkg from{' '}
|
||||
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||
<strong>{formatDate(until, 'MMM D')}</strong>. Only the top{' '}
|
||||
{Object.keys(totals.requests.package).length} packages are shown.
|
||||
</p>
|
||||
|
||||
<p className="table-filter">Include only packages that received at least <select
|
||||
<p className="table-filter">
|
||||
Include only packages that received at least{' '}
|
||||
<select
|
||||
value={this.state.minPackageRequests}
|
||||
onChange={event => this.setState({ minPackageRequests: parseInt(event.target.value, 10) })}
|
||||
onChange={event =>
|
||||
this.setState({
|
||||
minPackageRequests: parseInt(event.target.value, 10)
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="0">0</option>
|
||||
<option value="1000">1,000</option>
|
||||
@ -147,7 +213,8 @@ class Stats extends React.Component {
|
||||
<option value="100000">100,000</option>
|
||||
<option value="1000000">1,000,000</option>
|
||||
<option value="10000000">10,000,000</option>
|
||||
</select> requests.
|
||||
</select>{' '}
|
||||
requests.
|
||||
</p>
|
||||
|
||||
<table cellSpacing="0" cellPadding="0" style={{ width: '100%' }}>
|
||||
@ -158,28 +225,42 @@ class Stats extends React.Component {
|
||||
<th>Bandwidth (% of total)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{packageRows}
|
||||
</tbody>
|
||||
<tbody>{packageRows}</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Regions</h3>
|
||||
|
||||
<p>The table below breaks down requests to unpkg from <strong>{formatDate(since, 'MMM D')}</strong> to <strong>{formatDate(until, 'MMM D')}</strong> by geographic region.</p>
|
||||
<p>
|
||||
The table below breaks down requests to unpkg from{' '}
|
||||
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||
<strong>{formatDate(until, 'MMM D')}</strong> by geographic region.
|
||||
</p>
|
||||
|
||||
<p className="table-filter">Include only countries that made at least <select
|
||||
<p className="table-filter">
|
||||
Include only countries that made at least{' '}
|
||||
<select
|
||||
value={this.state.minCountryRequests}
|
||||
onChange={event => this.setState({ minCountryRequests: parseInt(event.target.value, 10) })}
|
||||
onChange={event =>
|
||||
this.setState({
|
||||
minCountryRequests: parseInt(event.target.value, 10)
|
||||
})
|
||||
}
|
||||
>
|
||||
<option value="0">0</option>
|
||||
<option value="100000">100,000</option>
|
||||
<option value="1000000">1,000,000</option>
|
||||
<option value="10000000">10,000,000</option>
|
||||
<option value="100000000">100,000,000</option>
|
||||
</select> requests.
|
||||
</select>{' '}
|
||||
requests.
|
||||
</p>
|
||||
|
||||
<table cellSpacing="0" cellPadding="0" style={{ width: '100%' }} className="regions-table">
|
||||
<table
|
||||
cellSpacing="0"
|
||||
cellPadding="0"
|
||||
style={{ width: '100%' }}
|
||||
className="regions-table"
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Region</th>
|
||||
@ -187,14 +268,16 @@ class Stats extends React.Component {
|
||||
<th>Bandwidth (% of total)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{regionRows}
|
||||
</tbody>
|
||||
<tbody>{regionRows}</tbody>
|
||||
</table>
|
||||
|
||||
<h3>Protocols</h3>
|
||||
|
||||
<p>The table below breaks down requests to unpkg from <strong>{formatDate(since, 'MMM D')}</strong> to <strong>{formatDate(until, 'MMM D')}</strong> by HTTP protocol.</p>
|
||||
<p>
|
||||
The table below breaks down requests to unpkg from{' '}
|
||||
<strong>{formatDate(since, 'MMM D')}</strong> to{' '}
|
||||
<strong>{formatDate(until, 'MMM D')}</strong> by HTTP protocol.
|
||||
</p>
|
||||
|
||||
<table cellSpacing="0" cellPadding="0" style={{ width: '100%' }}>
|
||||
<thead>
|
||||
@ -203,11 +286,8 @@ class Stats extends React.Component {
|
||||
<th>Requests (% of total)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{protocolRows}
|
||||
</tbody>
|
||||
<tbody>{protocolRows}</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -3,7 +3,4 @@ import ReactDOM from 'react-dom'
|
||||
import App from './App'
|
||||
import './index.css'
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
)
|
||||
ReactDOM.render(<App />, document.getElementById('root'))
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
const formatNumber = (n) => {
|
||||
const formatNumber = n => {
|
||||
const digits = String(n).split('')
|
||||
const groups = []
|
||||
|
||||
while (digits.length)
|
||||
groups.unshift(digits.splice(-3).join(''))
|
||||
while (digits.length) groups.unshift(digits.splice(-3).join(''))
|
||||
|
||||
return groups.join(',')
|
||||
}
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
const parseNumber = (s) =>
|
||||
parseInt(s.replace(/,/g, ''), 10) || 0
|
||||
const parseNumber = s => parseInt(s.replace(/,/g, ''), 10) || 0
|
||||
|
||||
export default parseNumber
|
||||
|
||||
Reference in New Issue
Block a user