mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-30 22:34:10 -05:00
Add new web ui, replace the old one based on jQuery by React components and webpack.
This commit is contained in:
parent
a6d3745cd4
commit
e49846bb1b
35 changed files with 1348 additions and 9895 deletions
20
.babelrc
Normal file
20
.babelrc
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"react",
|
||||||
|
["env", {
|
||||||
|
"targets": {
|
||||||
|
"browsers": "last 2 versions"
|
||||||
|
},
|
||||||
|
"loose": true,
|
||||||
|
"modules": false
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"plugins": [
|
||||||
|
"transform-es2015-modules-commonjs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
lib/web/static
|
lib/web/static
|
||||||
lib/web/ui/
|
|
||||||
coverage/
|
coverage/
|
||||||
wiki/
|
wiki/
|
||||||
|
|
|
@ -9,13 +9,22 @@
|
||||||
# Created to work with eslint@0.18.0
|
# Created to work with eslint@0.18.0
|
||||||
#
|
#
|
||||||
|
|
||||||
extends: ["eslint:recommended", "google"]
|
plugins: ["react"]
|
||||||
|
|
||||||
|
extends: ["eslint:recommended", "google", "plugin:react/recommended"]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
node: true
|
node: true
|
||||||
browser: true
|
browser: true
|
||||||
es6: true
|
es6: true
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
sourceType: "module"
|
||||||
|
ecmaVersion: 7
|
||||||
|
ecmaFeatures:
|
||||||
|
jsx: true
|
||||||
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
# useful to have in node.js,
|
# useful to have in node.js,
|
||||||
# if you're sure you don't need to handle error, rename it to "_err"
|
# if you're sure you don't need to handle error, rename it to "_err"
|
||||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -20,3 +20,10 @@ coverage/
|
||||||
jsconfig.json
|
jsconfig.json
|
||||||
|
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
|
||||||
|
# React
|
||||||
|
bundle.js
|
||||||
|
bundle.js.map
|
||||||
|
__tests__
|
||||||
|
__snapshots__
|
||||||
|
|
|
@ -4,4 +4,5 @@ coverage/
|
||||||
verdaccio-*.tgz
|
verdaccio-*.tgz
|
||||||
test-storage*
|
test-storage*
|
||||||
/.*
|
/.*
|
||||||
|
scripts/
|
||||||
wiki/
|
wiki/
|
||||||
|
|
39
Gruntfile.js
39
Gruntfile.js
|
@ -1,39 +0,0 @@
|
||||||
module.exports = function(grunt) {
|
|
||||||
grunt.initConfig({
|
|
||||||
pkg: grunt.file.readJSON('package.json'),
|
|
||||||
browserify: {
|
|
||||||
dist: {
|
|
||||||
files: {
|
|
||||||
'lib/static/main.js': ['lib/ui/js/main.js'],
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
debug: true,
|
|
||||||
transform: ['browserify-handlebars'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
less: {
|
|
||||||
dist: {
|
|
||||||
files: {
|
|
||||||
'lib/static/main.css': ['lib/ui/css/main.less'],
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
sourceMap: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
files: ['lib/ui/**/*'],
|
|
||||||
tasks: ['default'],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
grunt.loadNpmTasks('grunt-browserify');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-less');
|
|
||||||
|
|
||||||
grunt.registerTask('default', [
|
|
||||||
'browserify',
|
|
||||||
'less',
|
|
||||||
]);
|
|
||||||
};
|
|
|
@ -18,8 +18,11 @@ web:
|
||||||
#enable: true
|
#enable: true
|
||||||
|
|
||||||
title: Verdaccio
|
title: Verdaccio
|
||||||
|
|
||||||
# logo: logo.png
|
# logo: logo.png
|
||||||
|
|
||||||
# template: custom.hbs
|
# template: custom.hbs
|
||||||
|
|
||||||
# tagline: "Some <b>HTML</b> enabled tagline that sits between the actual \
|
# tagline: "Some <b>HTML</b> enabled tagline that sits between the actual \
|
||||||
#header and the list of packages. You can even add <a \
|
#header and the list of packages. You can even add <a \
|
||||||
#href=\"https://github.com\">links</a>!"
|
#href=\"https://github.com\">links</a>!"
|
||||||
|
|
BIN
lib/web/static/header.png
Normal file
BIN
lib/web/static/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
511
lib/web/static/styles.css
Normal file
511
lib/web/static/styles.css
Normal file
|
@ -0,0 +1,511 @@
|
||||||
|
/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the default font family in all browsers (opinionated).
|
||||||
|
* 2. Correct the line height in all browsers.
|
||||||
|
* 3. Prevent adjustments of font size after orientation changes in
|
||||||
|
* IE on Windows Phone and in iOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Document
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
line-height: 1.15; /* 2 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 3 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sections
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the margin in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the font size and margin on `h1` elements within `section` and
|
||||||
|
* `article` contexts in Chrome, Firefox, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
main { /* 1 */
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct margin in IE 8.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in Firefox.
|
||||||
|
* 2. Show the overflow in Edge and IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
box-sizing: content-box; /* 1 */
|
||||||
|
height: 0; /* 1 */
|
||||||
|
overflow: visible; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the gray background on active links in IE 10.
|
||||||
|
* 2. Remove gaps in links underline in iOS 8+ and Safari 8+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent; /* 1 */
|
||||||
|
-webkit-text-decoration-skip: objects; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the outline on focused links when they are also active or hovered
|
||||||
|
* in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Remove the bottom border in Firefox 39-.
|
||||||
|
* 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: none; /* 1 */
|
||||||
|
text-decoration: underline; /* 2 */
|
||||||
|
text-decoration: underline dotted; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent the duplicate application of `bolder` by the next rule in Safari 6.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font weight in Chrome, Edge, and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inheritance and scaling of font size in all browsers.
|
||||||
|
* 2. Correct the odd `em` font sizing in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace; /* 1 */
|
||||||
|
font-size: 1em; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font style in Android 4.3-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct background and color in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background-color: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` elements from affecting the line height in
|
||||||
|
* all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in iOS 4-7.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the border on images inside links in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide the overflow in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Change the font styles in all browsers (opinionated).
|
||||||
|
* 2. Remove the margin in Firefox and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
font-size: 100%; /* 1 */
|
||||||
|
line-height: 1.15; /* 1 */
|
||||||
|
margin: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the overflow in IE.
|
||||||
|
* 1. Show the overflow in Edge.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input { /* 1 */
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inheritance of text transform in Edge, Firefox, and IE.
|
||||||
|
* 1. Remove the inheritance of text transform in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select { /* 1 */
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
|
||||||
|
* controls in Android 4.
|
||||||
|
* 2. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html [type="button"], /* 1 */
|
||||||
|
[type="reset"],
|
||||||
|
[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner border and padding in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
[type="button"]::-moz-focus-inner,
|
||||||
|
[type="reset"]::-moz-focus-inner,
|
||||||
|
[type="submit"]::-moz-focus-inner {
|
||||||
|
border-style: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the focus styles unset by the previous rule.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button:-moz-focusring,
|
||||||
|
[type="button"]:-moz-focusring,
|
||||||
|
[type="reset"]:-moz-focusring,
|
||||||
|
[type="submit"]:-moz-focusring {
|
||||||
|
outline: 1px dotted ButtonText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the border, margin, and padding in all browsers (opinionated).
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 0.35em 0.625em 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the text wrapping in Edge and IE.
|
||||||
|
* 2. Correct the color inheritance from `fieldset` elements in IE.
|
||||||
|
* 3. Remove the padding so developers are not caught out when they zero out
|
||||||
|
* `fieldset` elements in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
color: inherit; /* 2 */
|
||||||
|
display: table; /* 1 */
|
||||||
|
max-width: 100%; /* 1 */
|
||||||
|
padding: 0; /* 3 */
|
||||||
|
white-space: normal; /* 1 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct display in IE 9-.
|
||||||
|
* 2. Add the correct vertical alignment in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
progress {
|
||||||
|
display: inline-block; /* 1 */
|
||||||
|
vertical-align: baseline; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the default vertical scrollbar in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Add the correct box sizing in IE 10-.
|
||||||
|
* 2. Remove the padding in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="checkbox"],
|
||||||
|
[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct the cursor style of increment and decrement buttons in Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="number"]::-webkit-inner-spin-button,
|
||||||
|
[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the odd appearance in Chrome and Safari.
|
||||||
|
* 2. Correct the outline style in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
outline-offset: -2px; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the inner padding and cancel buttons in Chrome and Safari on macOS.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[type="search"]::-webkit-search-cancel-button,
|
||||||
|
[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct the inability to style clickable types in iOS and Safari.
|
||||||
|
* 2. Change font properties to `inherit` in Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
::-webkit-file-upload-button {
|
||||||
|
-webkit-appearance: button; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interactive
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
* 1. Add the correct display in Edge, IE, and Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
details, /* 1 */
|
||||||
|
menu {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the correct display in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
summary {
|
||||||
|
display: list-item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scripting
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 9-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hidden
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the correct display in IE 10-.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
font-family: "Roboto","Helvetica","Arial",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.wrapper {
|
||||||
|
width: 750px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.npm-logo {
|
||||||
|
width: 100px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.packages-header {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.navbar {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
width: 755px;
|
||||||
|
}
|
||||||
|
.list-container {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=styles.css.map*/
|
BIN
lib/web/static/styles.css.map
Normal file
BIN
lib/web/static/styles.css.map
Normal file
Binary file not shown.
|
@ -1,8 +0,0 @@
|
||||||
|
|
||||||
env:
|
|
||||||
node: true
|
|
||||||
browser: true
|
|
||||||
|
|
||||||
globals:
|
|
||||||
jQuery: true
|
|
||||||
|
|
23
lib/web/ui/.eslintrc.yml
Normal file
23
lib/web/ui/.eslintrc.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# vim: syntax=yaml
|
||||||
|
|
||||||
|
|
||||||
|
## rules for react components
|
||||||
|
|
||||||
|
extends: ["eslint:recommended", "google", "plugin:react/recommended", "plugin:flowtype/recommended", "prettier", "prettier/react"]
|
||||||
|
|
||||||
|
plugins: ["flowtype", "prettier"]
|
||||||
|
|
||||||
|
parser: babel-eslint
|
||||||
|
|
||||||
|
env:
|
||||||
|
node: true
|
||||||
|
browser: true
|
||||||
|
jest: true
|
||||||
|
|
||||||
|
rules:
|
||||||
|
# jsdoc is mandatory
|
||||||
|
require-jsdoc: 0
|
||||||
|
# jsx rules
|
||||||
|
react/no-danger-with-children: 0
|
||||||
|
react/no-string-refs: 0
|
||||||
|
|
93
lib/web/ui/components/App.js
Normal file
93
lib/web/ui/components/App.js
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import injectTapEventPlugin from 'react-tap-event-plugin';
|
||||||
|
import request from 'superagent';
|
||||||
|
import getMuiTheme from 'material-ui/styles/getMuiTheme';
|
||||||
|
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
|
||||||
|
import {green100, green500, green700} from 'material-ui/styles/colors';
|
||||||
|
|
||||||
|
import Header from './Header/Header';
|
||||||
|
import Search from './Search/Search';
|
||||||
|
import List from './List/List';
|
||||||
|
|
||||||
|
injectTapEventPlugin();
|
||||||
|
|
||||||
|
if (process.env.BROWSER) {
|
||||||
|
require('./browser.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
const muiTheme = getMuiTheme({
|
||||||
|
palette: {
|
||||||
|
primary1Color: green500,
|
||||||
|
primary2Color: green700,
|
||||||
|
primary3Color: green100,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
avatar: {
|
||||||
|
borderColor: null,
|
||||||
|
},
|
||||||
|
userAgent: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
class App extends React.Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
packages: props.packages,
|
||||||
|
frontPackages: props.packages,
|
||||||
|
req: null,
|
||||||
|
};
|
||||||
|
this.updatePackages = this.updatePackages.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
updatePackages(keyword) {
|
||||||
|
if (keyword !== '') {
|
||||||
|
if (this.req) {
|
||||||
|
this.req.abort();
|
||||||
|
}
|
||||||
|
this.req = request.get(`/-/search/${keyword}`)
|
||||||
|
.end((err, res) => {
|
||||||
|
if(_.isNil(err) === false) {
|
||||||
|
this.setState({
|
||||||
|
packages: [],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
packages: res.body,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (this.req) {
|
||||||
|
this.req.abort();
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
packages: this.state.frontPackages,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<MuiThemeProvider muiTheme={muiTheme}>
|
||||||
|
<main>
|
||||||
|
<Header baseUrl={this.props.baseUrl} username={this.props.username}/>
|
||||||
|
<div className="wrapper">
|
||||||
|
<Search updatePackages={this.updatePackages}/>
|
||||||
|
<List packages={this.state.packages}/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</MuiThemeProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
App.propTypes = {
|
||||||
|
packages: PropTypes.array.isRequired,
|
||||||
|
baseUrl: PropTypes.string.isRequired,
|
||||||
|
username: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default App;
|
144
lib/web/ui/components/Header/Header.js
Normal file
144
lib/web/ui/components/Header/Header.js
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import RaisedButton from 'material-ui/RaisedButton';
|
||||||
|
import TextField from 'material-ui/TextField';
|
||||||
|
import IconPerson from 'material-ui/svg-icons/social/person';
|
||||||
|
import Lock from 'material-ui/svg-icons/action/lock';
|
||||||
|
import {List, ListItem} from 'material-ui/List';
|
||||||
|
import Dialog from 'material-ui/Dialog';
|
||||||
|
|
||||||
|
import {HeaderNav,
|
||||||
|
MenuGroup,
|
||||||
|
MenuItem,
|
||||||
|
Code,
|
||||||
|
CodeGroup,
|
||||||
|
LogoImage,
|
||||||
|
LogoItem, Navigation} from '../styled';
|
||||||
|
|
||||||
|
let logo = '/header.png';
|
||||||
|
if (process.env.BROWSER) {
|
||||||
|
logo = require('./header.png');
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
flex: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
red: {
|
||||||
|
backgroundColor: '#cc3d33',
|
||||||
|
},
|
||||||
|
fullWidth: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
spaceItems: {
|
||||||
|
marginRight: 20,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
class Header extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
|
this.handleRequestClose = this.handleRequestClose.bind(this);
|
||||||
|
this.handleTouchTap = this.handleTouchTap.bind(this);
|
||||||
|
this.handleLogIn = this.handleLogIn.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRequestClose() {
|
||||||
|
this.setState({
|
||||||
|
open: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTouchTap() {
|
||||||
|
this.setState({
|
||||||
|
open: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleLogIn() {
|
||||||
|
this.refs.form.submit();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const standardActions = process.env.BROWSER ? [
|
||||||
|
<RaisedButton key="Close"
|
||||||
|
label="Close"
|
||||||
|
style={styles.spaceItems}
|
||||||
|
onTouchTap={this.handleRequestClose}
|
||||||
|
/>,
|
||||||
|
<RaisedButton
|
||||||
|
key="LogIn"
|
||||||
|
label="Log In"
|
||||||
|
primary={true}
|
||||||
|
backgroundColor={styles.red.backgroundColor}
|
||||||
|
onTouchTap={this.handleLogIn}
|
||||||
|
/>,
|
||||||
|
] : [];
|
||||||
|
return (
|
||||||
|
<HeaderNav className="navbar">
|
||||||
|
<Navigation className="wrapper">
|
||||||
|
<MenuGroup>
|
||||||
|
<LogoItem style={{'flex': '2 0 0'}}>
|
||||||
|
<LogoImage src={`/-/static${logo}`}/>
|
||||||
|
</LogoItem>
|
||||||
|
<MenuItem style={{'flex': '10 0 0'}}>
|
||||||
|
<CodeGroup>
|
||||||
|
<div>
|
||||||
|
<Code>
|
||||||
|
{ `npm set registry ${this.props.baseUrl}` }
|
||||||
|
</Code>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Code>
|
||||||
|
{ `npm adduser --registry ${this.props.baseUrl}` }
|
||||||
|
</Code>
|
||||||
|
</div>
|
||||||
|
</CodeGroup>
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem style={{'flex': '1 0 0'}}>
|
||||||
|
<Dialog
|
||||||
|
open={this.state.open}
|
||||||
|
title="Welcome Back"
|
||||||
|
actions={standardActions}
|
||||||
|
onRequestClose={this.handleRequestClose}>
|
||||||
|
<form method="POST" ref="form" action="/-/login" autoComplete={false}>
|
||||||
|
<List>
|
||||||
|
<ListItem disabled leftIcon={<IconPerson />}>
|
||||||
|
<TextField
|
||||||
|
name="user"
|
||||||
|
hintText="Username"
|
||||||
|
fullWidth={true}
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
<ListItem disabled leftIcon={<Lock />}>
|
||||||
|
<TextField
|
||||||
|
name="pass"
|
||||||
|
hintText="Password"
|
||||||
|
fullWidth={true}
|
||||||
|
type="password"
|
||||||
|
/>
|
||||||
|
</ListItem>
|
||||||
|
</List>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
<RaisedButton
|
||||||
|
label="Login"
|
||||||
|
onTouchTap={this.handleTouchTap}
|
||||||
|
/>
|
||||||
|
</MenuItem>
|
||||||
|
</MenuGroup>
|
||||||
|
</Navigation>
|
||||||
|
</HeaderNav>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Header.propTypes = {
|
||||||
|
baseUrl: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default Header;
|
BIN
lib/web/ui/components/Header/header.png
Normal file
BIN
lib/web/ui/components/Header/header.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
143
lib/web/ui/components/Item/Item.js
Normal file
143
lib/web/ui/components/Item/Item.js
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import request from 'superagent';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import ArrowRight from 'material-ui/svg-icons/hardware/keyboard-arrow-right';
|
||||||
|
import ArrowDown from 'material-ui/svg-icons/hardware/keyboard-arrow-down';
|
||||||
|
|
||||||
|
import Readme from '../Readme/Readme';
|
||||||
|
|
||||||
|
const ItemWrap = styled.li`
|
||||||
|
padding: 9px 10px;
|
||||||
|
border-bottom: 1px solid #E7E7E7;
|
||||||
|
list-style-type: none;
|
||||||
|
&:nth-child(even) {
|
||||||
|
background: #f3f3f3;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Description = styled.p`
|
||||||
|
margin: 0 0 0 18px;
|
||||||
|
font-size: 13px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Group = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
line-height: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const GroupTitle = styled.div`
|
||||||
|
margin: 0 5px;
|
||||||
|
flex-basis: 85%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Author = styled.div`
|
||||||
|
flex-basis: 15%;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 0;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Small = styled.small`
|
||||||
|
color: #666;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = styled.h4`
|
||||||
|
margin: 0px;
|
||||||
|
margin-right: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Link = styled.a`
|
||||||
|
color: #cc3d33;
|
||||||
|
fill: currentColor;
|
||||||
|
text-decoration: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
class Item extends React.Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
open: false,
|
||||||
|
};
|
||||||
|
this.displayReadme = this.displayReadme.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {*} event
|
||||||
|
*/
|
||||||
|
displayReadme(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
if (!this.state.open) {
|
||||||
|
this.req = request
|
||||||
|
.get(this._buildUrl())
|
||||||
|
.set('Content-Type', 'text/html; charset=utf8')
|
||||||
|
.end((err, res) => {
|
||||||
|
if (_.isNil(err) === false) {
|
||||||
|
this.setState({
|
||||||
|
open: true,
|
||||||
|
readme: 'No readme available',
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
open: true,
|
||||||
|
readme: res.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.setState({
|
||||||
|
open: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildUrl() {
|
||||||
|
return `/-/readme/${encodeURIComponent(this.props.pkg.name)}/${encodeURIComponent(this.props.pkg.version)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const author = this.props.pkg.author ? this.props.pkg.author.name : '';
|
||||||
|
return (
|
||||||
|
<ItemWrap>
|
||||||
|
<div>
|
||||||
|
<Group>
|
||||||
|
<GroupTitle>
|
||||||
|
<Link href={'#'}>
|
||||||
|
<Group>
|
||||||
|
<span>
|
||||||
|
{ this.state.open ? <ArrowDown/> : <ArrowRight/> }
|
||||||
|
</span>
|
||||||
|
<Title onClick={this.displayReadme}>
|
||||||
|
{this.props.pkg.name}
|
||||||
|
</Title>
|
||||||
|
<Small>
|
||||||
|
{ `v${this.props.pkg.version}` }
|
||||||
|
</Small>
|
||||||
|
</Group>
|
||||||
|
</Link>
|
||||||
|
</GroupTitle>
|
||||||
|
<Author>
|
||||||
|
<Small>
|
||||||
|
{ `By: ${_.isNil(author) ? 'Not available' : author}` }
|
||||||
|
</Small>
|
||||||
|
</Author>
|
||||||
|
</Group>
|
||||||
|
</div>
|
||||||
|
<Description>
|
||||||
|
{this.props.pkg.description}
|
||||||
|
</Description>
|
||||||
|
{ this.state.open ? <Readme html={ this.state.readme }/> : '' }
|
||||||
|
</ItemWrap>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item.propTypes = {
|
||||||
|
pkg: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export default Item;
|
29
lib/web/ui/components/List/List.js
Normal file
29
lib/web/ui/components/List/List.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
import Item from '../Item/Item';
|
||||||
|
|
||||||
|
const ListItems = styled.ul`
|
||||||
|
margin: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
class List extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<ListItems className="list-container">
|
||||||
|
{ this.props.packages.map((item)=> {
|
||||||
|
return (<Item key={item.name} pkg={item}/>);
|
||||||
|
})}
|
||||||
|
</ListItems>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List.propTypes = {
|
||||||
|
packages: PropTypes.array.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default List;
|
30
lib/web/ui/components/Readme/Readme.js
Normal file
30
lib/web/ui/components/Readme/Readme.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const ReadmeStyle = styled.div`
|
||||||
|
margin-top: 10px;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 10px 12px;
|
||||||
|
border-radius: 3px;
|
||||||
|
border: 1px solid #dadada;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, "Segoe UI", Arial, freesans, sans-serif;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
|
word-wrap: break-word;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Readme = (props) => {
|
||||||
|
return (<ReadmeStyle className="readme"
|
||||||
|
dangerouslySetInnerHTML={{__html: props.html}}>
|
||||||
|
</ReadmeStyle>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Readme.propTypes = {
|
||||||
|
html: PropTypes.string.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Readme;
|
57
lib/web/ui/components/Search/Search.js
Normal file
57
lib/web/ui/components/Search/Search.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import TextField from 'material-ui/TextField';
|
||||||
|
|
||||||
|
const SearchContainer = styled.div`
|
||||||
|
margin-top: 20px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const styles = {
|
||||||
|
flex: {
|
||||||
|
display: 'flex',
|
||||||
|
},
|
||||||
|
fullWidth: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
class Search extends React.Component {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = {
|
||||||
|
value: '',
|
||||||
|
};
|
||||||
|
this.handleChange = this.handleChange.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange(event) {
|
||||||
|
const keyword = event.target.value.trim();
|
||||||
|
this.setState({
|
||||||
|
value: keyword,
|
||||||
|
});
|
||||||
|
this.props.updatePackages(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<SearchContainer className="search-box">
|
||||||
|
<TextField
|
||||||
|
style={styles.fullWidth}
|
||||||
|
hintText="Search for packages"
|
||||||
|
fullWidth={true}
|
||||||
|
value={this.state.value}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
/>
|
||||||
|
</SearchContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Search.propTypes = {
|
||||||
|
updatePackages: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Search;
|
||||||
|
|
50
lib/web/ui/components/browser.css
Normal file
50
lib/web/ui/components/browser.css
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
@import '../../../../node_modules/normalize.css/normalize.css';
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "Roboto","Helvetica","Arial",sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-container {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-left: 15px;
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.wrapper {
|
||||||
|
width: 750px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 992px) {
|
||||||
|
.npm-logo {
|
||||||
|
width: 100px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.packages-header {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.navbar {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
.wrapper {
|
||||||
|
width: 755px;
|
||||||
|
}
|
||||||
|
.list-container {
|
||||||
|
padding-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
13
lib/web/ui/components/packages/Detail.js
Normal file
13
lib/web/ui/components/packages/Detail.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class Detail extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<header>
|
||||||
|
<h1>DETAIL</h1>
|
||||||
|
</header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Detail;
|
49
lib/web/ui/components/styled/index.js
Normal file
49
lib/web/ui/components/styled/index.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const MenuItem = styled.li``;
|
||||||
|
|
||||||
|
const HeaderNav = styled.header`
|
||||||
|
background: #cc3d33;
|
||||||
|
margin: 0px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Navigation = styled.nav`
|
||||||
|
margin-bottom: 0px;
|
||||||
|
border-radius: 0px;
|
||||||
|
position: relative;
|
||||||
|
min-height: 50px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const MenuGroup = styled.ul`
|
||||||
|
display: flex;
|
||||||
|
margin: 0;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LogoItem = styled(MenuItem)`
|
||||||
|
width: 100px;
|
||||||
|
height: 50px;
|
||||||
|
padding: 5px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Code = styled.code`
|
||||||
|
background: none;
|
||||||
|
color: white;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const LogoImage = styled.img`
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 85%;
|
||||||
|
height: 85%;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CodeGroup = styled.div`
|
||||||
|
line-height: 1.5em;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export { Code, LogoImage, CodeGroup, MenuGroup, Navigation, HeaderNav, MenuItem, LogoItem }
|
|
@ -3,132 +3,15 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>{{ name }}</title>
|
<title>{{ name }}</title>
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="{{ baseUrl }}/-/static/favicon.png"/>
|
<link rel="icon" type="image/png" href="{{ baseUrl }}/-/static/favicon.png"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{{ baseUrl }}/-/static/main.css">
|
<link rel="stylesheet" type="text/css" href="{{ baseUrl }}/-/static/styles.css">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body class="body">
|
<body class="body">
|
||||||
<header class="main-header">
|
<div id="root"></div>
|
||||||
<nav class="navbar navbar-default red-bg white no-border no-rnd-cnr" role="navigation">
|
<script>
|
||||||
<div class="container">
|
window.__INITIAL_STATE = JSON.parse('{{{data}}}');
|
||||||
<div class="navbar-header clearfix">
|
</script>
|
||||||
<div class="npm-logo brand">
|
<script type='text/javascript' src='{{ baseUrl }}/-/static/bundle.js'></script>
|
||||||
<a href="{{ baseUrl }}"></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- login/logout for small devices -->
|
|
||||||
<div class="pull-right visible-xs pad-right-10">
|
|
||||||
<div>
|
|
||||||
{{#if username}}
|
|
||||||
<p class="white no-bg navbar-text pad-right-10 inline-block">Hi {{username}}</p>
|
|
||||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
|
||||||
{{else}}
|
|
||||||
<p class="white no-bg navbar-text pad-right-10 inline-block"> </p>
|
|
||||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="navbar-left hidden-xs"> </div>
|
|
||||||
|
|
||||||
<div class="navbar-left setup hidden-xs">
|
|
||||||
<code class="white no-bg">npm set registry {{ baseUrl }}</code><br>
|
|
||||||
<code class="white no-bg">npm adduser --registry {{ baseUrl }}</code>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- login/logout for large devices -->
|
|
||||||
<div class="navbar-collapse collapse">
|
|
||||||
<div class="navbar-right">
|
|
||||||
<form class="navbar-form navbar-right">
|
|
||||||
{{#if username}}
|
|
||||||
<p class="white no-bg pad-right-10 inline-block">Hi {{username}}</p>
|
|
||||||
<button type="submit" class="btn btn-danger inline-block js-userLogoutBtn">Logout</button>
|
|
||||||
{{else}}
|
|
||||||
<button type="submit" class="btn btn-danger inline-block" data-toggle="modal" data-target="#login-form" onclick="return false">Login</button>
|
|
||||||
{{/if}}
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
<header class="sm-registry-info light-red-bg center hidden-sm hidden-lg hidden-md">
|
|
||||||
<code class="white no-bg">{{ baseUrl }}</code><br>
|
|
||||||
</header>
|
|
||||||
<header class="packages-header container">
|
|
||||||
{{#if tagline}}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-12">
|
|
||||||
{{{tagline}}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-5 hidden-xs hidden-sm">
|
|
||||||
<h2 class="title">Available Packages</h2>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-4 col-md-offset-3 col-sm-12">
|
|
||||||
<form id='search-form'>
|
|
||||||
<div class="input-group input-group-lg search-container">
|
|
||||||
<input type="text" class="form-control" placeholder="Search for packages">
|
|
||||||
<span class="input-group-btn">
|
|
||||||
<button class="btn btn-default search-icon js-search-btn"><i class="icon-search"></i></button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<section class="content container packages-container" id="all-packages">
|
|
||||||
{{#each packages}}
|
|
||||||
{{> entry}}
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
{{#unless packages.length}}
|
|
||||||
<div class='no-results'>
|
|
||||||
<big>No Packages</big><br>
|
|
||||||
Use <code>npm publish</code>
|
|
||||||
</div>
|
|
||||||
{{/unless}}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="content container pkg-search-container" id="search-results"></section>
|
|
||||||
|
|
||||||
<div class="modal fade" id="login-form" tabindex="-1" role="dialog" aria-labelledby="login-form-label" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-sm">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">×</span><span class="sr-only">Close</span></button>
|
|
||||||
<h5 class="modal-title" id="login-form-label">Welcome back</h5>
|
|
||||||
</div>
|
|
||||||
<form role="form" action="{{ baseUrl }}/-/login" method="post" id="login-form" autocomplete="off">
|
|
||||||
<div class="modal-body">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="user" class="sr-only">Email</label>
|
|
||||||
<input name="user" id="user" class="form-control" placeholder="Username" type="text">
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="pass" class="sr-only">Password</label>
|
|
||||||
<input name="pass" id="pass" class="form-control" placeholder="Password" type="password">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
|
||||||
<button type="submit" class="btn btn-primary">Log in</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form action="{{ baseUrl }}/-/logout" method="post" class="hide" id="userLogoutForm"></form>
|
|
||||||
|
|
||||||
<script src="{{ baseUrl }}/-/static/jquery.min.js"></script>
|
|
||||||
<script type='text/javascript' src='{{ baseUrl }}/-/static/main.js'></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
8
lib/web/ui/index.js
Normal file
8
lib/web/ui/index.js
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import React from 'react';
|
||||||
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
|
import App from './components/App.js';
|
||||||
|
import './style.css';
|
||||||
|
|
||||||
|
const state = window.__INITIAL_STATE;
|
||||||
|
ReactDOM.render(<App {...state} />, document.getElementById('root'));
|
278
lib/web/ui/js/bootstrap-modal.js
vendored
278
lib/web/ui/js/bootstrap-modal.js
vendored
|
@ -1,278 +0,0 @@
|
||||||
/* ========================================================================
|
|
||||||
* Bootstrap: modal.js v3.3.0
|
|
||||||
* http://getbootstrap.com/javascript/#modals
|
|
||||||
* ========================================================================
|
|
||||||
* Copyright 2011-2014 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
||||||
* ======================================================================== */
|
|
||||||
|
|
||||||
|
|
||||||
+function($) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
// MODAL CLASS DEFINITION
|
|
||||||
// ======================
|
|
||||||
|
|
||||||
let Modal = function(element, options) {
|
|
||||||
this.options = options;
|
|
||||||
this.$body = $(document.body);
|
|
||||||
this.$element = $(element);
|
|
||||||
this.$backdrop =
|
|
||||||
this.isShown = null;
|
|
||||||
this.scrollbarWidth = 0;
|
|
||||||
|
|
||||||
if (this.options.remote) {
|
|
||||||
this.$element
|
|
||||||
.find('.modal-content')
|
|
||||||
.load(this.options.remote, $.proxy(function() {
|
|
||||||
this.$element.trigger('loaded.bs.modal');
|
|
||||||
}, this));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.VERSION = '3.3.0';
|
|
||||||
|
|
||||||
Modal.TRANSITION_DURATION = 300;
|
|
||||||
Modal.BACKDROP_TRANSITION_DURATION = 150;
|
|
||||||
|
|
||||||
Modal.DEFAULTS = {
|
|
||||||
backdrop: true,
|
|
||||||
keyboard: true,
|
|
||||||
show: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.toggle = function(_relatedTarget) {
|
|
||||||
return this.isShown ? this.hide() : this.show(_relatedTarget);
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.show = function(_relatedTarget) {
|
|
||||||
let that = this;
|
|
||||||
let e = $.Event('show.bs.modal', {relatedTarget: _relatedTarget});
|
|
||||||
|
|
||||||
this.$element.trigger(e);
|
|
||||||
|
|
||||||
if (this.isShown || e.isDefaultPrevented()) return;
|
|
||||||
|
|
||||||
this.isShown = true;
|
|
||||||
|
|
||||||
this.checkScrollbar();
|
|
||||||
this.$body.addClass('modal-open');
|
|
||||||
|
|
||||||
this.setScrollbar();
|
|
||||||
this.escape();
|
|
||||||
|
|
||||||
this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this));
|
|
||||||
|
|
||||||
this.backdrop(function() {
|
|
||||||
let transition = $.support.transition && that.$element.hasClass('fade');
|
|
||||||
|
|
||||||
if (!that.$element.parent().length) {
|
|
||||||
that.$element.appendTo(that.$body); // don't move modals dom position
|
|
||||||
}
|
|
||||||
|
|
||||||
that.$element
|
|
||||||
.show()
|
|
||||||
.scrollTop(0);
|
|
||||||
|
|
||||||
if (transition) {
|
|
||||||
that.$element[0].offsetWidth; // force reflow
|
|
||||||
}
|
|
||||||
|
|
||||||
that.$element
|
|
||||||
.addClass('in')
|
|
||||||
.attr('aria-hidden', false);
|
|
||||||
|
|
||||||
that.enforceFocus();
|
|
||||||
|
|
||||||
let e = $.Event('shown.bs.modal', {relatedTarget: _relatedTarget});
|
|
||||||
|
|
||||||
transition ?
|
|
||||||
that.$element.find('.modal-dialog') // wait for modal to slide in
|
|
||||||
.one('bsTransitionEnd', function() {
|
|
||||||
that.$element.trigger('focus').trigger(e);
|
|
||||||
})
|
|
||||||
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
|
||||||
that.$element.trigger('focus').trigger(e);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.hide = function(e) {
|
|
||||||
if (e) e.preventDefault();
|
|
||||||
|
|
||||||
e = $.Event('hide.bs.modal');
|
|
||||||
|
|
||||||
this.$element.trigger(e);
|
|
||||||
|
|
||||||
if (!this.isShown || e.isDefaultPrevented()) return;
|
|
||||||
|
|
||||||
this.isShown = false;
|
|
||||||
|
|
||||||
this.escape();
|
|
||||||
|
|
||||||
$(document).off('focusin.bs.modal');
|
|
||||||
|
|
||||||
this.$element
|
|
||||||
.removeClass('in')
|
|
||||||
.attr('aria-hidden', true)
|
|
||||||
.off('click.dismiss.bs.modal');
|
|
||||||
|
|
||||||
$.support.transition && this.$element.hasClass('fade') ?
|
|
||||||
this.$element
|
|
||||||
.one('bsTransitionEnd', $.proxy(this.hideModal, this))
|
|
||||||
.emulateTransitionEnd(Modal.TRANSITION_DURATION) :
|
|
||||||
this.hideModal();
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.enforceFocus = function() {
|
|
||||||
$(document)
|
|
||||||
.off('focusin.bs.modal') // guard against infinite focus loop
|
|
||||||
.on('focusin.bs.modal', $.proxy(function(e) {
|
|
||||||
if (this.$element[0] !== e.target && !this.$element.has(e.target).length) {
|
|
||||||
this.$element.trigger('focus');
|
|
||||||
}
|
|
||||||
}, this));
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.escape = function() {
|
|
||||||
if (this.isShown && this.options.keyboard) {
|
|
||||||
this.$element.on('keydown.dismiss.bs.modal', $.proxy(function(e) {
|
|
||||||
e.which == 27 && this.hide();
|
|
||||||
}, this));
|
|
||||||
} else if (!this.isShown) {
|
|
||||||
this.$element.off('keydown.dismiss.bs.modal');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.hideModal = function() {
|
|
||||||
let that = this;
|
|
||||||
this.$element.hide();
|
|
||||||
this.backdrop(function() {
|
|
||||||
that.$body.removeClass('modal-open');
|
|
||||||
that.resetScrollbar();
|
|
||||||
that.$element.trigger('hidden.bs.modal');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.removeBackdrop = function() {
|
|
||||||
this.$backdrop && this.$backdrop.remove();
|
|
||||||
this.$backdrop = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.backdrop = function(callback) {
|
|
||||||
let that = this;
|
|
||||||
let animate = this.$element.hasClass('fade') ? 'fade' : '';
|
|
||||||
|
|
||||||
if (this.isShown && this.options.backdrop) {
|
|
||||||
let doAnimate = $.support.transition && animate;
|
|
||||||
|
|
||||||
this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
|
|
||||||
.prependTo(this.$element)
|
|
||||||
.on('click.dismiss.bs.modal', $.proxy(function(e) {
|
|
||||||
if (e.target !== e.currentTarget) return;
|
|
||||||
this.options.backdrop == 'static'
|
|
||||||
? this.$element[0].focus.call(this.$element[0])
|
|
||||||
: this.hide.call(this);
|
|
||||||
}, this));
|
|
||||||
|
|
||||||
if (doAnimate) this.$backdrop[0].offsetWidth; // force reflow
|
|
||||||
|
|
||||||
this.$backdrop.addClass('in');
|
|
||||||
|
|
||||||
if (!callback) return;
|
|
||||||
|
|
||||||
doAnimate ?
|
|
||||||
this.$backdrop
|
|
||||||
.one('bsTransitionEnd', callback)
|
|
||||||
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
|
||||||
callback();
|
|
||||||
} else if (!this.isShown && this.$backdrop) {
|
|
||||||
this.$backdrop.removeClass('in');
|
|
||||||
|
|
||||||
let callbackRemove = function() {
|
|
||||||
that.removeBackdrop();
|
|
||||||
callback && callback();
|
|
||||||
};
|
|
||||||
$.support.transition && this.$element.hasClass('fade') ?
|
|
||||||
this.$backdrop
|
|
||||||
.one('bsTransitionEnd', callbackRemove)
|
|
||||||
.emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) :
|
|
||||||
callbackRemove();
|
|
||||||
} else if (callback) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.checkScrollbar = function() {
|
|
||||||
this.scrollbarWidth = this.measureScrollbar();
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.setScrollbar = function() {
|
|
||||||
let bodyPad = parseInt((this.$body.css('padding-right') || 0), 10);
|
|
||||||
if (this.scrollbarWidth) this.$body.css('padding-right', bodyPad + this.scrollbarWidth);
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.resetScrollbar = function() {
|
|
||||||
this.$body.css('padding-right', '');
|
|
||||||
};
|
|
||||||
|
|
||||||
Modal.prototype.measureScrollbar = function() { // thx walsh
|
|
||||||
if (document.body.clientWidth >= window.innerWidth) return 0;
|
|
||||||
let scrollDiv = document.createElement('div');
|
|
||||||
scrollDiv.className = 'modal-scrollbar-measure';
|
|
||||||
this.$body.append(scrollDiv);
|
|
||||||
let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth;
|
|
||||||
this.$body[0].removeChild(scrollDiv);
|
|
||||||
return scrollbarWidth;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODAL PLUGIN DEFINITION
|
|
||||||
// =======================
|
|
||||||
|
|
||||||
function Plugin(option, _relatedTarget) {
|
|
||||||
return this.each(function() {
|
|
||||||
let $this = $(this);
|
|
||||||
let data = $this.data('bs.modal');
|
|
||||||
let options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option);
|
|
||||||
|
|
||||||
if (!data) $this.data('bs.modal', (data = new Modal(this, options)));
|
|
||||||
if (typeof option == 'string') data[option](_relatedTarget);
|
|
||||||
else if (options.show) data.show(_relatedTarget);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let old = $.fn.modal;
|
|
||||||
|
|
||||||
$.fn.modal = Plugin;
|
|
||||||
$.fn.modal.Constructor = Modal;
|
|
||||||
|
|
||||||
|
|
||||||
// MODAL NO CONFLICT
|
|
||||||
// =================
|
|
||||||
|
|
||||||
$.fn.modal.noConflict = function() {
|
|
||||||
$.fn.modal = old;
|
|
||||||
return this;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// MODAL DATA-API
|
|
||||||
// ==============
|
|
||||||
|
|
||||||
$(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function(e) {
|
|
||||||
let $this = $(this);
|
|
||||||
let href = $this.attr('href');
|
|
||||||
let $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))); // strip for ie7
|
|
||||||
let option = $target.data('bs.modal') ? 'toggle' : $.extend({remote: !/#/.test(href) && href}, $target.data(), $this.data());
|
|
||||||
|
|
||||||
if ($this.is('a')) e.preventDefault();
|
|
||||||
|
|
||||||
$target.one('show.bs.modal', function(showEvent) {
|
|
||||||
if (showEvent.isDefaultPrevented()) return; // only register focus restorer if modal will actually get shown
|
|
||||||
$target.one('hidden.bs.modal', function() {
|
|
||||||
$this.is(':visible') && $this.trigger('focus');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Plugin.call($target, option, this);
|
|
||||||
});
|
|
||||||
}(jQuery);
|
|
|
@ -1,72 +0,0 @@
|
||||||
let $ = require('unopinionate').selector;
|
|
||||||
let onClick = require('onclick');
|
|
||||||
let transitionComplete = require('transition-complete');
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
onClick('.entry .name', function() {
|
|
||||||
let $this = $(this);
|
|
||||||
let $entry = $this.closest('.entry');
|
|
||||||
|
|
||||||
if ($entry.hasClass('open')) {
|
|
||||||
// Close entry
|
|
||||||
$entry
|
|
||||||
.height($entry.outerHeight())
|
|
||||||
.removeClass('open');
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
transitionComplete(function() {
|
|
||||||
$entry.find('.readme').remove();
|
|
||||||
$entry.css('height', 'auto');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Open entry
|
|
||||||
$('.entry.open').each(function() {
|
|
||||||
// Close open entries
|
|
||||||
let $entry = $(this);
|
|
||||||
$entry
|
|
||||||
.height($entry.outerHeight())
|
|
||||||
.removeClass('open');
|
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
$entry.css('height', $entry.attr('data-height') + 'px');
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
transitionComplete(function() {
|
|
||||||
$entry.find('.readme').remove();
|
|
||||||
$entry.css('height', 'auto');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add the open class
|
|
||||||
$entry.addClass('open');
|
|
||||||
|
|
||||||
// Explicitly set heights for transitions
|
|
||||||
let height = $entry.outerHeight();
|
|
||||||
$entry
|
|
||||||
.attr('data-height', height)
|
|
||||||
.css('height', height);
|
|
||||||
|
|
||||||
// Get the data
|
|
||||||
$.ajax({
|
|
||||||
url: '-/readme/'
|
|
||||||
+ encodeURIComponent($entry.attr('data-name')) + '/'
|
|
||||||
+ encodeURIComponent($entry.attr('data-version')),
|
|
||||||
dataType: 'text',
|
|
||||||
success: function(html) {
|
|
||||||
let $readme = $('<div class=\'readme\'>')
|
|
||||||
.html(html)
|
|
||||||
.appendTo($entry);
|
|
||||||
|
|
||||||
$entry.height(height + $readme.outerHeight());
|
|
||||||
|
|
||||||
transitionComplete(function() {
|
|
||||||
$entry.css('height', 'auto');
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,13 +0,0 @@
|
||||||
// twitter bootstrap stuff;
|
|
||||||
// not in static 'cause I want it to be bundled with the rest of javascripts
|
|
||||||
require('./bootstrap-modal');
|
|
||||||
|
|
||||||
// our own files
|
|
||||||
require('./search');
|
|
||||||
require('./entry');
|
|
||||||
|
|
||||||
let $ = require('unopinionate').selector;
|
|
||||||
$(document).on('click', '.js-userLogoutBtn', function() {
|
|
||||||
$('#userLogoutForm').submit();
|
|
||||||
return false;
|
|
||||||
});
|
|
|
@ -1,77 +0,0 @@
|
||||||
let $ = require('unopinionate').selector;
|
|
||||||
let template = require('../entry.hbs');
|
|
||||||
|
|
||||||
$(function() {
|
|
||||||
;(function(window, document) {
|
|
||||||
var $form = $('#search-form')
|
|
||||||
var $input = $form.find('input')
|
|
||||||
var $searchResults = $('#search-results')
|
|
||||||
var $pkgListing = $('#all-packages')
|
|
||||||
var $searchBtn = $('.js-search-btn')
|
|
||||||
var request
|
|
||||||
var lastQuery = ''
|
|
||||||
|
|
||||||
var toggle = function(validQuery) {
|
|
||||||
$searchResults.toggleClass('show', validQuery)
|
|
||||||
$pkgListing.toggleClass('hide', validQuery)
|
|
||||||
|
|
||||||
$searchBtn.find('i').toggleClass('icon-cancel', validQuery)
|
|
||||||
$searchBtn.find('i').toggleClass('icon-search', !validQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
$form.bind('submit keyup', function(e) {
|
|
||||||
var query, isValidQuery
|
|
||||||
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
query = $input.val()
|
|
||||||
isValidQuery = (query !== '')
|
|
||||||
|
|
||||||
toggle(isValidQuery)
|
|
||||||
|
|
||||||
if (!isValidQuery) {
|
|
||||||
if (request && typeof request.abort === 'function') {
|
|
||||||
request.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
$searchResults.html('')
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (request && typeof request.abort === 'function') {
|
|
||||||
request.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (query !== lastQuery) {
|
|
||||||
lastQuery = query
|
|
||||||
$searchResults.html(
|
|
||||||
'<img class=\'search-ajax\' src=\'-/static/ajax.gif\' alt=\'Spinner\'/>');
|
|
||||||
}
|
|
||||||
|
|
||||||
request = $.getJSON('-/search/' + query, function( results ) {
|
|
||||||
if (results.length > 0) {
|
|
||||||
let html = '';
|
|
||||||
|
|
||||||
$.each(results, function(i, entry) {
|
|
||||||
html += template(entry);
|
|
||||||
});
|
|
||||||
|
|
||||||
$searchResults.html(html);
|
|
||||||
} else {
|
|
||||||
$searchResults.html(
|
|
||||||
'<div class=\'no-results\'><big>No Results</big></div>');
|
|
||||||
}
|
|
||||||
}).fail(function () {
|
|
||||||
$searchResults.html(
|
|
||||||
"<div class='no-results'><big>No Results</big></div>")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
$(document).on('click', '.icon-cancel', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$input.val('');
|
|
||||||
$form.keyup();
|
|
||||||
});
|
|
||||||
})(window, window.document);
|
|
||||||
});
|
|
0
lib/web/ui/style.css
Normal file
0
lib/web/ui/style.css
Normal file
101
package.json
101
package.json
|
@ -23,64 +23,123 @@
|
||||||
"bunyan": "^1.8.0",
|
"bunyan": "^1.8.0",
|
||||||
"chalk": "^1.1.3",
|
"chalk": "^1.1.3",
|
||||||
"commander": "^2.9.0",
|
"commander": "^2.9.0",
|
||||||
"compression": "^1.6.1",
|
"compression": "1.6.2",
|
||||||
"cookies": "^0.6.1",
|
"cookies": "^0.6.1",
|
||||||
"express": "^4.13.4",
|
"cors": "2.8.3",
|
||||||
|
"express": "4.15.3",
|
||||||
|
"global": "^4.3.2",
|
||||||
"handlebars": "^4.0.5",
|
"handlebars": "^4.0.5",
|
||||||
"highlight.js": "^9.3.0",
|
"highlight.js": "^9.3.0",
|
||||||
"http-errors": "^1.4.0",
|
"http-errors": "^1.4.0",
|
||||||
"jju": "^1.3.0",
|
"jju": "^1.3.0",
|
||||||
|
"js-string-escape": "1.0.1",
|
||||||
"js-yaml": "^3.6.0",
|
"js-yaml": "^3.6.0",
|
||||||
"lockfile": "^1.0.1",
|
"lockfile": "^1.0.1",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.4",
|
||||||
"lunr": "^0.7.0",
|
"lunr": "^0.7.0",
|
||||||
|
"marked": "0.3.6",
|
||||||
"minimatch": "^3.0.2",
|
"minimatch": "^3.0.2",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
"pkginfo": "^0.4.0",
|
"pkginfo": "^0.4.0",
|
||||||
"render-readme": "^1.3.1",
|
"prop-types": "^15.5.10",
|
||||||
"request": "^2.72.0",
|
"request": "^2.72.0",
|
||||||
"semver": "^5.1.0",
|
"semver": "^5.1.0",
|
||||||
"unix-crypt-td-js": "^1.0.0"
|
"unix-crypt-td-js": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify": "^13.0.0",
|
"babel-cli": "6.22.2",
|
||||||
"browserify-handlebars": "^1.0.0",
|
"babel-core": "6.22.1",
|
||||||
"codecov": "^2.2.0",
|
"babel-eslint": "7.2.3",
|
||||||
"eslint": "^3.19.0",
|
"babel-loader": "6.2.4",
|
||||||
"eslint-config-google": "^0.7.1",
|
"babel-plugin-dynamic-import-node": "1.0.2",
|
||||||
"grunt": "^1.0.1",
|
"babel-plugin-dynamic-import-webpack": "1.0.1",
|
||||||
"grunt-browserify": "^5.0.0",
|
"babel-plugin-syntax-dynamic-import": "6.18.0",
|
||||||
"grunt-cli": "^1.2.0",
|
"babel-plugin-transform-class-properties": "6.24.1",
|
||||||
"grunt-contrib-less": "^1.3.0",
|
"babel-plugin-transform-decorators-legacy": "1.3.4",
|
||||||
"grunt-contrib-watch": "^1.0.0",
|
"babel-plugin-transform-es2015-modules-commonjs": "6.24.1",
|
||||||
"mocha": "^3.2.0",
|
"babel-polyfill": "6.23.0",
|
||||||
"mocha-lcov-reporter": "^1.3.0",
|
"babel-preset-env": "1.5.1",
|
||||||
"nyc": "^10.1.2",
|
"babel-preset-es2015": "6.22.0",
|
||||||
"onclick": "^0.1.0",
|
"babel-preset-es2016": "6.22.0",
|
||||||
"rimraf": "^2.5.2",
|
"babel-preset-es2017": "6.22.0",
|
||||||
"transition-complete": "^0.0.2",
|
"babel-preset-react": "6.24.1",
|
||||||
"unopinionate": "^0.0.4"
|
"babel-register": "6.24.1",
|
||||||
|
"codacy-coverage": "2.0.2",
|
||||||
|
"codecov": "2.2.0",
|
||||||
|
"coveralls": "2.13.0",
|
||||||
|
"css-loader": "0.23.1",
|
||||||
|
"enzyme": "2.8.2",
|
||||||
|
"eslint": "3.19.0",
|
||||||
|
"eslint-config-google": "0.7.1",
|
||||||
|
"eslint-config-prettier": "2.1.1",
|
||||||
|
"eslint-loader": "1.7.1",
|
||||||
|
"eslint-plugin-flow": "2.29.1",
|
||||||
|
"eslint-plugin-flowtype": "2.33.0",
|
||||||
|
"eslint-plugin-import": "2.3.0",
|
||||||
|
"eslint-plugin-jsx-a11y": "5.0.3",
|
||||||
|
"eslint-plugin-prettier": "2.1.1",
|
||||||
|
"eslint-plugin-react": "7.0.1",
|
||||||
|
"extract-text-webpack-plugin": "2.1.2",
|
||||||
|
"file-loader": "0.10.1",
|
||||||
|
"flow-bin": "0.47.0",
|
||||||
|
"in-publish": "2.0.0",
|
||||||
|
"jest": "20.0.4",
|
||||||
|
"jest-serializer-enzyme": "1.0.0",
|
||||||
|
"material-ui": "0.17.1",
|
||||||
|
"mocha": "3.2.0",
|
||||||
|
"mocha-lcov-reporter": "1.3.0",
|
||||||
|
"normalize.css": "5.0.0",
|
||||||
|
"nyc": "10.1.2",
|
||||||
|
"onclick": "0.1.0",
|
||||||
|
"prettier": "1.3.1",
|
||||||
|
"react": "15.6.0",
|
||||||
|
"react-dom": "15.6.0",
|
||||||
|
"react-hot-loader": "3.0.0-beta.6",
|
||||||
|
"react-router": "3.0.2",
|
||||||
|
"react-tap-event-plugin": "2.0.1",
|
||||||
|
"react-test-renderer": "15.5.4",
|
||||||
|
"rimraf": "2.5.2",
|
||||||
|
"sinon": "^2.3.4",
|
||||||
|
"style-loader": "0.13.1",
|
||||||
|
"styled-components": "1.4.6",
|
||||||
|
"styled-theme": "0.3.0",
|
||||||
|
"styled-tools": "0.1.2",
|
||||||
|
"superagent": "2.0.0",
|
||||||
|
"transition-complete": "0.0.2",
|
||||||
|
"unopinionate": "0.0.4",
|
||||||
|
"url-loader": "0.5.8",
|
||||||
|
"webpack": "2.6.1"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"private",
|
"private",
|
||||||
"package",
|
"package",
|
||||||
"repository",
|
"repository",
|
||||||
"registry",
|
"registry",
|
||||||
|
"enterprise",
|
||||||
"modules",
|
"modules",
|
||||||
"proxy",
|
"proxy",
|
||||||
"server"
|
"server"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "npm run lint && mocha ./test/functional ./test/unit",
|
"test": "npm run lint && mocha ./test/functional --reporter=spec --full-trace",
|
||||||
"test:coverage": "nyc mocha -R spec ./test/functional ./test/unit",
|
"test:coverage": "nyc mocha -R spec ./test/functional ./test/unit",
|
||||||
|
"test:ui": "NODE_ENV=test jest",
|
||||||
|
"test:ui:update": "NODE_ENV=test jest -u",
|
||||||
"coverage:html": "nyc report --reporter=html",
|
"coverage:html": "nyc report --reporter=html",
|
||||||
"coverage:codecov": "nyc report --reporter=lcov | codecov",
|
"coverage:codecov": "nyc report --reporter=lcov | codecov",
|
||||||
"test-travis": "npm run lint && npm run test:coverage",
|
"test-travis": "npm run lint && npm run test:coverage",
|
||||||
"test-only": "mocha ./test/functional ./test/unit",
|
"test-only": "mocha ./test/functional ./test/unit",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"build-docker": "docker build -t verdaccio .",
|
"build-docker": "docker build -t verdaccio .",
|
||||||
|
"build:webpack": "webpack --config tools/webpack.config.js",
|
||||||
|
"prepublish": "in-publish && thing-I-dont-want-on-dev-install || not-in-publish",
|
||||||
"build-docker:rpi": "docker build -f Dockerfile.rpi -t verdaccio:rpi ."
|
"build-docker:rpi": "docker build -f Dockerfile.rpi -t verdaccio:rpi ."
|
||||||
},
|
},
|
||||||
|
"jest": {
|
||||||
|
"snapshotSerializers": [
|
||||||
|
"jest-serializer-enzyme"
|
||||||
|
]
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.6.1",
|
"node": ">=4.6.1",
|
||||||
"npm": ">=2.15.9"
|
"npm": ">=2.15.9"
|
||||||
|
|
15
tools/.eslintrc.yml
Normal file
15
tools/.eslintrc.yml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# vim: syntax=yaml
|
||||||
|
|
||||||
|
|
||||||
|
## rules for react components
|
||||||
|
|
||||||
|
env:
|
||||||
|
node: true
|
||||||
|
browser: true
|
||||||
|
|
||||||
|
rules:
|
||||||
|
# jsdoc is mandatory
|
||||||
|
require-jsdoc: 0
|
||||||
|
comma-dangle: 0
|
||||||
|
|
||||||
|
|
56
tools/webpack.config.js
Normal file
56
tools/webpack.config.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
const path = require('path');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './lib/web/ui/index.js',
|
||||||
|
devtool: 'source-map',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, '../lib/web/static'),
|
||||||
|
filename: 'bundle.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
enforce: 'pre',
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
loader: 'eslint-loader',
|
||||||
|
options: {
|
||||||
|
failOnError: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(js)$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: ['babel-loader']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(jpe?g|png|gif|svg)$/i,
|
||||||
|
use: 'file-loader?name=/[name].[ext]'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(ttf|eot|woff|woff2|svg)$/,
|
||||||
|
loader: 'url-loader?limit=50000&name=fonts/[hash].[ext]'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: ExtractTextPlugin.extract({
|
||||||
|
fallback: 'style-loader',
|
||||||
|
use: 'css-loader'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ExtractTextPlugin('styles.css'),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
BROWSER: JSON.stringify(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.css']
|
||||||
|
}
|
||||||
|
};
|
BIN
yarn.lock
BIN
yarn.lock
Binary file not shown.
Loading…
Reference in a new issue